xref: /plugin/approve/action/approve.php (revision 1b552e8770c2762b98667d8e9f261a9c379a4ec6)
1<?php
2
3use dokuwiki\plugin\approve\meta\ApproveConst;
4
5if(!defined('DOKU_INC')) die();
6
7class action_plugin_approve_approve extends DokuWiki_Action_Plugin {
8
9    /** @var helper_plugin_sqlite */
10    protected $sqlite;
11
12    /** @var helper_plugin_approve */
13    protected $helper;
14
15    /**
16     * @return helper_plugin_sqlite
17     */
18    protected function sqlite() {
19        if (!$this->sqlite) {
20            /** @var helper_plugin_approve_db $db_helper */
21            $db_helper = plugin_load('helper', 'approve_db');
22            $this->sqlite = $db_helper->getDB();
23        }
24        return $this->sqlite;
25    }
26
27    /**
28     * @return helper_plugin_approve
29     */
30    protected function helper() {
31        if (!$this->helper) {
32            $helper = plugin_load('helper', 'approve');
33            $this->helper = $helper;
34        }
35        return $this->helper;
36    }
37
38
39    /**
40     * @param Doku_Event_Handler $controller
41     */
42    public function register(Doku_Event_Handler $controller) {
43        $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'handle_diff_accept');
44        $controller->register_hook('HTML_SHOWREV_OUTPUT', 'BEFORE', $this, 'handle_showrev');
45        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_approve');
46        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_viewer');
47        $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'handle_display_banner');
48        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handle_pagesave_after');
49    }
50
51    /**
52     * @param Doku_Event $event
53     */
54    public function handle_diff_accept(Doku_Event $event) {
55		global $INFO;
56
57		if (!$this->helper()->use_approve_here($INFO['id'])) return;
58
59		if ($event->data == 'diff' && isset($_GET['approve'])) {
60		    $href = wl($INFO['id'], ['approve' => 'approve']);
61			ptln('<a href="' . $href . '">'.$this->getLang('approve').'</a>');
62		}
63
64        if ($this->getConf('ready_for_approval') && $event->data == 'diff' && isset($_GET['ready_for_approval'])) {
65            $href = wl($INFO['id'], ['ready_for_approval' => 'ready_for_approval']);
66            ptln('<a href="' . $href . '">'.$this->getLang('approve').'</a>');
67		}
68	}
69
70    /**
71     * @param Doku_Event $event
72     */
73    public function handle_showrev(Doku_Event $event) {
74        global $INFO;
75
76        if (!$this->helper()->use_approve_here($INFO['id'])) return;
77
78        $last_approved_rev = $this->helper()->find_last_approved($INFO['id']);
79		if ($last_approved_rev == $INFO['rev']) {
80            $event->preventDefault();
81        }
82	}
83
84	/**
85     * @param Doku_Event $event
86     */
87    public function handle_approve(Doku_Event $event) {
88		global $INFO;
89
90        if (!$this->helper()->use_approve_here($INFO['id'])) return;
91
92		if ($event->data == 'show' && isset($_GET['approve']) &&
93            auth_quickaclcheck($INFO['id']) >= AUTH_DELETE) {
94
95		    $res = $this->sqlite()->query('SELECT MAX(version)+1 FROM revision
96                                            WHERE page=?', $INFO['id']);
97		    $next_version = $this->sqlite()->res2single($res);
98		    if (!$next_version) {
99                $next_version = 1;
100            }
101		    //approved IS NULL prevents from overriding already approved page
102		    $this->sqlite()->query('UPDATE revision
103		                    SET approved=?, version=?
104                            WHERE page=? AND current=1 AND approved IS NULL',
105                            date('c'), $next_version, $INFO['id']);
106
107			header('Location: ' . wl($INFO['id']));
108		} elseif ($event->data == 'show' && isset($_GET['ready_for_approval']) &&
109            auth_quickaclcheck($INFO['id']) >= AUTH_EDIT) {
110
111            $this->sqlite()->query('UPDATE revision SET ready_for_approval=?
112                            WHERE page=? AND current=1 AND ready_for_approval IS NULL',
113                            date('c'), $INFO['id']);
114
115            header('Location: ' . wl($INFO['id']));
116		}
117	}
118
119    /**
120     * Redirect to newest approved page for user that don't have EDIT permission.
121     *
122     * @param Doku_Event $event
123     */
124    public function handle_viewer(Doku_Event $event) {
125        global $INFO;
126
127        if ($event->data != 'show') return;
128        //apply only to current page
129        if (!$INFO['rev']) return;
130        if (auth_quickaclcheck($INFO['id']) >= AUTH_EDIT) return;
131        if (!$this->helper()->use_approve_here($INFO['id'])) return;
132
133        $last_approved_rev = $this->helper()->find_last_approved($INFO['id']);
134        //no page is approved
135        if (!$last_approved_rev) return;
136
137        $last_change_date = @filemtime(wikiFN($INFO['id']));
138        //current page is approved
139        if ($last_approved_rev == $last_change_date) return;
140
141	    header("Location: " . wl($INFO['id'], ['rev' => $last_approved_rev]));
142	}
143
144    /**
145     * @param Doku_Event $event
146     */
147    public function handle_display_banner(Doku_Event $event) {
148		global $INFO;
149
150        if ($event->data != 'show') return;
151        if (!$INFO['exists']) return;
152        if (!$this->helper()->use_approve_here($INFO['id'])) return;
153
154//        $last_change_date = p_get_metadata($INFO['id'], 'last_change date');
155        $last_change_date = @filemtime(wikiFN($INFO['id']));
156        $rev = !$INFO['rev'] ? $last_change_date : $INFO['rev'];
157
158
159        $res = $this->sqlite()->query('SELECT ready_for_approval, approved, version
160                                FROM revision
161                                WHERE page=? AND rev=?', $INFO['id'], $rev);
162
163        $approve = $this->sqlite()->res_fetch_assoc($res);
164
165		$classes = [];
166		if ($this->getConf('prettyprint')) {
167		    $classes[] = 'plugin__approve_noprint';
168        }
169
170        if ($approve['approved']) {
171		    $classes[] = 'plugin__approve_green';
172		} elseif ($this->getConf('ready_for_approval') && $approve['ready_for_approval']) {
173		    $classes[] = 'plugin__approve_ready';
174        } else {
175            $classes[] = 'plugin__approve_red';
176        }
177
178		ptln('<div id="plugin__approve" class="' . implode(' ', $classes) . '">');
179
180		tpl_pageinfo();
181		ptln(' | ');
182
183		if ($approve['approved']) {
184			ptln('<strong>'.$this->getLang('approved').'</strong> ('
185                . $this->getLang('version') .  ': ' . $approve['version'] . ')');
186
187			//not the newest page
188			if ($rev != $last_change_date) {
189                $res = $this->sqlite()->query('SELECT rev, current FROM revision
190                                WHERE page=? AND approved IS NOT NULL
191                                ORDER BY rev DESC LIMIT 1', $INFO['id']);
192
193                $lastest_approve = $this->sqlite()->res_fetch_assoc($res);
194
195			    //we can see drafts
196                if (auth_quickaclcheck($INFO['id']) >= AUTH_EDIT) {
197                    ptln('<a href="' . wl($INFO['id']) . '">');
198                    ptln($this->getLang($lastest_approve['current'] ? 'newest_approved' : 'newest_draft'));
199                    ptln('</a>');
200                } else {
201                    $urlParameters = [];
202                    if (!$lastest_approve['current']) {
203                        $urlParameters['rev'] = $lastest_approve['rev'];
204                    }
205                    ptln('<a href="' . wl($INFO['id'], $urlParameters) . '">');
206                    ptln($this->getLang('newest_approved'));
207                    ptln('</a>');
208                }
209            }
210
211		} else {
212			ptln('<span>'.$this->getLang('draft').'</span>');
213
214			if ($this->getConf('ready_for_approval') && $approve['ready_for_approval']) {
215				ptln('<span>| '.$this->getLang('marked_approve_ready').'</span>');
216			}
217
218
219            $res = $this->sqlite()->query('SELECT rev, current FROM revision
220                            WHERE page=? AND approved IS NOT NULL
221                            ORDER BY rev DESC LIMIT 1', $INFO['id']);
222
223            $lastest_approve = $this->sqlite()->res_fetch_assoc($res);
224
225
226            //not exists approve for current page
227			if (!$lastest_approve) {
228                //not the newest page
229                if ($rev != $last_change_date) {
230				    ptln('<a href="'.wl($INFO['id']).'">');
231                    ptln($this->getLang('newest_draft'));
232				    ptln('</a>');
233				}
234			} else {
235                $urlParameters = [];
236                if (!$lastest_approve['current']) {
237                    $urlParameters['rev'] = $lastest_approve['rev'];
238                }
239                ptln('<a href="' . wl($INFO['id'], $urlParameters) . '">');
240                ptln($this->getLang('newest_approved'));
241				ptln('</a>');
242			}
243
244			//we are in current page
245			if ($rev == $last_change_date) {
246
247                $last_approved_rev = 0;
248                if (isset($lastest_approve['rev'])) {
249                    $last_approved_rev = $lastest_approve['rev'];
250                }
251
252                if ($this->getConf('ready_for_approval') &&
253                    auth_quickaclcheck($INFO['id']) >= AUTH_EDIT &&
254                    !$approve['ready_for_approval']) {
255
256                    $urlParameters = [
257                        'rev' => $last_approved_rev,
258                        'do' => 'diff',
259                        'ready_for_approval' => 'ready_for_approval'
260                    ];
261                    ptln(' | <a href="'.wl($INFO['id'], $urlParameters).'">');
262                    ptln($this->getLang('approve_ready'));
263                    ptln('</a>');
264                }
265
266                if (auth_quickaclcheck($INFO['id']) >= AUTH_DELETE) {
267
268                    $urlParameters = [
269                        'rev' => $last_approved_rev,
270                        'do' => 'diff',
271                        'approve' => 'approve'
272                    ];
273                    ptln(' | <a href="'.wl($INFO['id'], $urlParameters).'">');
274                    ptln($this->getLang('approve'));
275                    ptln('</a>');
276                }
277
278            }
279		}
280		ptln('</div>');
281	}
282
283    /**
284     * @return bool|string
285     */
286    protected function prev_rev($id) {
287        $res = $this->sqlite()->query('SELECT rev FROM revision
288                                        WHERE page=?
289                                          AND current=1
290                                          AND approved IS NULL
291                                          AND ready_for_approval IS NULL', $id);
292
293        return $this->sqlite()->res2single($res);
294    }
295
296    /**
297     *
298     * @param Doku_Event $event  event object by reference
299     * @return void
300     */
301    public function handle_pagesave_after(Doku_Event $event) {
302        //no content was changed
303        if (!$event->data['contentChanged']) return;
304
305        $changeType = $event->data['changeType'];
306        if ($changeType == DOKU_CHANGE_TYPE_REVERT) {
307            if ($event->data['oldContent'] == '') {
308                $changeType = DOKU_CHANGE_TYPE_CREATE;
309            } else {
310                $changeType = DOKU_CHANGE_TYPE_EDIT;
311            }
312        }
313
314        $id = $event->data['id'];
315        switch ($changeType) {
316            case DOKU_CHANGE_TYPE_EDIT:
317            case DOKU_CHANGE_TYPE_REVERT:
318            case DOKU_CHANGE_TYPE_MINOR_EDIT:
319                $last_change_date = $event->data['newRevision'];
320
321                //if the current page has approved or ready_for_approval -- keep it
322                $prev_rev = $this->prev_rev($id);
323                if ($prev_rev) {
324                    $this->sqlite()->query('UPDATE revision SET rev=? WHERE page=? AND rev=?',
325                        $last_change_date, $id, $prev_rev);
326
327                } else {
328                    //keep previous record
329                    $this->sqlite()->query('UPDATE revision SET current=0
330                                            WHERE page=?
331                                            AND current=1', $id);
332
333                    $this->sqlite()->storeEntry('revision', [
334                        'page' => $id,
335                        'rev' => $last_change_date,
336                        'current' => 1
337                    ]);
338                }
339                break;
340            case DOKU_CHANGE_TYPE_DELETE:
341                //delete information about availability of a page but keep the history
342                $this->sqlite()->query('DELETE FROM page WHERE page=?', $id);
343
344                //delete revision if no information about approvals
345                $prev_rev = $this->prev_rev($id);
346                if ($prev_rev) {
347                    $this->sqlite()->query('DELETE FROM revision WHERE page=? AND rev=?',
348                        $id, $prev_rev);
349                } else {
350                    $this->sqlite()->query('UPDATE revision SET current=0 WHERE page=? AND rev=?',
351                        $id, $prev_rev);
352                }
353
354                break;
355            case DOKU_CHANGE_TYPE_CREATE:
356                $last_change_date = $event->data['newRevision'];
357                $hidden = $this->helper()->in_hidden_namespace($id);
358
359                $this->sqlite()->storeEntry('page', [
360                    'page' => $id,
361                    'hidden' => $hidden
362                ]);
363
364                $this->sqlite()->storeEntry('revision', [
365                    'page' => $id,
366                    'rev' => $last_change_date,
367                    'current' => 1
368                ]);
369                break;
370        }
371    }
372}
373