1<?php 2/** 3 * DokuWiki Plugin publish (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Jarrod Lowe <dokuwiki@rrod.net> 7 * @author Andreas Gohr <gohr@cosmocode.de> 8 */ 9 10 11// must be run within DokuWiki 12if(!defined('DOKU_INC')) die(); 13 14 15class syntax_plugin_publish extends DokuWiki_Syntax_Plugin { 16 private $hlp; 17 18 function syntax_plugin_publish(){ 19 $this->hlp = plugin_load('helper','publish'); 20 } 21 22 function pattern() { 23 return '\[APPROVALS.*?\]'; 24 } 25 26 function getType() { 27 return 'substition'; 28 } 29 30 function getSort() { 31 return 20; 32 } 33 34 function PType() { 35 return 'block'; 36 } 37 38 function connectTo($mode) { 39 $this->Lexer->addSpecialPattern($this->pattern(),$mode,'plugin_publish'); 40 } 41 42 function handle($match, $state, $pos, &$handler){ 43 $namespace = substr($match, 11, -1); 44 return array($match, $state, $pos, $namespace); 45 } 46 47 function render($mode, &$renderer, $data) { 48 global $conf; 49 50 if($mode != 'xhtml') { 51 return false; 52 } 53 54 list($match, $state, $pos, $namespace) = $data; 55 56 $namespace = cleanID(getNS($namespace . ":*")); 57 58 $pages = $this->getPagesFromNamespace($namespace); 59 60 if(count($pages) == 0) { 61 $renderer->doc .= '<p class="apr_none">' . $this->getLang('apr_p_none') . '</p>'; 62 return true; 63 } 64 65 usort($pages, array($this,'_pagesorter')); 66 67 // Output Table 68 $renderer->doc .= '<table class="apr_table"><tr class="apr_head">'; 69 $renderer->doc .= '<th class="apr_page">' . $this->getLang('apr_p_hdr_page') . '</th>'; 70 $renderer->doc .= '<th class="apr_prev">' . $this->getLang('apr_p_hdr_previous') . '</th>'; 71 $renderer->doc .= '<th class="apr_upd">' . $this->getLang('apr_p_hdr_updated') . '</th>'; 72 $renderer->doc .= '</tr>'; 73 74 75 $working_ns = null; 76 foreach($pages as $page) { 77 // $page: 0 -> pagename, 1 -> approval metadata, 2 -> last changed date 78 $this_ns = getNS($page[0]); 79 80 if($this_ns != $working_ns) { 81 $name_ns = $this_ns; 82 if($this_ns == '') { $name_ns = 'root'; } 83 $renderer->doc .= '<tr class="apr_ns"><td colspan="3"><a href="'; 84 $renderer->doc .= wl($this_ns . ':' . $this->getConf('start')); 85 $renderer->doc .= '">'; 86 $renderer->doc .= $name_ns; 87 $renderer->doc .= '</a></td></tr>'; 88 $working_ns = $this_ns; 89 } 90 91 $updated = '<a href="' . wl($page[0]) . '">' . dformat($page[2]) . '</a>'; 92 if($page[1] == null || count($page[1]) == 0) { 93 // Has never been approved 94 $approved = ''; 95 }else{ 96 $keys = array_keys($page[1]); 97 sort($keys); 98 $last = $keys[count($keys)-1]; 99 $approved = sprintf($this->getLang('apr_p_approved'), 100 $page[1][$last][1], 101 wl($page[0], 'rev=' . $last), 102 dformat($last)); 103 if($last == $page[2]) { $updated = 'Unchanged'; } //shouldn't be possible: 104 //the search_helper should have 105 //excluded this 106 } 107 108 $renderer->doc .= '<tr class="apr_table'; 109 if($approved == '') { $renderer->doc .= ' apr_never'; } 110 $renderer->doc .= '"><td class="apr_page"><a href="'; 111 $renderer->doc .= wl($page[0]); 112 $renderer->doc .= '">'; 113 $renderer->doc .= $page[0]; 114 $renderer->doc .= '</a></td><td class="apr_prev">'; 115 $renderer->doc .= $approved; 116 $renderer->doc .= '</td><td class="apr_upd">'; 117 $renderer->doc .= $updated; 118 $renderer->doc .= '</td></tr>'; 119 120 //$renderer->doc .= '<tr><td colspan="3">' . print_r($page, true) . '</td></tr>'; 121 } 122 $renderer->doc .= '</table>'; 123 return true; 124 } 125 126 function getPagesFromNamespace($namespace) { 127 global $conf; 128 $dir = $conf['datadir'] . '/' . str_replace(':', '/', $namespace); 129 $pages = array(); 130 search($pages, $dir, array($this,'_search_helper'), array($namespace, $this->getConf('apr_namespaces'))); 131 return $pages; 132 } 133 134 /** 135 * search callback function 136 * 137 * filter out pages which can't be approved by the current user 138 * then check if they need approving 139 */ 140 function _search_helper(&$data, $base, $file, $type, $lvl, $opts) { 141 $ns = $opts[0]; 142 $valid_ns = $opts[1]; 143 144 if ($type == 'd') { 145 return $this->hlp->in_sub_namespace($valid_ns, $ns . ':' . str_replace('/', ':', $file)); 146 } 147 148 if (!preg_match('#\.txt$#', $file)) { 149 return false; 150 } 151 152 $id = pathID($ns . $file); 153 if (!$this->hlp->in_namespace($valid_ns, $id)) { 154 return false; 155 } 156 157 if (auth_quickaclcheck($id) < AUTH_DELETE) { 158 return false; 159 } 160 161 $meta = $this->hlp->getMeta($id); 162 if ($this->hlp->isRevisionApproved($meta['meta']['last_change']['date'], $id)) { 163 164 // Already approved 165 return false; 166 } 167 168 $data[] = array($id, $meta['approval'], $meta['last_change']['date']); 169 return false; 170 } 171 172 /** 173 * Custom sort callback 174 */ 175 function _pagesorter($a, $b){ 176 $ac = explode(':',$a[0]); 177 $bc = explode(':',$b[0]); 178 $an = count($ac); 179 $bn = count($bc); 180 181 // Same number of elements, can just string sort 182 if($an == $bn) { return strcmp($a[0], $b[0]); } 183 184 // For each level: 185 // If this is not the last element in either list: 186 // same -> continue 187 // otherwise strcmp 188 // If this is the last element in either list, it wins 189 $n = 0; 190 while(true) { 191 if($n + 1 == $an) { return -1; } 192 if($n + 1 == $bn) { return 1; } 193 $s = strcmp($ac[$n], $bc[$n]); 194 if($s != 0) { return $s; } 195 $n += 1; 196 } 197 } 198 199} 200 201