xref: /plugin/approve/syntax/table.php (revision ac3d390a3a827d6e16da8f8e7f6a45ced7165111)
1<?php
2
3// must be run within DokuWiki
4if(!defined('DOKU_INC')) die();
5
6
7class syntax_plugin_approve_table extends DokuWiki_Syntax_Plugin {
8
9    protected $states = ['approved', 'draft', 'ready_for_approval'];
10
11    function getType() {
12        return 'substition';
13    }
14
15    function getSort() {
16        return 20;
17    }
18
19    function PType() {
20        return 'block';
21    }
22
23    function connectTo($mode) {
24        $this->Lexer->addSpecialPattern('----+ *approve table *-+\n.*?----+', $mode,'plugin_approve_table');
25    }
26
27    function handle($match, $state, $pos, Doku_Handler $handler){
28        $lines = explode("\n", $match);
29        array_shift($lines);
30        array_pop($lines);
31
32        $params = [
33            'namespace' => '',
34            'filter' => false,
35            'states' => [],
36            'summarize' => true,
37            'maintainer' => null
38        ];
39
40        foreach ($lines as $line) {
41            $pair = explode(':', $line, 2);
42            if (count($pair) < 2) {
43                continue;
44            }
45            $key = trim($pair[0]);
46            $value = trim($pair[1]);
47            if ($key == 'states') {
48                $value = array_map('trim', explode(',', $value));
49                //normalize
50                $value = array_map('strtolower', $value);
51                foreach ($value as $state) {
52                    if (!in_array($state, $this->states)) {
53                        msg('approve plugin: unknown state "'.$state.'" should be: ' .
54                            implode(', ', $this->states), -1);
55                        return false;
56                    }
57                }
58            } elseif($key == 'filter') {
59                $value = trim($value, '/');
60                if (preg_match('/' . $value . '/', null) === false) {
61                    msg('approve plugin: invalid filter regex', -1);
62                    return false;
63                }
64            } elseif ($key == 'summarize') {
65                $value = $value == '0' ? false : true;
66            } elseif ($key == 'namespace') {
67                $value = trim(cleanID($value), ':');
68            }
69            $params[$key] = $value;
70        }
71        return $params;
72    }
73
74    /**
75     * Render xhtml output or metadata
76     *
77     * @param string        $mode     Renderer mode (supported modes: xhtml)
78     * @param Doku_Renderer $renderer The renderer
79     * @param array         $data     The data from the handler() function
80     *
81     * @return bool If rendering was successful.
82     */
83
84    public function render($mode, Doku_Renderer $renderer, $data)
85    {
86        $method = 'render' . ucfirst($mode);
87        if (method_exists($this, $method)) {
88            call_user_func([$this, $method], $renderer, $data);
89            return true;
90        }
91        return false;
92    }
93
94    /**
95     * Render metadata
96     *
97     * @param Doku_Renderer $renderer The renderer
98     * @param array         $data     The data from the handler() function
99     */
100    public function renderMetadata(Doku_Renderer $renderer, $params)
101    {
102        $plugin_name = $this->getPluginName();
103        $renderer->meta['plugin'][$plugin_name] = [];
104
105        if ($params['maintainer'] == '$USER$') {
106            $renderer->meta['plugin'][$plugin_name]['dynamic_maintainer'] = true;
107        }
108
109        $renderer->meta['plugin'][$plugin_name]['approve_table'] = true;
110    }
111
112    protected function array_equal($a, $b) {
113        return (
114            is_array($a)
115            && is_array($b)
116            && count($a) == count($b)
117            && array_diff($a, $b) === array_diff($b, $a)
118        );
119    }
120
121    public function renderXhtml(Doku_Renderer $renderer, $params)
122    {
123        global $INFO;
124
125        global $conf;
126        /** @var DokuWiki_Auth_Plugin $auth */
127        global $auth;
128
129        /** @var \helper_plugin_ireadit_db $db_helper */
130        $db_helper = plugin_load('helper', 'approve_db');
131        $sqlite = $db_helper->getDB();
132
133        if ($params['maintainer'] == '$USER$') {
134            $params['maintainer'] = $INFO['client'];
135        }
136
137        $maintainer_query = '';
138        $query_args = [$params['namespace'].'%'];
139        if ($params['maintainer']) {
140            $maintainer_query .= " AND page.maintainer LIKE ?";
141            $query_args[] = $params['maintainer'];
142        }
143
144        if ($params['filter']) {
145            $maintainer_query .= " AND page.page REGEXP ?";
146            $query_args[] = $params['filter'];
147        }
148
149        //if all 3 states are enabled nothing is filtered
150        if ($params['states'] && count($params['states']) < 3) {
151            if ($this->array_equal(['draft'], $params['states'])) {
152                $maintainer_query .= " AND revision.ready_for_approval IS NULL AND revision.approved IS NULL";
153            } elseif ($this->array_equal(['ready_for_approval'], $params['states'])) {
154                $maintainer_query .= " AND revision.ready_for_approval IS NOT NULL AND revision.approved IS NULL";
155            } elseif ($this->array_equal(['approved'], $params['states'])) {
156                $maintainer_query .= " AND revision.approved IS NOT NULL";
157            } elseif ($this->array_equal(['draft', 'ready_for_approval'], $params['states'])) {
158                $maintainer_query .= " AND revision.approved IS NULL";
159            } elseif ($this->array_equal(['draft', 'approved'], $params['states'])) {
160                $maintainer_query .= " AND (revision.approved IS NOT NULL OR (revision.approved IS NULL AND revision.ready_for_approval IS NULL))";
161            } elseif ($this->array_equal(['ready_for_approval', 'approved'], $params['states'])) {
162                $maintainer_query .= " AND (revision.ready_for_approval IS NOT NULL OR revision.approved IS NOT NULL)";
163            }
164        }
165
166        $q = "SELECT page.page, page.maintainer, revision.rev, revision.approved, revision.approved_by,
167                    revision.ready_for_approval, revision.ready_for_approval_by,
168                    LENGTH(page.page) - LENGTH(REPLACE(page.page, ':', '')) AS colons
169                    FROM page INNER JOIN revision ON page.page = revision.page
170                    WHERE page.hidden = 0 AND revision.current=1 AND page.page LIKE ? ESCAPE '_'
171                            $maintainer_query
172                    ORDER BY colons, page.page";
173
174        $res = $sqlite->query($q, $query_args);
175        $pages = $sqlite->res2arr($res);
176
177        // Output Table
178        $renderer->doc .= '<table><tr>';
179        $renderer->doc .= '<th>' . $this->getLang('hdr_page') . '</th>';
180        $renderer->doc .= '<th>' . $this->getLang('hdr_state') . '</th>';
181        $renderer->doc .= '<th>' . $this->getLang('hdr_updated') . '</th>';
182        $renderer->doc .= '<th>' . $this->getLang('hdr_maintainer') . '</th>';
183        $renderer->doc .= '</tr>';
184
185
186        $all_approved = 0;
187        $all_approved_ready = 0;
188        $all = 0;
189
190        $curNS = '';
191        foreach($pages as $page) {
192            $id = $page['page'];
193            $maintainer = $page['maintainer'];
194            $rev = $page['rev'];
195            $approved = strtotime($page['approved']);
196            $approved_by = $page['approved_by'];
197            $ready_for_approval = strtotime($page['ready_for_approval']);
198            $ready_for_approval_by = $page['ready_for_approval_by'];
199
200            $pageNS = getNS($id);
201
202            if($pageNS != '' && $pageNS != $curNS) {
203                $curNS = $pageNS;
204
205                $renderer->doc .= '<tr><td colspan="4"><a href="';
206                $renderer->doc .= wl($curNS);
207                $renderer->doc .= '">';
208                $renderer->doc .= $curNS;
209                $renderer->doc .= '</a> ';
210                $renderer->doc .= '</td></tr>';
211            }
212
213            $all += 1;
214            if ($approved) {
215                $class = 'plugin__approve_green';
216                $state = $this->getLang('approved');
217                $date = $approved;
218                $by = $approved_by;
219
220                $all_approved += 1;
221            } elseif ($this->getConf('ready_for_approval') && $ready_for_approval) {
222                $class = 'plugin__approve_ready';
223                $state = $this->getLang('marked_approve_ready');
224                $date = $ready_for_approval;
225                $by = $ready_for_approval_by;
226
227                $all_approved_ready += 1;
228            } else {
229                $class = 'plugin__approve_red';
230                $state = $this->getLang('draft');
231                $date = $rev;
232                $by = p_get_metadata($id, 'last_change user');
233            }
234
235            $renderer->doc .= '<tr class="'.$class.'">';
236            $renderer->doc .= '<td><a href="';
237            $renderer->doc .= wl($id);
238            $renderer->doc .= '">';
239            if ($conf['useheading'] == '1') {
240                $heading = p_get_first_heading($id);
241                if ($heading != '') {
242                    $renderer->doc .= $heading;
243                } else {
244                    $renderer->doc .= $id;
245                }
246            } else {
247                $renderer->doc .= $id;
248            }
249
250            $renderer->doc .= '</a></td><td>';
251            $renderer->doc .= '<strong>'.$state. '</strong> ';
252
253            $user = $auth->getUserData($by);
254            if ($user) {
255                $renderer->doc .= $this->getLang('by'). ' ' . $user['name'];
256            }
257            $renderer->doc .= '</td><td>';
258            $renderer->doc .= '<a href="' . wl($id) . '">' . dformat($date) . '</a>';;
259            $renderer->doc .= '</td><td>';
260            if ($maintainer) {
261                $user = $auth->getUserData($maintainer);
262                if ($user) {
263                    $renderer->doc .= $user['name'];
264                } else {
265                    $renderer->doc .= $maintainer;
266                }
267            } else {
268                $renderer->doc .= '---';
269            }
270            $renderer->doc .= '</td></tr>';
271        }
272
273        if ($params['summarize']) {
274            if($this->getConf('ready_for_approval')) {
275                $renderer->doc .= '<tr><td><strong>';
276                $renderer->doc .= $this->getLang('all_approved_ready');
277                $renderer->doc .= '</strong></td>';
278
279                $renderer->doc .= '<td colspan="3">';
280                $percent       = 0;
281                if($all > 0) {
282                    $percent = $all_approved_ready * 100 / $all;
283                }
284                $renderer->doc .= $all_approved_ready . ' / ' . $all . sprintf(" (%.0f%%)", $percent);
285                $renderer->doc .= '</td></tr>';
286            }
287
288            $renderer->doc .= '<tr><td><strong>';
289            $renderer->doc .= $this->getLang('all_approved');
290            $renderer->doc .= '</strong></td>';
291
292            $renderer->doc .= '<td colspan="3">';
293            $percent       = 0;
294            if($all > 0) {
295                $percent = $all_approved * 100 / $all;
296            }
297            $renderer->doc .= $all_approved . ' / ' . $all . sprintf(" (%.0f%%)", $percent);
298            $renderer->doc .= '</td></tr>';
299        }
300
301        $renderer->doc .= '</table>';
302    }
303}
304