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