1<?php
2
3use dokuwiki\Extension\ActionPlugin;
4use dokuwiki\Extension\EventHandler;
5use dokuwiki\Extension\Event;
6
7class action_plugin_approve_approve extends ActionPlugin {
8    /**
9     * @inheritDoc
10     */
11    public function register(EventHandler $controller) {
12        $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'handle_diff_accept');
13        $controller->register_hook('HTML_SHOWREV_OUTPUT', 'BEFORE', $this, 'handle_showrev');
14        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_approve');
15        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_mark_ready_for_approval');
16        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_viewer');
17        $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'handle_display_banner');
18        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handle_pagesave_after');
19    }
20
21    /**
22     * @param Event $event
23     */
24    public function handle_diff_accept(Event $event) {
25        global $INFO;
26
27        /** @var helper_plugin_approve_acl $acl */
28        $acl = $this->loadHelper('approve_acl');
29
30        if (!$acl->useApproveHere($INFO['id'])) return;
31
32        if ($event->data == 'diff' && isset($_GET['approve'])) {
33            $href = wl($INFO['id'], ['approve' => 'approve']);
34            echo '<a href="' . $href . '">'.$this->getLang('approve').'</a>';
35        }
36
37        if ($this->getConf('ready_for_approval') && $event->data == 'diff' && isset($_GET['ready_for_approval'])) {
38            $href = wl($INFO['id'], ['ready_for_approval' => 'ready_for_approval']);
39            echo '<a href="' . $href . '">'.$this->getLang('approve_ready').'</a>';
40        }
41    }
42
43    /**
44     * @param Event $event
45     */
46    public function handle_showrev(Event $event) {
47        global $INFO;
48
49        /** @var helper_plugin_approve_db $db */
50        $db = $this->loadHelper('approve_db');
51        /** @var helper_plugin_approve_acl $acl */
52        $acl = $this->loadHelper('approve_acl');
53
54        if (!$acl->useApproveHere($INFO['id'])) return;
55
56        $last_approved_rev = $db->getLastDbRev($INFO['id'], 'approved');
57        if ($last_approved_rev == $INFO['rev']) {
58            $event->preventDefault();
59        }
60    }
61
62    /**
63     * @param Event $event
64     */
65    public function handle_approve(Event $event) {
66        global $INFO;
67
68        /** @var helper_plugin_approve_acl $acl */
69        $acl = $this->loadHelper('approve_acl');
70
71        if ($event->data != 'show') return;
72        if (!isset($_GET['approve'])) return;
73        if (!$acl->useApproveHere($INFO['id'])) return;
74        if (!$acl->clientCanApprove($INFO['id'])) return;
75
76        /** @var helper_plugin_approve_db $db */
77        $db = $this->loadHelper('approve_db');
78        $db->setApprovedStatus($INFO['id']);
79
80        header('Location: ' . wl($INFO['id']));
81    }
82
83    /**
84     * @param Event $event
85     */
86    public function handle_mark_ready_for_approval(Event $event) {
87        global $INFO;
88
89        /** @var helper_plugin_approve_acl $acl */
90        $acl = $this->loadHelper('approve_acl');
91
92        if ($event->data != 'show') return;
93        if (!isset($_GET['ready_for_approval'])) return;
94        if (!$acl->useApproveHere($INFO['id'])) return;
95        if (!$acl->clientCanMarkReadyForApproval($INFO['id'])) return;
96
97        /** @var helper_plugin_approve_db $db */
98        $db = $this->loadHelper('approve_db');
99        $db->setReadyForApprovalStatus($INFO['id']);
100
101        header('Location: ' . wl($INFO['id']));
102    }
103
104    /**
105     * Redirect to latest approved page for user that don't have EDIT permission.
106     *
107     * @param Event $event
108     */
109    public function handle_viewer(Event $event) {
110        global $INFO;
111
112        /** @var helper_plugin_approve_acl $acl */
113        $acl = $this->loadHelper('approve_acl');
114
115        if ($event->data != 'show') return;
116        //apply only to current page
117        if ($INFO['rev'] != 0) return;
118        if (!$acl->useApproveHere($INFO['id'])) return;
119        if ($acl->clientCanSeeDrafts($INFO['id'])) return;
120
121        /** @var helper_plugin_approve_db $db */
122        $db = $this->loadHelper('approve_db');
123        $last_approved_rev = $db->getLastDbRev($INFO['id'], 'approved');
124
125        //no page is approved
126        if (!$last_approved_rev) return;
127
128        $last_change_date = @filemtime(wikiFN($INFO['id']));
129        // current page is approved
130        if ($last_approved_rev == $last_change_date) return;
131
132        header("Location: " . wl($INFO['id'], ['rev' => $last_approved_rev], false, '&'));
133    }
134
135    /**
136     * @param Event $event
137     */
138    public function handle_display_banner(Event $event) {
139        global $INFO;
140
141        if ($event->data != 'show' || !$INFO['exists']) return;
142
143        /* Return true if banner should not be displayed for users with or below read only permission. */
144        if (auth_quickaclcheck($INFO['id']) <= AUTH_READ && !$this->getConf('display_banner_for_readonly')) {
145            return;
146        }
147
148        /** @var helper_plugin_approve_acl $acl */
149        $acl = $this->loadHelper('approve_acl');
150        if (!$acl->useApproveHere($INFO['id'])) return;
151
152        $last_change_date = @filemtime(wikiFN($INFO['id']));
153        $rev = !$INFO['rev'] ? $last_change_date : $INFO['rev'];
154
155
156        /** @var helper_plugin_approve_db $db */
157        $db = $this->loadHelper('approve_db');
158
159        $page_revision = $db->getPageRevision($INFO['id'], $rev);
160        $last_approved_rev = $db->getLastDbRev($INFO['id'], 'approved');
161
162        $classes = [];
163        if ($page_revision['status'] == 'approved' && $rev == $last_approved_rev) {
164            $classes[] = 'plugin__approve_approved';
165        } elseif ($page_revision['status'] == 'approved') {
166            $classes[] = 'plugin__approve_old_approved';
167        } elseif ($this->getConf('ready_for_approval') && $page_revision['status'] == 'ready_for_approval') {
168            $classes[] = 'plugin__approve_ready';
169        } else {
170            $classes[] = 'plugin__approve_draft';
171        }
172
173        echo '<div id="plugin__approve" class="' . implode(' ', $classes) . '">';
174
175
176        if ($page_revision['status'] == 'approved') {
177            echo '<strong>'.$this->getLang('approved').'</strong>';
178            echo ' ' . dformat(strtotime($page_revision['approved']));
179
180            if($this->getConf('banner_long')) {
181                echo ' ' . $this->getLang('by') . ' ' . userlink($page_revision['approved_by'], true);
182                echo ' (' . $this->getLang('version') .  ': ' . $page_revision['version'] . ')';
183            }
184
185            //not the newest page
186            if ($rev != $last_change_date) {
187                // we can see drafts
188                if ($acl->clientCanSeeDrafts($INFO['id'])) {
189                    echo ' <a href="' . wl($INFO['id']) . '">';
190                    echo $this->getLang($last_approved_rev == $last_change_date ? 'newest_approved' : 'newest_draft');
191                    echo '</a>';
192                    // we cannot see link to draft but there is some newer approved version
193                } elseif ($last_approved_rev != $rev) {
194                    $urlParameters = [];
195                    if ($last_approved_rev != $last_change_date) {
196                        $urlParameters['rev'] = $last_approved_rev;
197                    }
198                    echo ' <a href="' . wl($INFO['id'], $urlParameters) . '">';
199                    echo $this->getLang('newest_approved');
200                    echo '</a>';
201                }
202            }
203
204        } else {
205            if ($this->getConf('ready_for_approval') && $page_revision['status'] == 'ready_for_approval') {
206                echo '<strong>'.$this->getLang('marked_approve_ready').'</strong>';
207                echo ' ' . dformat(strtotime($page_revision['ready_for_approval']));
208                echo ' ' . $this->getLang('by') . ' ' . userlink($page_revision['ready_for_approval_by'], true);
209            } else {
210                echo '<strong>'.$this->getLang('draft').'</strong>';
211            }
212
213            // not exists approve for current page
214            if ($last_approved_rev == null) {
215                // not the newest page
216                if ($rev != $last_change_date) {
217                    echo ' <a href="'.wl($INFO['id']).'">';
218                    echo $this->getLang('newest_draft');
219                    echo '</a>';
220                }
221            } else {
222                $urlParameters = [];
223                if ($last_approved_rev != $last_change_date) {
224                    $urlParameters['rev'] = $last_approved_rev;
225                }
226                echo ' <a href="' . wl($INFO['id'], $urlParameters) . '">';
227                echo $this->getLang('newest_approved');
228                echo '</a>';
229            }
230
231            //we are in current page
232            if ($rev == $last_change_date) {
233                if ($this->getConf('ready_for_approval') &&
234                    $acl->clientCanMarkReadyForApproval($INFO['id']) &&
235                    $page_revision['status'] != 'ready_for_approval') {
236
237                    $urlParameters = [
238                        'rev' => $last_approved_rev,
239                        'do' => 'diff',
240                        'ready_for_approval' => 'ready_for_approval'
241                    ];
242                    echo ' | <a href="'.wl($INFO['id'], $urlParameters).'">';
243                    echo $this->getLang('approve_ready');
244                    echo '</a>';
245                }
246
247                if ($acl->clientCanApprove($INFO['id'])) {
248                    $urlParameters = [
249                        'rev' => $last_approved_rev,
250                        'do' => 'diff',
251                        'approve' => 'approve'
252                    ];
253                    echo ' | <a href="'.wl($INFO['id'], $urlParameters).'">';
254                    echo $this->getLang('approve');
255                    echo '</a>';
256                }
257            }
258        }
259
260
261        if ($this->getConf('banner_long')) {
262            $page_metadata = $db->getPageMetadata($INFO['id']);
263            if (isset($page_metadata['approver'])) {
264                echo ' | ' . $this->getLang('approver') . ': ' . userlink($page_metadata['approver'], true);
265            }
266        }
267
268        echo '</div>';
269    }
270
271    /**
272     *
273     * @param Event $event
274     */
275    public function handle_pagesave_after(Event $event) {
276        //no content was changed
277        if (!$event->data['contentChanged']) return;
278
279        /** @var helper_plugin_approve_db $db */
280        $db = $this->loadHelper('approve_db');
281
282        $id = $event->data['id'];
283        switch ($event->data['changeType']) {
284            case DOKU_CHANGE_TYPE_CREATE:
285            case DOKU_CHANGE_TYPE_EDIT:
286            case DOKU_CHANGE_TYPE_MINOR_EDIT:
287            case DOKU_CHANGE_TYPE_REVERT:
288                $db->handlePageEdit($id);
289                break;
290            case DOKU_CHANGE_TYPE_DELETE:
291                $db->handlePageDelete($id);
292                break;
293        }
294    }
295}
296