1<?php 2/** 3 * DokuWiki Plugin publish (Helper Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Jarrod Lowe <dokuwiki@rrod.net> 7 * @author Andreas Gohr <gohr@cosmocode.de> 8 */ 9 10// must be run within Dokuwiki 11if (!defined('DOKU_INC')) die(); 12 13class helper_plugin_publish extends DokuWiki_Plugin { 14 15 private $sortedApprovedRevisions = null; 16 17 /** 18 * checks if an id is within one of the namespaces in $namespace_list 19 * 20 * @param string $namespace_list 21 * @param string $id 22 * 23 * @return bool 24 */ 25 function in_namespace($namespace_list, $id) { 26 // PHP apparantly does not have closures - 27 // so we will parse $valid ourselves. Wasteful. 28 $namespace_list = preg_split('/\s+/', $namespace_list); 29 //if(count($valid) == 0) { return true; }//whole wiki matches 30 if((count($namespace_list)==1) and ($namespace_list[0]=="")) { return true; }//whole wiki matches 31 $id = trim($id, ':'); 32 $id = explode(':', $id); 33 34 // Check against all possible namespaces 35 foreach($namespace_list as $namespace) { 36 $namespace = explode(':', $namespace); 37 $current_ns_depth = 0; 38 $total_ns_depth = count($namespace); 39 $matching = true; 40 41 // Check each element, untill all elements of $v satisfied 42 while($current_ns_depth < $total_ns_depth) { 43 if($namespace[$current_ns_depth] != $id[$current_ns_depth]) { 44 // not a match 45 $matching = false; 46 break; 47 } 48 $current_ns_depth += 1; 49 } 50 if($matching) { return true; } // a match 51 } 52 return false; 53 } 54 55 // FIXME find out what this is supposed to do and how it can be done better 56 function in_sub_namespace($valid, $check) { 57 // is check a dir which contains any valid? 58 // PHP apparantly does not have closures - 59 // so we will parse $valid ourselves. Wasteful. 60 $valid = preg_split('/\s+/', $valid); 61 //if(count($valid) == 0) { return true; }//whole wiki matches 62 if((count($valid)==1) and ($valid[0]=="")) { return true; }//whole wiki matches 63 $check = trim($check, ':'); 64 $check = explode(':', $check); 65 66 // Check against all possible namespaces 67 foreach($valid as $v) { 68 $v = explode(':', $v); 69 $n = 0; 70 $c = count($check); //this is what is different from above! 71 $matching = 1; 72 73 // Check each element, untill all elements of $v satisfied 74 while($n < $c) { 75 if($v[$n] != $check[$n]) { 76 // not a match 77 $matching = 0; 78 break; 79 } 80 $n += 1; 81 } 82 if($matching == 1) { return true; } // a match 83 } 84 return false; 85 } 86 87 function canApprove() { 88 global $INFO; 89 global $ID; 90 91 if (!$this->in_namespace($this->getConf('apr_namespaces'), $ID)) { 92 return false; 93 } 94 95 return ($INFO['perm'] >= AUTH_DELETE); 96 } 97 98 function getRevision($id = null) { 99 global $REV; 100 if (isset($REV) && !empty($REV)) { 101 return $REV; 102 } 103 $meta = $this->getMeta($id); 104 if (isset($meta['last_change']['date'])) { 105 return $meta['last_change']['date']; 106 } 107 return $meta['date']['modified']; 108 } 109 110 function getApprovals($id = null) { 111 $meta = $this->getMeta($id); 112 if (!isset($meta['approval'])) { 113 return array(); 114 } 115 $approvals = $meta['approval']; 116 if (!is_array($approvals)) { 117 return array(); 118 } 119 return $approvals; 120 } 121 122 function getMeta($id = null) { 123 global $ID; 124 global $INFO; 125 126 if ($id === null) $id = $ID; 127 128 if($ID === $id && $INFO['meta']) { 129 $meta = $INFO['meta']; 130 } else { 131 $meta = p_get_metadata($id); 132 } 133 134 $this->checkApprovalFormat($meta, $id); 135 136 return $meta; 137 } 138 139 function checkApprovalFormat($meta, $id) { 140 if (isset($meta['approval_version']) && $meta['approval_version'] >= 2) { 141 return; 142 } 143 144 if (!$this->hasApprovals($meta)) { 145 return; 146 } 147 148 $approvals = $meta['approval']; 149 foreach (array_keys($approvals) as $approvedId) { 150 $keys = array_keys($approvals[$approvedId]); 151 152 if (is_array($approvals[$approvedId][$keys[0]])) { 153 continue; // current format 154 } 155 156 $newEntry = $approvals[$approvedId]; 157 if (count($newEntry) !== 3) { 158 //continue; // some messed up format... 159 } 160 $newEntry[] = intval($approvedId); // revision is the time of page edit 161 162 $approvals[$approvedId] = array(); 163 $approvals[$approvedId][$newEntry[0]] = $newEntry; 164 } 165 p_set_metadata($id, array('approval' => $approvals), true, true); 166 p_set_metadata($id, array('approval_version' => 2), true, true); 167 } 168 169 function hasApprovals($meta) { 170 return isset($meta['approval']) && !empty($meta['approval']); 171 } 172 173 function getApprovalsOnRevision($revision) { 174 $approvals = $this->getApprovals(); 175 176 if (isset($approvals[$revision])) { 177 return $approvals[$revision]; 178 } 179 return array(); 180 } 181 182 function getSortedApprovedRevisions($id = null) { 183 if ($id === null) { 184 global $ID; 185 $id = $ID; 186 } 187 188 static $sortedApprovedRevisions = array(); 189 if (!isset($sortedApprovedRevisions[$id])) { 190 $approvals = $this->getApprovals($id); 191 krsort($approvals); 192 $sortedApprovedRevisions[$id] = $approvals; 193 } 194 195 return $sortedApprovedRevisions[$id]; 196 } 197 198 function isRevisionApproved($revision, $id = null) { 199 $approvals = $this->getApprovals($id); 200 if (!isset($approvals[$revision])) { 201 return false; 202 } 203 return (count($approvals[$revision]) >= $this->getConf('number_of_approved')); 204 } 205 206 function isCurrentRevisionApproved($id = null) { 207 return $this->isRevisionApproved($this->getRevision($id), $id); 208 } 209 210 function getLatestApprovedRevision($id = null) { 211 $approvals = $this->getSortedApprovedRevisions($id); 212 foreach ($approvals as $revision => $ignored) { 213 if ($this->isRevisionApproved($revision, $id)) { 214 return $revision; 215 } 216 } 217 return 0; 218 } 219 220 function getLastestRevision() { 221 global $INFO; 222 return $INFO['meta']['date']['modified']; 223 } 224 225 function getApprovalDate() { 226 if (!$this->isCurrentRevisionApproved()) { 227 return -1; 228 } 229 230 $approvals = $this->getApprovalsOnRevision($this->getRevision()); 231 uasort($approvals, array(&$this, 'cmpApprovals')); 232 $keys = array_keys($approvals); 233 return $approvals[$keys[$this->getConf('number_of_approved') -1]][3]; 234 235 } 236 237 function cmpApprovals($left, $right) { 238 if ($left[3] == $right[3]) { 239 return 0; 240 } 241 return ($left[3] < $right[3]) ? -1 : 1; 242 } 243 244 function getApprovers() { 245 $approvers = $this->getApprovalsOnRevision($this->getRevision()); 246 if (count($approvers) === 0) { 247 return; 248 } 249 250 $result = array(); 251 foreach ($approvers as $approver) { 252 $result[] = editorinfo($this->getApproverName($approver)); 253 } 254 return $result; 255 } 256 257 function getApproverName($approver) { 258 if ($approver[1]) { 259 return $approver[1]; 260 } 261 if ($approver[2]) { 262 return $approver[2]; 263 } 264 return $approver[0]; 265 } 266 267 function getPreviousApprovedRevision() { 268 $currentRevision = $this->getRevision(); 269 $approvals = $this->getSortedApprovedRevisions(); 270 foreach ($approvals as $revision => $ignored) { 271 if ($revision >= $currentRevision) { 272 continue; 273 } 274 if ($this->isRevisionApproved($revision)) { 275 return $revision; 276 } 277 } 278 return 0; 279 } 280 281 function isHidden($id = null) { 282 if (!$this->getConf('hide drafts')) { 283 return false; 284 } 285 286 // needs to check if the actual namespace belongs to the apr_namespaces 287 if ($id == null) { 288 global $ID; 289 $id = $ID; 290 } 291 if (!$this->isActive($id)) { 292 return false; 293 } 294 295 if ($this->getLatestApprovedRevision($id)) { 296 return false; 297 } 298 return true; 299 } 300 301 function isHiddenForUser($id = null) { 302 if (!$this->isHidden($id)) { 303 return false; 304 } 305 306 if ($id == null) { 307 global $ID; 308 $id = $ID; 309 } 310 311 $allowedGroups = array_filter(explode(' ', trim($this->getConf('author groups')))); 312 if (empty($allowedGroups)) { 313 return auth_quickaclcheck($id) < AUTH_EDIT; 314 } 315 316 if (!$_SERVER['REMOTE_USER']) { 317 return true; 318 } 319 320 global $USERINFO; 321 foreach ($allowedGroups as $allowedGroup) { 322 $allowedGroup = trim($allowedGroup); 323 if (in_array($allowedGroup, $USERINFO['grps'])) { 324 return false; 325 } 326 } 327 return true; 328 } 329 330 function isActive($id = null) { 331 if ($id == null) { 332 global $ID; 333 $id = $ID; 334 } 335 if (!$this->in_namespace($this->getConf('apr_namespaces'), $id)) { 336 return false; 337 } 338 339 $no_apr_namespaces = $this->getConf('no_apr_namespaces'); 340 if (!empty($no_apr_namespaces)) { 341 if ($this->in_namespace($no_apr_namespaces, $id)) { 342 return false; 343 } 344 } 345 return true; 346 } 347 348 /** 349 * Create absolute diff-link between the two given revisions 350 * 351 * @param string $id 352 * @param int $rev1 353 * @param int $rev2 354 * @return string Diff-Link or empty string if $rev1 == $rev2 355 */ 356 public function getDifflink($id, $rev1, $rev2) { 357 if($rev1 == $rev2) { 358 return ''; 359 } 360 $params = 'do=diff,rev2[0]=' . $rev1 . ',rev2[1]=' . $rev2 . ',difftype=sidebyside'; 361 $difflink = wl($id, $params,true,'&'); 362 return $difflink; 363 } 364 365} 366