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