| [ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Plugin management functions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Christopher Smith <chris@jalakai.co.uk> 7 */ 8 // must be run within Dokuwiki 9 if(!defined('DOKU_INC')) die(); 10 11 // todo 12 // - maintain a history of file modified 13 // - allow a plugin to contain extras to be copied to the current template (extra/tpl/) 14 // - to images (lib/images/) [ not needed, should go in lib/plugin/images/ ] 15 16 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 17 require_once (DOKU_PLUGIN.'admin.php'); 18 19 //--------------------------[ GLOBALS ]------------------------------------------------ 20 // note: probably should be dokuwiki wide globals, where they can be accessed by pluginutils.php 21 // global $plugin_types; 22 // $plugin_types = array('syntax', 'admin'); 23 24 // plugins that are an integral part of dokuwiki, they shouldn't be disabled or deleted 25 global $plugin_protected; 26 $plugin_protected = array('acl','plugin','config','info','usermanager','revert'); 27 28 /** 29 * All DokuWiki plugins to extend the admin function 30 * need to inherit from this class 31 */ 32 class admin_plugin_plugin extends DokuWiki_Admin_Plugin { 33 34 var $disabled = 0; 35 var $plugin = ''; 36 var $cmd = ''; 37 var $handler = NULL; 38 39 var $functions = array('delete','update',/*'settings',*/'info'); // require a plugin name 40 var $commands = array('manage','download','enable'); // don't require a plugin name 41 var $plugin_list = array(); 42 43 var $msg = ''; 44 var $error = ''; 45 46 function admin_plugin_plugin() { 47 global $conf; 48 $this->disabled = (isset($conf['pluginmanager']) && ($conf['pluginmanager'] == 0)); 49 } 50 51 /** 52 * return some info 53 */ 54 function getInfo(){ 55 $disabled = ($this->disabled) ? '(disabled)' : ''; 56 57 return array( 58 'author' => 'Christopher Smith', 59 'email' => 'chris@jalakai.co.uk', 60 'date' => '2005-08-10', 61 'name' => 'Plugin Manager', 62 'desc' => "Manage Plugins, including automated plugin installer $disabled", 63 'url' => 'http://dokuwiki.org/plugin:plugin', 64 ); 65 } 66 67 /** 68 * return prompt for admin menu 69 */ 70 function getMenuText($language) { 71 72 if (!$this->disabled) 73 return parent::getMenuText($language); 74 75 return ''; 76 } 77 78 /** 79 * return sort order for position in admin menu 80 */ 81 function getMenuSort() { 82 return 20; 83 } 84 85 /** 86 * handle user request 87 */ 88 function handle() { 89 90 if ($this->disabled) return; 91 92 // enable direct access to language strings 93 $this->setupLocale(); 94 95 // $this->plugin = $_REQUEST['plugin']; 96 // $this->cmd = $_REQUEST['fn']; 97 // if (is_array($this->cmd)) $this->cmd = key($this->cmd); 98 99 $fn = $_REQUEST['fn']; 100 if (is_array($fn)) { 101 $this->cmd = key($fn); 102 $this->plugin = is_array($fn[$this->cmd]) ? key($fn[$this->cmd]) : null; 103 } else { 104 $this->cmd = $fn; 105 $this->plugin = null; 106 } 107 108 $this->plugin_list = plugin_list('', true); 109 sort($this->plugin_list); 110 111 // verify $_REQUEST vars 112 if (in_array($this->cmd, $this->commands)) { 113 $this->plugin = ''; 114 } else if (!in_array($this->cmd, $this->functions) || !in_array($this->plugin, $this->plugin_list)) { 115 $this->cmd = 'manage'; 116 $this->plugin = ''; 117 } 118 119 if(($this->cmd != 'manage' || $this->plugin != '') && !checkSecurityToken()){ 120 $this->cmd = 'manage'; 121 $this->plugin = ''; 122 } 123 124 // create object to handle the command 125 $class = "ap_".$this->cmd; 126 if (!class_exists($class)) $class = 'ap_manage'; 127 128 $this->handler = & new $class($this, $this->plugin); 129 $this->msg = $this->handler->process(); 130 } 131 132 /** 133 * output appropriate html 134 */ 135 function html() { 136 137 if ($this->disabled) return; 138 139 // enable direct access to language strings 140 $this->setupLocale(); 141 142 if ($this->handler === NULL) $this->handler = & new ap_manage($this, $this->plugin); 143 if (!$this->plugin_list) { 144 $this->plugin_list = plugin_list('',true); 145 sort($this->plugin_list); 146 } 147 148 ptln('<div id="plugin__manager">'); 149 $this->handler->html(); 150 ptln('</div><!-- #plugin_manager -->'); 151 } 152 153 } 154 155 class ap_manage { 156 157 var $manager = NULL; 158 var $lang = array(); 159 var $plugin = ''; 160 var $downloaded = array(); 161 162 function ap_manage(&$manager, $plugin) { 163 $this->manager = & $manager; 164 $this->plugin = $plugin; 165 $this->lang = & $manager->lang; 166 } 167 168 function process() { 169 return ''; 170 } 171 172 function html() { 173 print $this->manager->locale_xhtml('admin_plugin'); 174 $this->html_menu(); 175 } 176 177 // build our standard menu 178 function html_menu($listPlugins = true) { 179 global $ID; 180 181 ptln('<div class="pm_menu">'); 182 183 ptln('<div class="common">'); 184 ptln(' <h2>'.$this->lang['download'].'</h2>'); 185 ptln(' <form action="'.wl($ID).'" method="post">'); 186 ptln(' <fieldset class="hidden">',4); 187 ptln(' <input type="hidden" name="do" value="admin" />'); 188 ptln(' <input type="hidden" name="page" value="plugin" />'); 189 formSecurityToken(); 190 ptln(' </fieldset>'); 191 ptln(' <fieldset>'); 192 ptln(' <legend>'.$this->lang['download'].'</legend>'); 193 ptln(' <label for="dw__url">'.$this->lang['url'].'<input name="url" id="dw__url" class="edit" type="text" maxlength="200" /></label>'); 194 ptln(' <input type="submit" class="button" name="fn[download]" value="'.$this->lang['btn_download'].'" />'); 195 ptln(' </fieldset>'); 196 ptln(' </form>'); 197 ptln('</div>'); 198 199 if ($listPlugins) { 200 ptln('<h2>'.$this->lang['manage'].'</h2>'); 201 202 ptln('<form action="'.wl($ID).'" method="post" class="plugins">'); 203 // ptln(' <div class="plugins">'); 204 205 ptln(' <fieldset class="hidden">'); 206 ptln(' <input type="hidden" name="do" value="admin" />'); 207 ptln(' <input type="hidden" name="page" value="plugin" />'); 208 formSecurityToken(); 209 ptln(' </fieldset>'); 210 211 $this->html_pluginlist(); 212 213 ptln(' <fieldset class="buttons">'); 214 ptln(' <input type="submit" class="button" name="fn[enable]" value="'.$this->lang['btn_enable'].'" />'); 215 ptln(' </fieldset>'); 216 217 // ptln(' </div>'); 218 ptln('</form>'); 219 } 220 221 ptln('</div>'); 222 } 223 224 function html_pluginlist() { 225 global $ID; 226 global $plugin_protected; 227 228 foreach ($this->manager->plugin_list as $plugin) { 229 230 $disabled = plugin_isdisabled($plugin); 231 $protected = in_array($plugin,$plugin_protected); 232 233 $checked = ($disabled) ? '' : ' checked="checked"'; 234 $check_disabled = ($protected) ? ' disabled="disabled"' : ''; 235 236 // determine display class(es) 237 $class = array(); 238 if (in_array($plugin, $this->downloaded)) $class[] = 'new'; 239 if ($disabled) $class[] = 'disabled'; 240 if ($protected) $class[] = 'protected'; 241 242 $class = count($class) ? ' class="'.join(' ', $class).'"' : ''; 243 244 ptln(' <fieldset'.$class.'>'); 245 ptln(' <legend>'.$plugin.'</legend>'); 246 ptln(' <input type="checkbox" class="enable" name="enabled[]" value="'.$plugin.'"'.$checked.$check_disabled.' />'); 247 ptln(' <h3 class="legend">'.$plugin.'</h3>'); 248 249 $this->html_button($plugin, 'info', false, 6); 250 if (in_array('settings', $this->manager->functions)) { 251 $this->html_button($plugin, 'settings', !@file_exists(DOKU_PLUGIN.$plugin.'/settings.php'), 6); 252 } 253 $this->html_button($plugin, 'update', !$this->plugin_readlog($plugin, 'url'), 6); 254 $this->html_button($plugin, 'delete', $protected, 6); 255 256 ptln(' </fieldset>'); 257 } 258 } 259 260 function html_button($plugin, $btn, $disabled=false, $indent=0) { 261 $disabled = ($disabled) ? 'disabled="disabled"' : ''; 262 ptln('<input type="submit" class="button" '.$disabled.' name="fn['.$btn.']['.$plugin.']" value="'.$this->lang['btn_'.$btn].'" />',$indent); 263 } 264 265 /** 266 * Refresh plugin list 267 */ 268 function refresh() { 269 270 $this->manager->plugin_list = plugin_list('',true); 271 sort($this->manager->plugin_list); 272 273 // expire dokuwiki caches 274 // touching local.php expires wiki page, JS and CSS caches 275 @touch(DOKU_CONF.'local.php'); 276 277 // update latest plugin date - FIXME 278 return (!$this->manager->error); 279 } 280 281 function download($url, $overwrite=false) { 282 global $lang; 283 284 // check the url 285 $matches = array(); 286 if (!preg_match("/[^\/]*$/", $url, $matches) || !$matches[0]) { 287 $this->manager->error = $this->lang['error_badurl']."\n"; 288 return false; 289 } 290 291 $file = $matches[0]; 292 293 if (!($tmp = io_mktmpdir())) { 294 $this->manager->error = $this->lang['error_dircreate']."\n"; 295 return false; 296 } 297 298 if (!$file = io_download($url, "$tmp/", true, $file)) { 299 $this->manager->error = sprintf($this->lang['error_download'],$url)."\n"; 300 } 301 302 if (!$this->manager->error && !ap_decompress("$tmp/$file", $tmp)) { 303 $this->manager->error = sprintf($this->lang['error_decompress'],$file)."\n"; 304 } 305 306 // search $tmp for the folder(s) that has been created 307 // move the folder(s) to lib/plugins/ 308 if (!$this->manager->error) { 309 if ($dh = @opendir("$tmp/")) { 310 while (false !== ($f = readdir($dh))) { 311 if ($f == '.' || $f == '..' || $f == 'tmp') continue; 312 if (!is_dir("$tmp/$f")) continue; 313 314 // check to make sure we aren't overwriting anything 315 if (!$overwrite && @file_exists(DOKU_PLUGIN.$f)) { 316 // remember our settings, ask the user to confirm overwrite, FIXME 317 continue; 318 } 319 320 $instruction = @file_exists(DOKU_PLUGIN.$f) ? 'update' : 'install'; 321 322 if (ap_copy("$tmp/$f", DOKU_PLUGIN.$f)) { 323 $this->downloaded[] = $f; 324 $this->plugin_writelog($f, $instruction, array($url)); 325 } else { 326 $this->manager->error .= sprintf($this->lang['error_copy']."\n", $f); 327 } 328 } 329 closedir($dh); 330 } else { 331 $this->manager->error = $this->lang['error']."\n"; 332 } 333 } 334 335 // cleanup 336 if ($tmp) ap_delete($tmp); 337 338 if (!$this->manager->error) { 339 $this->refresh(); 340 return true; 341 } 342 343 return false; 344 } 345 346 // log 347 function plugin_writelog($plugin, $cmd, $data) { 348 349 $file = DOKU_PLUGIN.$plugin.'/manager.dat'; 350 351 switch ($cmd) { 352 case 'install' : 353 $url = $data[0]; 354 $date = date('r'); 355 if (!$fp = @fopen($file, 'w')) return; 356 fwrite($fp, "installed=$date\nurl=$url\n"); 357 fclose($fp); 358 break; 359 360 case 'update' : 361 $date = date('r'); 362 if (!$fp = @fopen($file, 'a')) return; 363 fwrite($fp, "updated=$date\n"); 364 fclose($fp); 365 break; 366 } 367 } 368 369 function plugin_readlog($plugin, $field) { 370 static $log = array(); 371 $file = DOKU_PLUGIN.$plugin.'/manager.dat'; 372 373 if (!isset($log[$plugin])) { 374 $tmp = @file_get_contents($file); 375 if (!$tmp) return ''; 376 $log[$plugin] = & $tmp; 377 } 378 379 if ($field == 'ALL') { 380 return $log[$plugin]; 381 } 382 383 $match = array(); 384 if (preg_match_all('/'.$field.'=(.*)$/m',$log[$plugin], $match)) 385 return implode("\n", $match[1]); 386 387 return ''; 388 } 389 } 390 391 class ap_download extends ap_manage { 392 393 var $overwrite = false; 394 395 function process() { 396 global $lang; 397 398 $plugin_url = $_REQUEST['url']; 399 $this->download($plugin_url, $this->overwrite); 400 return ''; 401 } 402 403 function html() { 404 parent::html(); 405 406 ptln('<div class="pm_info">'); 407 ptln('<h2>'.$this->lang['downloading'].'</h2>'); 408 409 if ($this->manager->error) { 410 ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); 411 } else if (count($this->downloaded) == 1) { 412 ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>'); 413 } else if (count($this->downloaded)) { // more than one plugin in the download 414 ptln('<p>'.$this->lang['downloads'].'</p>'); 415 ptln('<ul>'); 416 foreach ($this->downloaded as $plugin) { 417 ptln('<li><div class="li">'.$plugin.'</div></li>',2); 418 } 419 ptln('</ul>'); 420 } else { // none found in download 421 ptln('<p>'.$this->lang['download_none'].'</p>'); 422 } 423 ptln('</div>'); 424 } 425 426 } 427 428 class ap_delete extends ap_manage { 429 430 function process() { 431 432 if (!ap_delete(DOKU_PLUGIN.$this->manager->plugin)) { 433 $this->manager->error = sprintf($this->lang['error_delete'],$this->manager->plugin); 434 } else { 435 $this->refresh(); 436 } 437 } 438 439 function html() { 440 parent::html(); 441 442 ptln('<div class="pm_info">'); 443 ptln('<h2>'.$this->lang['deleting'].'</h2>'); 444 445 if ($this->manager->error) { 446 ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); 447 } else { 448 ptln('<p>'.sprintf($this->lang['deleted'],$this->plugin).'</p>'); 449 } 450 ptln('</div>'); 451 } 452 } 453 454 class ap_info extends ap_manage { 455 456 var $plugin_info = array(); // the plugin itself 457 var $details = array(); // any component plugins 458 459 function process() { 460 461 // sanity check 462 if (!$this->manager->plugin) { return; } 463 464 $component_list = ap_plugin_components($this->manager->plugin); 465 usort($component_list, 'ap_component_sort'); 466 467 foreach ($component_list as $component) { 468 if ($obj = & plugin_load($component['type'],$component['name']) === NULL) continue; 469 470 $this->details[] = array_merge($obj->getInfo(), array('type' => $component['type'])); 471 unset($obj); 472 } 473 474 // review details to simplify things 475 foreach($this->details as $info) { 476 foreach($info as