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