xref: /plugin/approve/syntax/table.php (revision b81b11283adb1dcdb0fe36d5f4bdac1021fa065b)
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