1<?php 2/** 3 * DokuWiki Plugin bez (Action Component) 4 * 5 */ 6 7// must be run within Dokuwiki 8 9if (!defined('DOKU_INC')) die(); 10 11/** 12 * Class action_plugin_bez_migration 13 * 14 * Handle migrations that need more than just SQL 15 */ 16class action_plugin_approve_migration extends DokuWiki_Action_Plugin 17{ 18 /** 19 * @inheritDoc 20 */ 21 public function register(Doku_Event_Handler $controller) 22 { 23 $controller->register_hook('PLUGIN_SQLITE_DATABASE_UPGRADE', 'AFTER', $this, 'handle_migrations'); 24 } 25 26 /** 27 * Call our custom migrations when defined 28 * 29 * @param Doku_Event $event 30 * @param $param 31 */ 32 public function handle_migrations(Doku_Event $event, $param) 33 { 34 if ($event->data['sqlite']->getAdapter()->getDbname() !== 'approve') { 35 return; 36 } 37 $to = $event->data['to']; 38 39 if (is_callable([$this, "migration$to"])) { 40 $event->result = call_user_func([$this, "migration$to"], $event->data); 41 } 42 } 43 44 /** 45 * Convenience function to run an INSERT ... ON CONFLICT IGNORE operation 46 * 47 * The function takes a key-value array with the column names in the key and the actual value in the value, 48 * build the appropriate query and executes it. 49 * 50 * @param string $table the table the entry should be saved to (will not be escaped) 51 * @param array $entry A simple key-value pair array (only values will be escaped) 52 * @return bool|SQLiteResult 53 */ 54 protected function insertOrIgnore(helper_plugin_sqlite $sqlite, $table, $entry) { 55 $keys = join(',', array_keys($entry)); 56 $vals = join(',', array_fill(0,count($entry),'?')); 57 58 $sql = "INSERT OR IGNORE INTO $table ($keys) VALUES ($vals)"; 59 return $sqlite->query($sql, array_values($entry)); 60 } 61 62 protected function migration1($data) 63 { 64 global $conf; 65 66 /** @var helper_plugin_sqlite $sqlite */ 67 $sqlite = $data['sqlite']; 68 $db = $sqlite->getAdapter()->getDb(); 69 70 71 $datadir = $conf['datadir']; 72 if (substr($datadir, -1) != '/') { 73 $datadir .= '/'; 74 } 75 76 $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir)); 77 $pages = []; 78 foreach ($rii as $file) { 79 if ($file->isDir()){ 80 continue; 81 } 82 83 //remove start path and extension 84 $page = substr($file->getPathname(), strlen($datadir), -4); 85 $pages[] = str_replace('/', ':', $page); 86 $pages[] = str_replace('\\', ':', $page); 87 } 88 89 $db->beginTransaction(); 90 91 $apr_namespaces = preg_split('/\s+/', $this->getConf('apr_namespaces', ''), 92 -1,PREG_SPLIT_NO_EMPTY); 93 94 if (!$apr_namespaces) { 95 $sqlite->storeEntry('maintainer',[ 96 'namespace' => '**' 97 ]); 98 } else { 99 foreach ($apr_namespaces as $namespace) { 100 $namespace = rtrim($namespace, ':'); 101 $namespace .= ':**'; 102 $sqlite->storeEntry('maintainer',[ 103 'namespace' => $namespace 104 ]); 105 } 106 } 107 108 //store config 109 $no_apr_namespaces = $this->getConf('no_apr_namespaces', ''); 110 $sqlite->storeEntry('config',[ 111 'key' => 'no_apr_namespaces', 112 'value' => $no_apr_namespaces 113 ]); 114 115 $no_apr_namespaces_list = preg_split('/\s+/', $no_apr_namespaces,-1, 116 PREG_SPLIT_NO_EMPTY); 117 $no_apr_namespaces_list = array_map(function ($namespace) { 118 return trim($namespace, ':'); 119 }, $no_apr_namespaces_list); 120 121 122 foreach ($pages as $page) { 123 //import historic data 124 $versions = p_get_metadata($page, 'plugin_approve_versions'); 125 if (!$versions) { 126 $versions = $this->render_metadata_for_approved_page($page); 127 } 128 129// $last_change_date = p_get_metadata($page, 'last_change date'); 130 $last_change_date = @filemtime(wikiFN($page)); 131 $last_version = $versions[0]; 132 133 //remove current versions to not process it here 134 unset($versions[0]); 135 unset($versions[$last_change_date]); 136 137 $revision_editors = $this->revision_editors($page); 138 foreach ($versions as $rev => $version) { 139 $data = [ 140 'page' => $page, 141 'rev' => $rev, 142 'approved' => date('c', $rev), 143 'approved_by' => $revision_editors[$rev], 144 'version' => $version 145 ]; 146 $sqlite->storeEntry('revision', $data); 147 } 148 149 //process current data 150 $summary = p_get_metadata($page, 'last_change sum'); 151 $user = p_get_metadata($page, 'last_change user'); 152 $data = [ 153 'page' => $page, 154 'rev' => $last_change_date, 155 'current' => 1 156 ]; 157 if ($this->getConf('ready_for_approval') && 158 $summary == $this->getConf('sum ready for approval')) { 159 $data['ready_for_approval'] = date('c', $last_change_date); 160 $data['ready_for_approval_by'] = $user; 161 } elseif($summary == $this->getConf('sum approved')) { 162 $data['approved'] = date('c', $last_change_date); 163 $data['approved_by'] = $user; 164 $data['version'] = $last_version; 165 } 166 $sqlite->storeEntry('revision', $data); 167 168 169 //empty apr_namespaces - all match 170 if (!$apr_namespaces) { 171 $in_apr_namespace = true; 172 } else { 173 $in_apr_namespace = false; 174 foreach ($apr_namespaces as $namespace) { 175 if (substr($page, 0, strlen($namespace)) == $namespace) { 176 $in_apr_namespace = true; 177 break; 178 } 179 } 180 } 181 182 if ($in_apr_namespace) { 183 $hidden = '0'; 184 foreach ($no_apr_namespaces_list as $namespace) { 185 if (substr($page, 0, strlen($namespace)) == $namespace) { 186 $hidden = '1'; 187 break; 188 } 189 } 190 $sqlite->storeEntry('page', [ 191 'page' => $page, 192 'hidden' => $hidden 193 ]); 194 } 195 } 196 197 198 $db->commit(); 199 200 return true; 201 } 202 203 /** 204 * Calculate current version 205 * 206 * @param $id 207 * @return array 208 */ 209 protected function render_metadata_for_approved_page($id, $currev=false) { 210 if (!$currev) $currev = @filemtime(wikiFN($id)); 211 212 $version = $this->approved($id); 213 //version for current page 214 $curver = $version + 1; 215 $versions = array(0 => $curver, $currev => $curver); 216 217 $changelog = new PageChangeLog($id); 218 $first = 0; 219 $num = 100; 220 while (count($revs = $changelog->getRevisions($first, $num)) > 0) { 221 foreach ($revs as $rev) { 222 $revInfo = $changelog->getRevisionInfo($rev); 223 if ($revInfo['sum'] == $this->getConf('sum approved')) { 224 $versions[$rev] = $version; 225 $version -= 1; 226 } 227 } 228 $first += $num; 229 } 230 231// p_set_metadata($id, array(ApproveConst::METADATA_VERSIONS_KEY => $versions)); 232 233 return $versions; 234 } 235 236 /** 237 * Get the number of approved pages 238 * @param $id 239 * @return int 240 */ 241 protected function approved($id) { 242 $count = 0; 243 244 $changelog = new PageChangeLog($id); 245 $first = 0; 246 $num = 100; 247 while (count($revs = $changelog->getRevisions($first, $num)) > 0) { 248 foreach ($revs as $rev) { 249 $revInfo = $changelog->getRevisionInfo($rev); 250 if ($revInfo['sum'] == $this->getConf('sum approved')) { 251 $count += 1; 252 } 253 } 254 $first += $num; 255 } 256 257 return $count; 258 } 259 260 /** 261 * Calculate current version 262 * 263 * @param $id 264 * @return array 265 */ 266 protected function revision_editors($id) 267 { 268 $currev = @filemtime(wikiFN($id)); 269 $user = p_get_metadata($id, 'last_change user'); 270 271 $revision_editors = array($currev => $user); 272 273 $changelog = new PageChangeLog($id); 274 $first = 0; 275 $num = 100; 276 while (count($revs = $changelog->getRevisions($first, $num)) > 0) { 277 foreach ($revs as $rev) { 278 $revInfo = $changelog->getRevisionInfo($rev); 279 $revision_editors[$rev] = $revInfo['user']; 280 } 281 $first += $num; 282 } 283 284 return $revision_editors; 285 } 286} 287