1<?php
2
3use dokuwiki\Extension\SyntaxPlugin;
4
5class syntax_plugin_approve_table extends SyntaxPlugin {
6
7    protected $states = ['approved', 'draft', 'ready_for_approval'];
8
9    function getType() {
10        return 'substition';
11    }
12
13    function getSort() {
14        return 20;
15    }
16
17    function PType() {
18        return 'block';
19    }
20
21    function connectTo($mode) {
22        $this->Lexer->addSpecialPattern('----+ *approve table *-+\n.*?----+', $mode,'plugin_approve_table');
23    }
24
25    function handle($match, $state, $pos, Doku_Handler $handler){
26        $lines = explode("\n", $match);
27        array_shift($lines);
28        array_pop($lines);
29
30        $params = [
31            'namespace' => '',
32            'filter' => false,
33            'states' => $this->states,
34            'summarize' => true,
35            'approver' => ''
36        ];
37
38        foreach ($lines as $line) {
39            $pair = explode(':', $line, 2);
40            if (count($pair) < 2) {
41                continue;
42            }
43            $key = trim($pair[0]);
44            $value = trim($pair[1]);
45            if ($key == 'states') {
46                $value = array_map('trim', explode(',', $value));
47                //normalize
48                $value = array_map('strtolower', $value);
49                foreach ($value as $state) {
50                    if (!in_array($state, $this->states)) {
51                        msg('approve plugin: unknown state "'.$state.'" should be: ' .
52                            implode(', ', $this->states), -1);
53                        return false;
54                    }
55                }
56            } elseif($key == 'filter') {
57                $value = trim($value, '/');
58                if (preg_match('/' . $value . '/', null) === false) {
59                    msg('approve plugin: invalid filter regex', -1);
60                    return false;
61                }
62            } elseif ($key == 'summarize') {
63                $value = $value == '0' ? false : true;
64            } elseif ($key == 'namespace') {
65                $value = trim(cleanID($value), ':');
66            }
67            $params[$key] = $value;
68        }
69        return $params;
70    }
71
72    /**
73     * Render xhtml output or metadata
74     *
75     * @param string        $mode     Renderer mode (supported modes: xhtml)
76     * @param Doku_Renderer $renderer The renderer
77     * @param array         $data     The data from the handler() function
78     *
79     * @return bool If rendering was successful.
80     */
81
82    public function render($mode, Doku_Renderer $renderer, $data)
83    {
84        $method = 'render' . ucfirst($mode);
85        if (method_exists($this, $method)) {
86            call_user_func([$this, $method], $renderer, $data);
87            return true;
88        }
89        return false;
90    }
91
92    /**
93     * Render metadata
94     *
95     * @param Doku_Renderer $renderer The renderer
96     * @param array         $data     The data from the handler() function
97     */
98    public function renderMetadata(Doku_Renderer $renderer, $params)
99    {
100        $plugin_name = $this->getPluginName();
101        $renderer->meta['plugin'][$plugin_name] = [];
102
103        $renderer->meta['plugin'][$plugin_name]['dynamic_approver'] = $params['approver'] == '$USER$';
104        $renderer->meta['plugin'][$plugin_name]['approve_table'] = true;
105    }
106
107    protected function array_equal($a, $b) {
108        return (
109            is_array($a)
110            && is_array($b)
111            && count($a) == count($b)
112            && array_diff($a, $b) === array_diff($b, $a)
113        );
114    }
115
116    public function renderXhtml(Doku_Renderer $renderer, $params)
117    {
118        global $INFO;
119
120        global $conf;
121        /** @var DokuWiki_Auth_Plugin $auth */
122        global $auth;
123
124
125
126        if ($params['approver'] == '$USER$') {
127            if (!isset($INFO['userinfo'])) return;  // only works for login users
128            $params['approver'] = $INFO['client'];
129        }
130
131        // Output Table
132        $renderer->doc .= '<table><tr>';
133        $renderer->doc .= '<th>' . $this->getLang('hdr_page') . '</th>';
134        $renderer->doc .= '<th>' . $this->getLang('hdr_state') . '</th>';
135        $renderer->doc .= '<th>' . $this->getLang('hdr_updated') . '</th>';
136        $renderer->doc .= '<th>' . $this->getLang('hdr_approver') . '</th>';
137        $renderer->doc .= '</tr>';
138
139
140        $all_approved = 0;
141        $all_approved_ready = 0;
142        $all = 0;
143
144        $curNS = '';
145
146        /** @var helper_plugin_approve_db $db */
147        $db = $this->loadHelper('approve_db');
148        $pages = $db->getPages($params['approver'], $params['states'], $params['namespace'], $params['filter']);
149        foreach($pages as $page) {
150            $id = $page['id'];
151            $approver = $page['approver'];
152            $rev = $page['rev'];
153            $approved = strtotime($page['approved']);
154            $approved_by = $page['approved_by'];
155            $ready_for_approval = strtotime($page['ready_for_approval']);
156            $ready_for_approval_by = $page['ready_for_approval_by'];
157
158            $pageNS = getNS($id);
159
160            if($pageNS != '' && $pageNS != $curNS) {
161                $curNS = $pageNS;
162
163                $renderer->doc .= '<tr><td colspan="4"><a href="';
164                $renderer->doc .= wl($curNS);
165                $renderer->doc .= '">';
166                $renderer->doc .= $curNS;
167                $renderer->doc .= '</a> ';
168                $renderer->doc .= '</td></tr>';
169            }
170
171            $all += 1;
172            if ($approved) {
173                $class = 'plugin__approve_approved';
174                $state = $this->getLang('approved');
175                $date = $approved;
176                $by = $approved_by;
177
178                $all_approved += 1;
179            } elseif ($this->getConf('ready_for_approval') && $ready_for_approval) {
180                $class = 'plugin__approve_ready';
181                $state = $this->getLang('marked_approve_ready');
182                $date = $ready_for_approval;
183                $by = $ready_for_approval_by;
184
185                $all_approved_ready += 1;
186            } else {
187                $class = 'plugin__approve_draft';
188                $state = $this->getLang('draft');
189                $date = $rev;
190                $by = p_get_metadata($id, 'last_change user');
191            }
192
193            $renderer->doc .= '<tr class="'.$class.'">';
194            $renderer->doc .= '<td><a href="';
195            $renderer->doc .= wl($id);
196            $renderer->doc .= '">';
197            if ($conf['useheading'] == '1') {
198                $heading = p_get_first_heading($id);
199                if ($heading != '') {
200                    $renderer->doc .= $heading;
201                } else {
202                    $renderer->doc .= $id;
203                }
204            } else {
205                $renderer->doc .= $id;
206            }
207
208            $renderer->doc .= '</a></td><td>';
209            $renderer->doc .= '<strong>'.$state. '</strong> ';
210
211            $user = $auth->getUserData($by);
212            if ($user) {
213                $renderer->doc .= $this->getLang('by'). ' ' . $user['name'];
214            }
215            $renderer->doc .= '</td><td>';
216            $renderer->doc .= '<a href="' . wl($id) . '">' . dformat($date) . '</a>';;
217            $renderer->doc .= '</td><td>';
218            if ($approver) {
219                $user = $auth->getUserData($approver);
220                if ($user) {
221                    $renderer->doc .= $user['name'];
222                } else {
223                    $renderer->doc .= $approver;
224                }
225            } else {
226                $renderer->doc .= '---';
227            }
228            $renderer->doc .= '</td></tr>';
229        }
230
231        if ($params['summarize']) {
232            if($this->getConf('ready_for_approval')) {
233                $renderer->doc .= '<tr><td><strong>';
234                $renderer->doc .= $this->getLang('all_approved_ready');
235                $renderer->doc .= '</strong></td>';
236
237                $renderer->doc .= '<td colspan="3">';
238                $percent       = 0;
239                if($all > 0) {
240                    $percent = $all_approved_ready * 100 / $all;
241                }
242                $renderer->doc .= $all_approved_ready . ' / ' . $all . sprintf(" (%.0f%%)", $percent);
243                $renderer->doc .= '</td></tr>';
244            }
245
246            $renderer->doc .= '<tr><td><strong>';
247            $renderer->doc .= $this->getLang('all_approved');
248            $renderer->doc .= '</strong></td>';
249
250            $renderer->doc .= '<td colspan="3">';
251            $percent       = 0;
252            if($all > 0) {
253                $percent = $all_approved * 100 / $all;
254            }
255            $renderer->doc .= $all_approved . ' / ' . $all . sprintf(" (%.0f%%)", $percent);
256            $renderer->doc .= '</td></tr>';
257        }
258
259        $renderer->doc .= '</table>';
260    }
261}
262