1<?php 2 3// must be run within Dokuwiki 4if (!defined('DOKU_INC')) die(); 5class helper_plugin_approve extends DokuWiki_Plugin { 6 7 /** 8 * @param helper_plugin_sqlite $sqlite 9 * @return string 10 */ 11 public function no_apr_namespace(helper_plugin_sqlite $sqlite) { 12 //check for config update 13 $key = 'no_apr_namespaces'; 14 $res = $sqlite->query('SELECT value FROM config WHERE key=?', $key); 15 $no_apr_namespaces_db = $sqlite->res2single($res); 16 $no_apr_namespaces_conf = $this->getConf($key); 17 //update internal config 18 if ($no_apr_namespaces_db != $no_apr_namespaces_conf) { 19 $sqlite->query('UPDATE config SET value=? WHERE key=?', $no_apr_namespaces_conf, $key); 20 21 $res = $sqlite->query('SELECT page, hidden FROM page'); 22 $pages = $sqlite->res2arr($res); 23 foreach ($pages as $page) { 24 $id = $page['page']; 25 $hidden = $page['hidden']; 26 $in_hidden_namespace = $this->in_hidden_namespace($sqlite, $id, $no_apr_namespaces_conf); 27 $new_hidden = $in_hidden_namespace ? '1' : '0'; 28 29 if ($hidden != $new_hidden) { 30 $sqlite->query('UPDATE page SET hidden=? WHERE page=?', $new_hidden, $id); 31 } 32 } 33 } 34 35 return $no_apr_namespaces_conf; 36 } 37 38 /** 39 * @param helper_plugin_sqlite $sqlite 40 * @param $id 41 * @param null $approver 42 * @return bool 43 */ 44 public function use_approve_here(helper_plugin_sqlite $sqlite, $id, &$approver=null) { 45 46 //check if we should update no_apr_namespace 47 $this->no_apr_namespace($sqlite); 48 49 $res = $sqlite->query('SELECT page, approver FROM page WHERE page=? AND hidden=0', $id); 50 $row = $sqlite->res2row($res); 51 $approver = $row['approver']; 52 if ($row) { 53 return true; 54 } 55 return false; 56 } 57 58 /** 59 * @param helper_plugin_sqlite $sqlite 60 * @param $id 61 * @return bool|string 62 */ 63 public function find_last_approved(helper_plugin_sqlite $sqlite, $id) { 64 $res = $sqlite->query('SELECT rev FROM revision 65 WHERE page=? AND approved IS NOT NULL 66 ORDER BY rev DESC LIMIT 1', $id); 67 return $sqlite->res2single($res); 68 } 69 70 /** 71 * @param helper_plugin_sqlite $sqlite 72 * @param null $no_apr_namespaces 73 * @return array|array[]|false|string[] 74 */ 75 public function get_hidden_namespaces_list(helper_plugin_sqlite $sqlite, $no_apr_namespaces=null) { 76 if (!$no_apr_namespaces) { 77 $no_apr_namespaces = $this->no_apr_namespace($sqlite); 78 } 79 80 $no_apr_namespaces_list = preg_split('/\s+/', $no_apr_namespaces,-1, 81 PREG_SPLIT_NO_EMPTY); 82 $no_apr_namespaces_list = array_map(function ($namespace) { 83 return ltrim($namespace, ':'); 84 }, $no_apr_namespaces_list); 85 86 return $no_apr_namespaces_list; 87 } 88 89 /** 90 * @param helper_plugin_sqlite $sqlite 91 * @param $id 92 * @param null $no_apr_namespaces 93 * @return bool|string 94 */ 95 public function in_hidden_namespace(helper_plugin_sqlite $sqlite, $id, $no_apr_namespaces=null) { 96 $no_apr_namespaces_list = $this->get_hidden_namespaces_list($sqlite, $no_apr_namespaces); 97 $id = ltrim($id, ':'); 98 foreach ($no_apr_namespaces_list as $namespace) { 99 if (substr($id, 0, strlen($namespace)) == $namespace) { 100 return true; 101 } 102 } 103 return false; 104 } 105 106 /** 107 * @param helper_plugin_sqlite $sqlite 108 * @return array 109 */ 110 public function weighted_assignments(helper_plugin_sqlite $sqlite) { 111 $res = $sqlite->query('SELECT id,namespace,approver FROM maintainer'); 112 $assignments = $sqlite->res2arr($res); 113 114 $weighted_assignments = []; 115 foreach ($assignments as $assignment) { 116 $ns = $assignment['namespace']; 117 //more general namespaces are overridden by more specific ones. 118 if (substr($ns, -1) == '*') { 119 $weight = substr_count($ns, ':'); 120 } else { 121 $weight = PHP_INT_MAX; 122 } 123 124 $assignment['weight'] = $weight; 125 $weighted_assignments[] = $assignment; 126 } 127 array_multisort(array_column($weighted_assignments, 'weight'), $weighted_assignments); 128 129 return $weighted_assignments; 130 } 131 132 /** 133 * @param helper_plugin_sqlite $sqlite 134 * @param $id 135 * @param null $pageApprover 136 * @param null $weighted_assignments 137 * @return bool 138 */ 139 public function isPageAssigned(helper_plugin_sqlite $sqlite, $id, &$pageApprover=null, $weighted_assignments=null) { 140 if (!$weighted_assignments) { 141 $weighted_assignments = $this->weighted_assignments($sqlite); 142 } 143 foreach ($weighted_assignments as $assignment) { 144 $ns = ltrim($assignment['namespace'], ':'); 145 $approver = $assignment['approver']; 146 if (substr($ns, -2) == '**') { 147 //remove '**' 148 $ns = substr($ns, 0, -2); 149 if (substr($id, 0, strlen($ns)) == $ns) { 150 $newAssignment = true; 151 $pageApprover = $approver; 152 } 153 } elseif (substr($ns, -1) == '*') { 154 //remove '*' 155 $ns = substr($ns, 0, -1); 156 $noNS = substr($id, strlen($ns)); 157 if (strpos($noNS, ':') === FALSE && 158 substr($id, 0, strlen($ns)) == $ns) { 159 $newAssignment = true; 160 $pageApprover = $approver; 161 } 162 } elseif($id == $ns) { 163 $newAssignment = true; 164 $pageApprover = $approver; 165 } 166 } 167 return $newAssignment; 168 } 169 170 /** 171 * @param helper_plugin_sqlite $sqlite 172 */ 173 public function updatePagesAssignments(helper_plugin_sqlite $sqlite) 174 { 175 //clean current settings 176 $sqlite->query('DELETE FROM page'); 177 178 $wikiPages = $this->getPages(); 179 $no_apr_namespace = $this->no_apr_namespace($sqlite); 180 $weighted_assignments = $this->weighted_assignments($sqlite); 181 foreach ($wikiPages as $id) { 182 if ($this->isPageAssigned($sqlite, $id, $approver, $weighted_assignments)) { 183 $data = [ 184 'page' => $id, 185 'hidden' => $this->in_hidden_namespace($sqlite, $id, $no_apr_namespace) ? '1' : '0' 186 ]; 187 if (!blank($approver)) { 188 $data['approver'] = $approver; 189 } 190 $sqlite->storeEntry('page', $data); 191 } 192 } 193 } 194 195 /** 196 * @param string $approver 197 * @return bool 198 */ 199 public function isGroup($approver) { 200 if (!$approver) return false; 201 if (strncmp($approver, "@", 1) === 0) return true; 202 return false; 203 } 204 205 /** 206 * @param $userinfo 207 * @param string $group 208 * @return bool 209 */ 210 public function isInGroup($userinfo, $group) { 211 $groupname = substr($group, 1); 212 if (in_array($groupname, $userinfo['grps'])) return true; 213 return false; 214 } 215 216 /** 217 * @param $id 218 * @param string $pageApprover 219 * @return bool 220 */ 221 public function client_can_approve($id, $pageApprover) { 222 global $INFO; 223 //user not log in 224 if (!isset($INFO['userinfo'])) return false; 225 226 if ($pageApprover == $INFO['client']) { 227 return true; 228 } elseif ($this->isGroup($pageApprover) && $this->isInGroup($INFO['userinfo'], $pageApprover)) { 229 return true; 230 //no approver provided, check if approve plugin apply here 231 } elseif (auth_quickaclcheck($id) >= AUTH_DELETE && 232 (!$pageApprover || !$this->getConf('strict_approver'))) { 233 return true; 234 } 235 236 return false; 237 } 238 239 /** 240 * @param $id 241 * @return bool 242 */ 243 public function client_can_mark_ready_for_approval($id) { 244 return auth_quickaclcheck($id) >= AUTH_EDIT; 245 } 246 247 /** 248 * @param $id 249 * @return bool 250 */ 251 public function client_can_see_drafts($id, $pageApprover) { 252 if (!$this->getConf('hide_drafts_for_viewers')) return true; 253 254 if (auth_quickaclcheck($id) >= AUTH_EDIT) return true; 255 if ($this->client_can_approve($id, $pageApprover)) return true; 256 257 return false; 258 } 259 260 /** 261 * Get the array of all pages ids in wiki 262 * 263 * @return array 264 */ 265 public function getPages() { 266 global $conf; 267 268 $datadir = realpath($conf['datadir']); // path without ending "/" 269 $directory = new RecursiveDirectoryIterator($datadir, FilesystemIterator::SKIP_DOTS); 270 $iterator = new RecursiveIteratorIterator($directory); 271 272 $pages = []; 273 /** @var SplFileInfo $fileinfo */ 274 foreach ($iterator as $fileinfo) { 275 if (!$fileinfo->isFile()) continue; 276 277 $path = $fileinfo->getRealPath(); // it should return "/" both on windows and linux 278 //remove dir part 279 $path = substr($path, strlen($datadir)); 280 //make file a dokuwiki path 281 $id = $this->pathID($path); 282 $pages[] = $id; 283 } 284 285 return $pages; 286 } 287 288 /** 289 * translates a document path to an ID 290 * 291 * fixes dokuwiki pathID - support for Windows enviroment 292 * 293 * @param string $path 294 * @param bool $keeptxt 295 * 296 * @return mixed|string 297 */ 298 public function pathID($path,$keeptxt=false){ 299 $id = utf8_decodeFN($path); 300 $id = str_replace(DIRECTORY_SEPARATOR,':',$id); 301 if(!$keeptxt) $id = preg_replace('#\.txt$#','',$id); 302 $id = trim($id, ':'); 303 return $id; 304 } 305} 306