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, $ID; 181 182 /* Return true if banner should not be displayed for users with or below read only permission. */ 183 if (auth_quickaclcheck($ID) <= AUTH_READ && !$this->getConf('display_banner_for_readonly')) { 184 return true; 185 } 186 187 /* Not returned - rendering the banner */ 188 try { 189 /** @var \helper_plugin_approve_db $db_helper */ 190 $db_helper = plugin_load('helper', 'approve_db'); 191 $sqlite = $db_helper->getDB(); 192 } catch (Exception $e) { 193 msg($e->getMessage(), -1); 194 return; 195 } 196 /** @var helper_plugin_approve $helper */ 197 $helper = plugin_load('helper', 'approve'); 198 199 if ($event->data != 'show') return; 200 if (!$INFO['exists']) return; 201 if (!$helper->use_approve_here($sqlite, $INFO['id'], $approver)) return; 202 203// $last_change_date = p_get_metadata($INFO['id'], 'last_change date'); 204 $last_change_date = @filemtime(wikiFN($INFO['id'])); 205 $rev = !$INFO['rev'] ? $last_change_date : $INFO['rev']; 206 207 208 $res = $sqlite->query('SELECT ready_for_approval, ready_for_approval_by, 209 approved, approved_by, version 210 FROM revision 211 WHERE page=? AND rev=?', $INFO['id'], $rev); 212 213 $approve = $sqlite->res_fetch_assoc($res); 214 215 $classes = []; 216 if ($this->getConf('prettyprint')) { 217 $classes[] = 'plugin__approve_noprint'; 218 } 219 220 if ($approve['approved']) { 221 $classes[] = 'plugin__approve_green'; 222 } elseif ($this->getConf('ready_for_approval') && $approve['ready_for_approval']) { 223 $classes[] = 'plugin__approve_ready'; 224 } else { 225 $classes[] = 'plugin__approve_red'; 226 } 227 228 ptln('<div id="plugin__approve" class="' . implode(' ', $classes) . '">'); 229 230// tpl_pageinfo(); 231// ptln(' | '); 232 233 if ($approve['approved']) { 234 ptln('<strong>'.$this->getLang('approved').'</strong>'); 235 ptln(' ' . dformat(strtotime($approve['approved']))); 236 237 if($this->getConf('banner_long')) { 238 ptln(' ' . $this->getLang('by') . ' ' . userlink($approve['approved_by'], true)); 239 ptln(' (' . $this->getLang('version') . ': ' . $approve['version'] . ')'); 240 } 241 242 //not the newest page 243 if ($rev != $last_change_date) { 244 $res = $sqlite->query('SELECT rev, current FROM revision 245 WHERE page=? AND approved IS NOT NULL 246 ORDER BY rev DESC LIMIT 1', $INFO['id']); 247 248 $last_approve = $sqlite->res_fetch_assoc($res); 249 250 //we can see drafts 251 if ($helper->client_can_see_drafts($INFO['id'], $approver)) { 252 ptln('<a href="' . wl($INFO['id']) . '">'); 253 ptln($this->getLang($last_approve['current'] ? 'newest_approved' : 'newest_draft')); 254 ptln('</a>'); 255 //we cannot see link to draft but there is some newer approved version 256 } elseif ($last_approve['rev'] != $rev) { 257 $urlParameters = []; 258 if (!$last_approve['current']) { 259 $urlParameters['rev'] = $last_approve['rev']; 260 } 261 ptln('<a href="' . wl($INFO['id'], $urlParameters) . '">'); 262 ptln($this->getLang('newest_approved')); 263 ptln('</a>'); 264 } 265 } 266 267 } else { 268 if ($this->getConf('ready_for_approval') && $approve['ready_for_approval']) { 269 ptln('<strong>'.$this->getLang('marked_approve_ready').'</strong>'); 270 ptln(' ' . dformat(strtotime($approve['ready_for_approval']))); 271 ptln(' ' . $this->getLang('by') . ' ' . userlink($approve['ready_for_approval_by'], true)); 272 } else { 273 ptln('<strong>'.$this->getLang('draft').'</strong>'); 274 } 275 276 277 $res = $sqlite->query('SELECT rev, current FROM revision 278 WHERE page=? AND approved IS NOT NULL 279 ORDER BY rev DESC LIMIT 1', $INFO['id']); 280 281 $last_approve = $sqlite->res_fetch_assoc($res); 282 283 284 //not exists approve for current page 285 if (!$last_approve) { 286 //not the newest page 287 if ($rev != $last_change_date) { 288 ptln('<a href="'.wl($INFO['id']).'">'); 289 ptln($this->getLang('newest_draft')); 290 ptln('</a>'); 291 } 292 } else { 293 $urlParameters = []; 294 if (!$last_approve['current']) { 295 $urlParameters['rev'] = $last_approve['rev']; 296 } 297 ptln('<a href="' . wl($INFO['id'], $urlParameters) . '">'); 298 ptln($this->getLang('newest_approved')); 299 ptln('</a>'); 300 } 301 302 //we are in current page 303 if ($rev == $last_change_date) { 304 305 //compare with the last approved page or 0 if there is no approved versions 306 $last_approved_rev = 0; 307 if (isset($last_approve['rev'])) { 308 $last_approved_rev = $last_approve['rev']; 309 } 310 311 if ($this->getConf('ready_for_approval') && 312 $helper->client_can_mark_ready_for_approval($INFO['id']) && 313 !$approve['ready_for_approval']) { 314 315 $urlParameters = [ 316 'rev' => $last_approved_rev, 317 'do' => 'diff', 318 'ready_for_approval' => 'ready_for_approval' 319 ]; 320 ptln(' | <a href="'.wl($INFO['id'], $urlParameters).'">'); 321 ptln($this->getLang('approve_ready')); 322 ptln('</a>'); 323 } 324 325 if ($helper->client_can_approve($INFO['id'], $approver)) { 326 327 $urlParameters = [ 328 'rev' => $last_approved_rev, 329 'do' => 'diff', 330 'approve' => 'approve' 331 ]; 332 ptln(' | <a href="'.wl($INFO['id'], $urlParameters).'">'); 333 ptln($this->getLang('approve')); 334 ptln('</a>'); 335 } 336 } 337 } 338 339 if ($approver && $this->getConf('banner_long')) { 340 ptln(' | ' . $this->getLang('approver') . ': ' . userlink($approver, true)); 341 } 342 343 ptln('</div>'); 344 } 345 346 /** 347 * @return bool|string|void 348 */ 349 protected function lastRevisionHasntApprovalData($id) { 350 351 try { 352 /** @var \helper_plugin_approve_db $db_helper */ 353 $db_helper = plugin_load('helper', 'approve_db'); 354 $sqlite = $db_helper->getDB(); 355 } catch (Exception $e) { 356 msg($e->getMessage(), -1); 357 return; 358 } 359 360 $res = $sqlite->query('SELECT rev FROM revision 361 WHERE page=? 362 AND current=1 363 AND approved IS NULL 364 AND ready_for_approval IS NULL', $id); 365 366 return $sqlite->res2single($res); 367 } 368 369 /** 370 * 371 * @param Doku_Event $event event object by reference 372 * @return void 373 */ 374 public function handle_pagesave_after(Doku_Event $event) { 375 try { 376 /** @var \helper_plugin_approve_db $db_helper */ 377 $db_helper = plugin_load('helper', 'approve_db'); 378 $sqlite = $db_helper->getDB(); 379 } catch (Exception $e) { 380 msg($e->getMessage(), -1); 381 return; 382 } 383 /** @var helper_plugin_approve $helper */ 384 $helper = plugin_load('helper', 'approve'); 385 386 //no content was changed 387 if (!$event->data['contentChanged']) return; 388 389 $changeType = $event->data['changeType']; 390 if ($changeType == DOKU_CHANGE_TYPE_REVERT) { 391 if ($event->data['oldContent'] == '') { 392 $changeType = DOKU_CHANGE_TYPE_CREATE; 393 } else { 394 $changeType = DOKU_CHANGE_TYPE_EDIT; 395 } 396 } 397 398 $id = $event->data['id']; 399 switch ($changeType) { 400 case DOKU_CHANGE_TYPE_EDIT: 401 case DOKU_CHANGE_TYPE_REVERT: 402 case DOKU_CHANGE_TYPE_MINOR_EDIT: 403 $last_change_date = $event->data['newRevision']; 404 405 //if the current page has approved or ready_for_approval -- keep it 406 $rev = $this->lastRevisionHasntApprovalData($id); 407 if ($rev) { 408 $sqlite->query('UPDATE revision SET rev=? WHERE page=? AND rev=?', 409 $last_change_date, $id, $rev); 410 411 } else { 412 //keep previous record 413 $sqlite->query('UPDATE revision SET current=0 414 WHERE page=? 415 AND current=1', $id); 416 417 $sqlite->storeEntry('revision', [ 418 'page' => $id, 419 'rev' => $last_change_date, 420 'current' => 1 421 ]); 422 } 423 break; 424 case DOKU_CHANGE_TYPE_DELETE: 425 //delete information about availability of a page but keep the history 426 $sqlite->query('DELETE FROM page WHERE page=?', $id); 427 428 //delete revision if no information about approvals 429 $rev = $this->lastRevisionHasntApprovalData($id); 430 if ($rev) { 431 $sqlite->query('DELETE FROM revision WHERE page=? AND rev=?', $id, $rev); 432 } else { 433 $sqlite->query('UPDATE revision SET current=0 WHERE page=? AND current=1', $id); 434 } 435 436 break; 437 case DOKU_CHANGE_TYPE_CREATE: 438 if ($helper->isPageAssigned($sqlite, $id, $newApprover)) { 439 $data = [ 440 'page' => $id, 441 'hidden' => $helper->in_hidden_namespace($sqlite, $id) ? '1' : '0' 442 ]; 443 if (!blank($newApprover)) { 444 $data['approver'] = $newApprover; 445 } 446 $sqlite->storeEntry('page', $data); 447 } 448 449 //store revision 450 $last_change_date = $event->data['newRevision']; 451 $sqlite->storeEntry('revision', [ 452 'page' => $id, 453 'rev' => $last_change_date, 454 'current' => 1 455 ]); 456 break; 457 } 458 } 459} 460