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($valid, $check) { 19 // PHP apparantly does not have closures - 20 // so we will parse $valid ourselves. Wasteful. 21 $valid = preg_split('/\s+/', $valid); 22 //if(count($valid) == 0) { return true; }//whole wiki matches 23 if((count($valid)==1) and ($valid[0]=="")) { return true; }//whole wiki matches 24 $check = trim($check, ':'); 25 $check = explode(':', $check); 26 27 // Check against all possible namespaces 28 foreach($valid as $v) { 29 $v = explode(':', $v); 30 $n = 0; 31 $c = count($v); 32 $matching = 1; 33 34 // Check each element, untill all elements of $v satisfied 35 while($n < $c) { 36 if($v[$n] != $check[$n]) { 37 // not a match 38 $matching = 0; 39 break; 40 } 41 $n += 1; 42 } 43 if($matching == 1) { 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 if ($id === null || $ID === $id) { 118 global $INFO; 119 $meta = $INFO['meta']; 120 $id = $ID; 121 } else { 122 $meta = p_get_metadata($id); 123 } 124 125 $this->checkApprovalFormat($meta, $id); 126 127 return $meta; 128 } 129 130 function checkApprovalFormat($meta, $id) { 131 if (isset($meta['approval_version']) && $meta['approval_version'] >= 2) { 132 return; 133 } 134 135 if (!$this->hasApprovals($meta)) { 136 return; 137 } 138 139 $approvals = $meta['approval']; 140 foreach (array_keys($approvals) as $approvedId) { 141 $keys = array_keys($approvals[$approvedId]); 142 143 if (is_array($approvals[$approvedId][$keys[0]])) { 144 continue; // current format 145 } 146 147 $newEntry = $approvals[$approvedId]; 148 if (count($newEntry) !== 3) { 149 //continue; // some messed up format... 150 } 151 $newEntry[] = intval($approvedId); // revision is the time of page edit 152 153 $approvals[$approvedId] = array(); 154 $approvals[$approvedId][$newEntry[0]] = $newEntry; 155 } 156 p_set_metadata($id, array('approval' => $approvals), true, true); 157 p_set_metadata($id, array('approval_version' => 2), true, true); 158 } 159 160 function hasApprovals($meta) { 161 return isset($meta['approval']) && !empty($meta['approval']); 162 } 163 164 function getApprovalsOnRevision($revision) { 165 $approvals = $this->getApprovals(); 166 167 if (isset($approvals[$revision])) { 168 return $approvals[$revision]; 169 } 170 return array(); 171 } 172 173 function getSortedApprovedRevisions($id = null) { 174 if ($id === null) { 175 global $ID; 176 $id = $ID; 177 } 178 179 static $sortedApprovedRevisions = array(); 180 if (!isset($sortedApprovedRevisions[$id])) { 181 $approvals = $this->getApprovals($id); 182 krsort($approvals); 183 $sortedApprovedRevisions[$id] = $approvals; 184 } 185 186 return $sortedApprovedRevisions[$id]; 187 } 188 189 function isRevisionApproved($revision, $id = null) { 190 $approvals = $this->getApprovals($id); 191 if (!isset($approvals[$revision])) { 192 return false; 193 } 194 return (count($approvals[$revision]) >= $this->getConf('number_of_approved')); 195 } 196 197 function isCurrentRevisionApproved($id = null) { 198 return $this->isRevisionApproved($this->getRevision($id), $id); 199 } 200 201 function getLatestApprovedRevision($id = null) { 202 $approvals = $this->getSortedApprovedRevisions($id); 203 foreach ($approvals as $revision => $ignored) { 204 if ($this->isRevisionApproved($revision, $id)) { 205 return $revision; 206 } 207 } 208 return 0; 209 } 210 211 function getLastestRevision() { 212 global $INFO; 213 return $INFO['meta']['date']['modified']; 214 } 215 216 function getApprovalDate() { 217 if (!$this->isCurrentRevisionApproved()) { 218 return -1; 219 } 220 221 $approvals = $this->getApprovalsOnRevision($this->getRevision()); 222 uasort($approvals, array(&$this, 'cmpApprovals')); 223 $keys = array_keys($approvals); 224 return $approvals[$keys[$this->getConf('number_of_approved') -1]][3]; 225 226 } 227 228 function cmpApprovals($left, $right) { 229 if ($left[3] == $right[3]) { 230 return 0; 231 } 232 return ($left[3] < $right[3]) ? -1 : 1; 233 } 234 235 function getApprovers() { 236 $approvers = $this->getApprovalsOnRevision($this->getRevision()); 237 if (count($approvers) === 0) { 238 return; 239 } 240 241 $result = array(); 242 foreach ($approvers as $approver) { 243 $result[] = editorinfo($this->getApproverName($approver)); 244 } 245 return $result; 246 } 247 248 function getApproverName($approver) { 249 if ($approver[1]) { 250 return $approver[1]; 251 } 252 if ($approver[2]) { 253 return $approver[2]; 254 } 255 return $approver[0]; 256 } 257 258 function getPreviousApprovedRevision() { 259 $currentRevision = $this->getRevision(); 260 $approvals = $this->getSortedApprovedRevisions(); 261 foreach ($approvals as $revision => $ignored) { 262 if ($revision >= $currentRevision) { 263 continue; 264 } 265 if ($this->isRevisionApproved($revision)) { 266 return $revision; 267 } 268 } 269 return 0; 270 } 271 272 function isHidden($id = null) { 273 if (!$this->getConf('hide drafts')) { 274 return false; 275 } 276 277 // needs to check if the actual namespace belongs to the apr_namespaces 278 if ($id == null) { 279 global $ID; 280 $id = $ID; 281 } 282 if (!$this->isActive($id)) { 283 return false; 284 } 285 286 if ($this->getLatestApprovedRevision($id)) { 287 return false; 288 } 289 return true; 290 } 291 292 function isHiddenForUser($id = null) { 293 if (!$this->isHidden($id)) { 294 return false; 295 } 296 297 if ($id == null) { 298 global $ID; 299 $id = $ID; 300 } 301 302 $allowedGroups = array_filter(explode(' ', trim($this->getConf('author groups')))); 303 if (empty($allowedGroups)) { 304 return auth_quickaclcheck($id) < AUTH_EDIT; 305 } 306 307 if (!$_SERVER['REMOTE_USER']) { 308 return true; 309 } 310 311 global $USERINFO; 312 foreach ($allowedGroups as $allowedGroup) { 313 $allowedGroup = trim($allowedGroup); 314 if (in_array($allowedGroup, $USERINFO['grps'])) { 315 return false; 316 } 317 } 318 return true; 319 } 320 321 function isActive($id = null) { 322 if ($id == null) { 323 global $ID; 324 $id = $ID; 325 } 326 if (!$this->in_namespace($this->getConf('apr_namespaces'), $id)) { 327 return false; 328 } 329 330 $no_apr_namespaces = $this->getConf('no_apr_namespaces'); 331 if (!empty($no_apr_namespaces)) { 332 if ($this->in_namespace($no_apr_namespaces, $id)) { 333 return false; 334 } 335 } 336 return true; 337 } 338} 339