1*b81b1128SSzymon Olewniczak<?php 2*b81b1128SSzymon Olewniczak 3*b81b1128SSzymon Olewniczakuse dokuwiki\plugin\approve\meta\ApproveConst; 4*b81b1128SSzymon Olewniczak 5*b81b1128SSzymon Olewniczak// must be run within DokuWiki 6*b81b1128SSzymon Olewniczakif(!defined('DOKU_INC')) die(); 7*b81b1128SSzymon Olewniczak 8*b81b1128SSzymon Olewniczak 9*b81b1128SSzymon Olewniczakclass syntax_plugin_approve_table extends DokuWiki_Syntax_Plugin { 10*b81b1128SSzymon Olewniczak 11*b81b1128SSzymon Olewniczak const STATES = [ApproveConst::APPROVED, ApproveConst::READY_FOR_APPROVAL, ApproveConst::DRAFT]; 12*b81b1128SSzymon Olewniczak 13*b81b1128SSzymon Olewniczak function getType() { 14*b81b1128SSzymon Olewniczak return 'substition'; 15*b81b1128SSzymon Olewniczak } 16*b81b1128SSzymon Olewniczak 17*b81b1128SSzymon Olewniczak function getSort() { 18*b81b1128SSzymon Olewniczak return 20; 19*b81b1128SSzymon Olewniczak } 20*b81b1128SSzymon Olewniczak 21*b81b1128SSzymon Olewniczak function PType() { 22*b81b1128SSzymon Olewniczak return 'block'; 23*b81b1128SSzymon Olewniczak } 24*b81b1128SSzymon Olewniczak 25*b81b1128SSzymon Olewniczak function connectTo($mode) { 26*b81b1128SSzymon Olewniczak $this->Lexer->addSpecialPattern('----+ *approve table *-+\n.*?----+', $mode,'plugin_approve_table'); 27*b81b1128SSzymon Olewniczak } 28*b81b1128SSzymon Olewniczak 29*b81b1128SSzymon Olewniczak function handle($match, $state, $pos, Doku_Handler $handler){ 30*b81b1128SSzymon Olewniczak $lines = explode("\n", $match); 31*b81b1128SSzymon Olewniczak array_shift($lines); 32*b81b1128SSzymon Olewniczak array_pop($lines); 33*b81b1128SSzymon Olewniczak 34*b81b1128SSzymon Olewniczak $params = []; 35*b81b1128SSzymon Olewniczak foreach ($lines as $line) { 36*b81b1128SSzymon Olewniczak $pair = explode(':', $line, 2); 37*b81b1128SSzymon Olewniczak if (count($pair) < 2) { 38*b81b1128SSzymon Olewniczak continue; 39*b81b1128SSzymon Olewniczak } 40*b81b1128SSzymon Olewniczak $key = trim($pair[0]); 41*b81b1128SSzymon Olewniczak $value = trim($pair[1]); 42*b81b1128SSzymon Olewniczak if ($key == 'states') { 43*b81b1128SSzymon Olewniczak $value = array_map('trim', explode(',', $value)); 44*b81b1128SSzymon Olewniczak //normalize 45*b81b1128SSzymon Olewniczak $value = array_map('strtolower', $value); 46*b81b1128SSzymon Olewniczak $value = array_map('ucfirst', $value); 47*b81b1128SSzymon Olewniczak foreach ($value as $state) { 48*b81b1128SSzymon Olewniczak if (!in_array($state, self::STATES)) { 49*b81b1128SSzymon Olewniczak msg('approve plugin: unknown state "'.$state.'" should be: ' . 50*b81b1128SSzymon Olewniczak implode(', ', self::STATES), -1); 51*b81b1128SSzymon Olewniczak return false; 52*b81b1128SSzymon Olewniczak } 53*b81b1128SSzymon Olewniczak } 54*b81b1128SSzymon Olewniczak } elseif($key == 'filter' && preg_match($value, null) === false) { 55*b81b1128SSzymon Olewniczak msg('approve plugin: invalid filter regex', -1); 56*b81b1128SSzymon Olewniczak return false; 57*b81b1128SSzymon Olewniczak } 58*b81b1128SSzymon Olewniczak $params[$key] = $value; 59*b81b1128SSzymon Olewniczak } 60*b81b1128SSzymon Olewniczak return $params; 61*b81b1128SSzymon Olewniczak } 62*b81b1128SSzymon Olewniczak 63*b81b1128SSzymon Olewniczak function render($mode, Doku_Renderer $renderer, $params) { 64*b81b1128SSzymon Olewniczak global $conf; 65*b81b1128SSzymon Olewniczak 66*b81b1128SSzymon Olewniczak if ($mode != 'xhtml') return false; 67*b81b1128SSzymon Olewniczak if ($params === false) return false; 68*b81b1128SSzymon Olewniczak 69*b81b1128SSzymon Olewniczak $defaults = [ 70*b81b1128SSzymon Olewniczak 'namespace' => '', 71*b81b1128SSzymon Olewniczak 'filter' => false, 72*b81b1128SSzymon Olewniczak 'states' => self::STATES 73*b81b1128SSzymon Olewniczak ]; 74*b81b1128SSzymon Olewniczak 75*b81b1128SSzymon Olewniczak $params = array_replace($defaults, $params); 76*b81b1128SSzymon Olewniczak 77*b81b1128SSzymon Olewniczak $namespace = cleanID(getNS($params['namespace'] . ":*")); 78*b81b1128SSzymon Olewniczak 79*b81b1128SSzymon Olewniczak $pages = $this->_getPagesFromNamespace($namespace, $params['filter'], $params['states']); 80*b81b1128SSzymon Olewniczak 81*b81b1128SSzymon Olewniczak usort($pages, array($this,'_pagesorter')); 82*b81b1128SSzymon Olewniczak 83*b81b1128SSzymon Olewniczak // Output Table 84*b81b1128SSzymon Olewniczak $renderer->doc .= '<table><tr>'; 85*b81b1128SSzymon Olewniczak $renderer->doc .= '<th>' . $this->getLang('hdr_page') . '</th>'; 86*b81b1128SSzymon Olewniczak $renderer->doc .= '<th>' . $this->getLang('hdr_state') . '</th>'; 87*b81b1128SSzymon Olewniczak $renderer->doc .= '<th>' . $this->getLang('hdr_updated') . '</th>'; 88*b81b1128SSzymon Olewniczak $renderer->doc .= '</tr>'; 89*b81b1128SSzymon Olewniczak 90*b81b1128SSzymon Olewniczak 91*b81b1128SSzymon Olewniczak $all_approved = 0; 92*b81b1128SSzymon Olewniczak $all_approved_ready = 0; 93*b81b1128SSzymon Olewniczak $all = 0; 94*b81b1128SSzymon Olewniczak 95*b81b1128SSzymon Olewniczak $working_ns = null; 96*b81b1128SSzymon Olewniczak foreach($pages as $page) { 97*b81b1128SSzymon Olewniczak // $page: 0 -> pagename, 1 -> true -> approved else false, 2 -> last changed date 98*b81b1128SSzymon Olewniczak $this_ns = getNS($page[0]); 99*b81b1128SSzymon Olewniczak 100*b81b1128SSzymon Olewniczak if($this_ns != $working_ns) { 101*b81b1128SSzymon Olewniczak $name_ns = $this_ns; 102*b81b1128SSzymon Olewniczak if($this_ns == '') { $name_ns = 'root'; } 103*b81b1128SSzymon Olewniczak $renderer->doc .= '<tr><td colspan="3"><a href="'; 104*b81b1128SSzymon Olewniczak $renderer->doc .= wl($this_ns . ':' . $this->getConf('start')); 105*b81b1128SSzymon Olewniczak $renderer->doc .= '">'; 106*b81b1128SSzymon Olewniczak $renderer->doc .= $name_ns; 107*b81b1128SSzymon Olewniczak $renderer->doc .= '</a> '; 108*b81b1128SSzymon Olewniczak $renderer->doc .= '</td></tr>'; 109*b81b1128SSzymon Olewniczak $working_ns = $this_ns; 110*b81b1128SSzymon Olewniczak } 111*b81b1128SSzymon Olewniczak 112*b81b1128SSzymon Olewniczak $updated = '<a href="' . wl($page[0]) . '">' . dformat($page[2]) . '</a>'; 113*b81b1128SSzymon Olewniczak 114*b81b1128SSzymon Olewniczak $class = 'plugin__approve_red'; 115*b81b1128SSzymon Olewniczak $state = $this->getLang('draft'); 116*b81b1128SSzymon Olewniczak $all += 1; 117*b81b1128SSzymon Olewniczak 118*b81b1128SSzymon Olewniczak if ($page[1] === 'approved') { 119*b81b1128SSzymon Olewniczak $class = 'plugin__approve_green'; 120*b81b1128SSzymon Olewniczak $state = $this->getLang('approved'); 121*b81b1128SSzymon Olewniczak $all_approved += 1; 122*b81b1128SSzymon Olewniczak } elseif ($page[1] === 'ready for approval' && $this->getConf('ready_for_approval') === 1) { 123*b81b1128SSzymon Olewniczak $class = 'plugin__approve_ready'; 124*b81b1128SSzymon Olewniczak $state = $this->getLang('marked_approve_ready'); 125*b81b1128SSzymon Olewniczak $all_approved_ready += 1; 126*b81b1128SSzymon Olewniczak } 127*b81b1128SSzymon Olewniczak 128*b81b1128SSzymon Olewniczak $renderer->doc .= '<tr class="'.$class.'">'; 129*b81b1128SSzymon Olewniczak $renderer->doc .= '<td><a href="'; 130*b81b1128SSzymon Olewniczak $renderer->doc .= wl($page[0]); 131*b81b1128SSzymon Olewniczak $renderer->doc .= '">'; 132*b81b1128SSzymon Olewniczak if ($conf['useheading'] === '1') { 133*b81b1128SSzymon Olewniczak $heading = p_get_first_heading($page[0]); 134*b81b1128SSzymon Olewniczak if ($heading != '') { 135*b81b1128SSzymon Olewniczak $renderer->doc .= $heading; 136*b81b1128SSzymon Olewniczak } else { 137*b81b1128SSzymon Olewniczak $renderer->doc .= $page[0]; 138*b81b1128SSzymon Olewniczak } 139*b81b1128SSzymon Olewniczak 140*b81b1128SSzymon Olewniczak } else { 141*b81b1128SSzymon Olewniczak $renderer->doc .= $page[0]; 142*b81b1128SSzymon Olewniczak } 143*b81b1128SSzymon Olewniczak 144*b81b1128SSzymon Olewniczak $renderer->doc .= '</a></td><td>'; 145*b81b1128SSzymon Olewniczak $renderer->doc .= '<strong>'.$state. '</strong> '. $this->getLang('by'). ' ' . $page[4]; 146*b81b1128SSzymon Olewniczak $renderer->doc .= '</td><td>'; 147*b81b1128SSzymon Olewniczak $renderer->doc .= $updated; 148*b81b1128SSzymon Olewniczak $renderer->doc .= '</td></tr>'; 149*b81b1128SSzymon Olewniczak } 150*b81b1128SSzymon Olewniczak 151*b81b1128SSzymon Olewniczak if ($this->getConf('ready_for_approval') === 1) { 152*b81b1128SSzymon Olewniczak $renderer->doc .= '<tr><td><strong>'; 153*b81b1128SSzymon Olewniczak $renderer->doc .= $this->getLang('all_approved_ready'); 154*b81b1128SSzymon Olewniczak $renderer->doc .= '</strong></td>'; 155*b81b1128SSzymon Olewniczak 156*b81b1128SSzymon Olewniczak $renderer->doc .= '<td colspan="2">'; 157*b81b1128SSzymon Olewniczak $percent = 0; 158*b81b1128SSzymon Olewniczak if ($all > 0) { 159*b81b1128SSzymon Olewniczak $percent = $all_approved_ready*100/$all; 160*b81b1128SSzymon Olewniczak } 161*b81b1128SSzymon Olewniczak $renderer->doc .= $all_approved_ready.' / '.$all . sprintf(" (%.0f%%)", $percent); 162*b81b1128SSzymon Olewniczak $renderer->doc .= '</td></tr>'; 163*b81b1128SSzymon Olewniczak } 164*b81b1128SSzymon Olewniczak 165*b81b1128SSzymon Olewniczak $renderer->doc .= '<tr><td><strong>'; 166*b81b1128SSzymon Olewniczak $renderer->doc .= $this->getLang('all_approved'); 167*b81b1128SSzymon Olewniczak $renderer->doc .= '</strong></td>'; 168*b81b1128SSzymon Olewniczak 169*b81b1128SSzymon Olewniczak $renderer->doc .= '<td colspan="2">'; 170*b81b1128SSzymon Olewniczak $percent = 0; 171*b81b1128SSzymon Olewniczak if ($all > 0) { 172*b81b1128SSzymon Olewniczak $percent = $all_approved*100/$all; 173*b81b1128SSzymon Olewniczak } 174*b81b1128SSzymon Olewniczak $renderer->doc .= $all_approved.' / '.$all . sprintf(" (%.0f%%)", $percent); 175*b81b1128SSzymon Olewniczak $renderer->doc .= '</td></tr>'; 176*b81b1128SSzymon Olewniczak 177*b81b1128SSzymon Olewniczak 178*b81b1128SSzymon Olewniczak 179*b81b1128SSzymon Olewniczak $renderer->doc .= '</table>'; 180*b81b1128SSzymon Olewniczak return true; 181*b81b1128SSzymon Olewniczak } 182*b81b1128SSzymon Olewniczak 183*b81b1128SSzymon Olewniczak function _search_helper(&$data, $base, $file, $type, $lvl, $opts) { 184*b81b1128SSzymon Olewniczak global $lang; 185*b81b1128SSzymon Olewniczak 186*b81b1128SSzymon Olewniczak $ns = $opts[0]; 187*b81b1128SSzymon Olewniczak $invalid_ns = $opts[1]; 188*b81b1128SSzymon Olewniczak $page_regex = $opts[2]; 189*b81b1128SSzymon Olewniczak $states = $opts[3]; 190*b81b1128SSzymon Olewniczak 191*b81b1128SSzymon Olewniczak if ($type == 'd') { 192*b81b1128SSzymon Olewniczak return true; 193*b81b1128SSzymon Olewniczak } 194*b81b1128SSzymon Olewniczak 195*b81b1128SSzymon Olewniczak if (!preg_match('#\.txt$#', $file)) { 196*b81b1128SSzymon Olewniczak return false; 197*b81b1128SSzymon Olewniczak } 198*b81b1128SSzymon Olewniczak 199*b81b1128SSzymon Olewniczak $id = pathID($ns . $file); 200*b81b1128SSzymon Olewniczak if (!empty($invalid_ns) && $this->hlp->in_namespace($invalid_ns, $id)) { 201*b81b1128SSzymon Olewniczak return false; 202*b81b1128SSzymon Olewniczak } 203*b81b1128SSzymon Olewniczak 204*b81b1128SSzymon Olewniczak //check page_regex 205*b81b1128SSzymon Olewniczak if ($page_regex && !preg_match($page_regex, $id)) { 206*b81b1128SSzymon Olewniczak return false; 207*b81b1128SSzymon Olewniczak } 208*b81b1128SSzymon Olewniczak 209*b81b1128SSzymon Olewniczak //check states 210*b81b1128SSzymon Olewniczak 211*b81b1128SSzymon Olewniczak 212*b81b1128SSzymon Olewniczak $meta = p_get_metadata($id); 213*b81b1128SSzymon Olewniczak 214*b81b1128SSzymon Olewniczak 215*b81b1128SSzymon Olewniczak //check states 216*b81b1128SSzymon Olewniczak $sum = $meta['last_change']['sum']; 217*b81b1128SSzymon Olewniczak if ($sum == ApproveConst::APPROVED && !in_array(ApproveConst::APPROVED, $states)) { 218*b81b1128SSzymon Olewniczak return false; 219*b81b1128SSzymon Olewniczak } 220*b81b1128SSzymon Olewniczak 221*b81b1128SSzymon Olewniczak if ($sum == ApproveConst::READY_FOR_APPROVAL && !in_array(ApproveConst::READY_FOR_APPROVAL, $states)) { 222*b81b1128SSzymon Olewniczak return false; 223*b81b1128SSzymon Olewniczak } 224*b81b1128SSzymon Olewniczak 225*b81b1128SSzymon Olewniczak if ($sum != ApproveConst::APPROVED && $sum != ApproveConst::READY_FOR_APPROVAL && 226*b81b1128SSzymon Olewniczak !in_array(ApproveConst::DRAFT, $states)) { 227*b81b1128SSzymon Olewniczak return false; 228*b81b1128SSzymon Olewniczak } 229*b81b1128SSzymon Olewniczak 230*b81b1128SSzymon Olewniczak $date = $meta['date']['modified']; 231*b81b1128SSzymon Olewniczak if (isset($meta['last_change']) && $meta['last_change']['sum'] === ApproveConst::APPROVED) { 232*b81b1128SSzymon Olewniczak $approved = 'approved'; 233*b81b1128SSzymon Olewniczak } elseif (isset($meta['last_change']) && $meta['last_change']['sum'] === ApproveConst::READY_FOR_APPROVAL) { 234*b81b1128SSzymon Olewniczak $approved = 'ready for approval'; 235*b81b1128SSzymon Olewniczak } else { 236*b81b1128SSzymon Olewniczak $approved = 'not approved'; 237*b81b1128SSzymon Olewniczak } 238*b81b1128SSzymon Olewniczak 239*b81b1128SSzymon Olewniczak if (isset($meta['last_change'])) { 240*b81b1128SSzymon Olewniczak $user = $meta['last_change']['user']; 241*b81b1128SSzymon Olewniczak 242*b81b1128SSzymon Olewniczak if (isset($meta['contributor'][$user])) { 243*b81b1128SSzymon Olewniczak $full_name = $meta['contributor'][$user]; 244*b81b1128SSzymon Olewniczak } else { 245*b81b1128SSzymon Olewniczak $full_name = $meta['creator']; 246*b81b1128SSzymon Olewniczak } 247*b81b1128SSzymon Olewniczak } else { 248*b81b1128SSzymon Olewniczak $user = ''; 249*b81b1128SSzymon Olewniczak $full_name = '('.$lang['external_edit'].')'; 250*b81b1128SSzymon Olewniczak } 251*b81b1128SSzymon Olewniczak 252*b81b1128SSzymon Olewniczak 253*b81b1128SSzymon Olewniczak $data[] = array($id, $approved, $date, $user, $full_name); 254*b81b1128SSzymon Olewniczak 255*b81b1128SSzymon Olewniczak return false; 256*b81b1128SSzymon Olewniczak } 257*b81b1128SSzymon Olewniczak 258*b81b1128SSzymon Olewniczak function _getPagesFromNamespace($namespace, $page_regex=false, $states=[]) { 259*b81b1128SSzymon Olewniczak global $conf; 260*b81b1128SSzymon Olewniczak $dir = $conf['datadir'] . '/' . str_replace(':', '/', $namespace); 261*b81b1128SSzymon Olewniczak $pages = array(); 262*b81b1128SSzymon Olewniczak search($pages, $dir, array($this,'_search_helper'), 263*b81b1128SSzymon Olewniczak array($namespace, $this->getConf('no_apr_namespaces'), $page_regex, $states)); 264*b81b1128SSzymon Olewniczak 265*b81b1128SSzymon Olewniczak return $pages; 266*b81b1128SSzymon Olewniczak } 267*b81b1128SSzymon Olewniczak 268*b81b1128SSzymon Olewniczak 269*b81b1128SSzymon Olewniczak 270*b81b1128SSzymon Olewniczak /** 271*b81b1128SSzymon Olewniczak * Custom sort callback 272*b81b1128SSzymon Olewniczak */ 273*b81b1128SSzymon Olewniczak function _pagesorter($a, $b){ 274*b81b1128SSzymon Olewniczak $ac = explode(':',$a[0]); 275*b81b1128SSzymon Olewniczak $bc = explode(':',$b[0]); 276*b81b1128SSzymon Olewniczak $an = count($ac); 277*b81b1128SSzymon Olewniczak $bn = count($bc); 278*b81b1128SSzymon Olewniczak 279*b81b1128SSzymon Olewniczak // Same number of elements, can just string sort 280*b81b1128SSzymon Olewniczak if($an == $bn) { return strcmp($a[0], $b[0]); } 281*b81b1128SSzymon Olewniczak 282*b81b1128SSzymon Olewniczak // For each level: 283*b81b1128SSzymon Olewniczak // If this is not the last element in either list: 284*b81b1128SSzymon Olewniczak // same -> continue 285*b81b1128SSzymon Olewniczak // otherwise strcmp 286*b81b1128SSzymon Olewniczak // If this is the last element in either list, it wins 287*b81b1128SSzymon Olewniczak $n = 0; 288*b81b1128SSzymon Olewniczak while(true) { 289*b81b1128SSzymon Olewniczak if($n + 1 == $an) { return -1; } 290*b81b1128SSzymon Olewniczak if($n + 1 == $bn) { return 1; } 291*b81b1128SSzymon Olewniczak $s = strcmp($ac[$n], $bc[$n]); 292*b81b1128SSzymon Olewniczak if($s != 0) { return $s; } 293*b81b1128SSzymon Olewniczak $n += 1; 294*b81b1128SSzymon Olewniczak } 295*b81b1128SSzymon Olewniczak } 296*b81b1128SSzymon Olewniczak 297*b81b1128SSzymon Olewniczak} 298