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