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