diff --git a/lib/plugins/plugin/admin.php b/lib/plugins/plugin/admin.php index c0918c70f5424b4cc7987693d3b59d51aea058d2..bf0b0b88a4240bdfe3a45da0c38c4e2a0fef1b99 100644 --- a/lib/plugins/plugin/admin.php +++ b/lib/plugins/plugin/admin.php @@ -15,15 +15,16 @@ if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'admin.php'); +require_once(DOKU_PLUGIN."/plugin/classes/ap_manage.class.php"); - //--------------------------[ GLOBALS ]------------------------------------------------ - // note: probably should be dokuwiki wide globals, where they can be accessed by pluginutils.php - // global $plugin_types; - // $plugin_types = array('syntax', 'admin'); +//--------------------------[ GLOBALS ]------------------------------------------------ +// note: probably should be dokuwiki wide globals, where they can be accessed by pluginutils.php +// global $plugin_types; +// $plugin_types = array('syntax', 'admin'); - // plugins that are an integral part of dokuwiki, they shouldn't be disabled or deleted - global $plugin_protected; - $plugin_protected = array('acl','plugin','config','info','usermanager','revert'); +// plugins that are an integral part of dokuwiki, they shouldn't be disabled or deleted +global $plugin_protected; +$plugin_protected = array('acl','plugin','config','info','usermanager','revert'); /** * All DokuWiki plugins to extend the admin function @@ -44,88 +45,73 @@ class admin_plugin_plugin extends DokuWiki_Admin_Plugin { var $error = ''; function admin_plugin_plugin() { - global $conf; - $this->disabled = (isset($conf['pluginmanager']) && ($conf['pluginmanager'] == 0)); + global $conf; + $this->disabled = (isset($conf['pluginmanager']) && ($conf['pluginmanager'] == 0)); } /** * return some info */ function getInfo(){ - $disabled = ($this->disabled) ? '(disabled)' : ''; - - return array( - 'author' => 'Christopher Smith', - 'email' => 'chris@jalakai.co.uk', - 'date' => '2005-08-10', - 'name' => 'Plugin Manager', - 'desc' => "Manage Plugins, including automated plugin installer $disabled", - 'url' => 'http://dokuwiki.org/plugin:plugin', - ); - } - - /** - * return prompt for admin menu - */ - function getMenuText($language) { - - if (!$this->disabled) - return parent::getMenuText($language); + $disabled = ($this->disabled) ? '(disabled)' : ''; - return ''; + return array( + 'author' => 'Christopher Smith', + 'email' => 'chris@jalakai.co.uk', + 'date' => '2009-07-25', + 'name' => 'Plugin Manager', + 'desc' => "Manage Plugins, including automated plugin installer $disabled", + 'url' => 'http://dokuwiki.org/plugin:plugin', + ); } /** * return sort order for position in admin menu */ function getMenuSort() { - return 20; + return 20; } /** * handle user request */ function handle() { + // enable direct access to language strings + $this->setupLocale(); - if ($this->disabled) return; - - // enable direct access to language strings - $this->setupLocale(); - -// $this->plugin = $_REQUEST['plugin']; -// $this->cmd = $_REQUEST['fn']; -// if (is_array($this->cmd)) $this->cmd = key($this->cmd); - - $fn = $_REQUEST['fn']; - if (is_array($fn)) { - $this->cmd = key($fn); - $this->plugin = is_array($fn[$this->cmd]) ? key($fn[$this->cmd]) : null; - } else { - $this->cmd = $fn; - $this->plugin = null; - } + $fn = $_REQUEST['fn']; + if (is_array($fn)) { + $this->cmd = key($fn); + $this->plugin = is_array($fn[$this->cmd]) ? key($fn[$this->cmd]) : null; + } else { + $this->cmd = $fn; + $this->plugin = null; + } - $this->_get_plugin_list(); + $this->_get_plugin_list(); - // verify $_REQUEST vars - if (in_array($this->cmd, $this->commands)) { - $this->plugin = ''; - } else if (!in_array($this->cmd, $this->functions) || !in_array($this->plugin, $this->plugin_list)) { - $this->cmd = 'manage'; - $this->plugin = ''; - } + // verify $_REQUEST vars + if (in_array($this->cmd, $this->commands)) { + $this->plugin = ''; + } else if (!in_array($this->cmd, $this->functions) || !in_array($this->plugin, $this->plugin_list)) { + $this->cmd = 'manage'; + $this->plugin = ''; + } - if(($this->cmd != 'manage' || $this->plugin != '') && !checkSecurityToken()){ - $this->cmd = 'manage'; - $this->plugin = ''; - } + if(($this->cmd != 'manage' || $this->plugin != '') && !checkSecurityToken()){ + $this->cmd = 'manage'; + $this->plugin = ''; + } - // create object to handle the command - $class = "ap_".$this->cmd; - if (!class_exists($class)) $class = 'ap_manage'; + // create object to handle the command + $class = "ap_".$this->cmd; + @require_once(DOKU_PLUGIN."/plugin/classes/$class.class.php"); + if (!class_exists($class)){ + $class = 'ap_manage'; + } - $this->handler = & new $class($this, $this->plugin); - $this->msg = $this->handler->process(); + $this->handler = & new $class($this, $this->plugin); + $this->msg = $this->handler->process(); } @@ -133,656 +119,34 @@ class admin_plugin_plugin extends DokuWiki_Admin_Plugin { * output appropriate html */ function html() { + // enable direct access to language strings + $this->setupLocale(); + $this->_get_plugin_list(); - if ($this->disabled) return; - - // enable direct access to language strings - $this->setupLocale(); - $this->_get_plugin_list(); + if ($this->handler === NULL) $this->handler = & new ap_manage($this, $this->plugin); - if ($this->handler === NULL) $this->handler = & new ap_manage($this, $this->plugin); - - ptln('<div id="plugin__manager">'); - $this->handler->html(); - ptln('</div><!-- #plugin_manager -->'); + ptln('<div id="plugin__manager">'); + $this->handler->html(); + ptln('</div><!-- #plugin_manager -->'); } + /** + * Returns a list of all plugins, including the disabled ones + */ function _get_plugin_list() { - if (empty($this->plugin_list)) { - $list = plugin_list('',true); // all plugins, including disabled ones - sort($list); - trigger_event('PLUGIN_PLUGINMANAGER_PLUGINLIST',$list); - $this->plugin_list = $list; - } - return $this->plugin_list; - } - -} - -class ap_manage { - - var $manager = NULL; - var $lang = array(); - var $plugin = ''; - var $downloaded = array(); - - function ap_manage(&$manager, $plugin) { - $this->manager = & $manager; - $this->plugin = $plugin; - $this->lang = & $manager->lang; + if (empty($this->plugin_list)) { + $list = plugin_list('',true); // all plugins, including disabled ones + sort($list); + trigger_event('PLUGIN_PLUGINMANAGER_PLUGINLIST',$list); + $this->plugin_list = $list; } - - function process() { - return ''; - } - - function html() { - print $this->manager->locale_xhtml('admin_plugin'); - $this->html_menu(); - } - - // build our standard menu - function html_menu($listPlugins = true) { - global $ID; - - ptln('<div class="pm_menu">'); - - ptln('<div class="common">'); - ptln(' <h2>'.$this->lang['download'].'</h2>'); - ptln(' <form action="'.wl($ID).'" method="post">'); - ptln(' <fieldset class="hidden">',4); - ptln(' <input type="hidden" name="do" value="admin" />'); - ptln(' <input type="hidden" name="page" value="plugin" />'); - formSecurityToken(); - ptln(' </fieldset>'); - ptln(' <fieldset>'); - ptln(' <legend>'.$this->lang['download'].'</legend>'); - ptln(' <label for="dw__url">'.$this->lang['url'].'<input name="url" id="dw__url" class="edit" type="text" maxlength="200" /></label>'); - ptln(' <input type="submit" class="button" name="fn[download]" value="'.$this->lang['btn_download'].'" />'); - ptln(' </fieldset>'); - ptln(' </form>'); - ptln('</div>'); - - if ($listPlugins) { - ptln('<h2>'.$this->lang['manage'].'</h2>'); - - ptln('<form action="'.wl($ID).'" method="post" class="plugins">'); -// ptln(' <div class="plugins">'); - - ptln(' <fieldset class="hidden">'); - ptln(' <input type="hidden" name="do" value="admin" />'); - ptln(' <input type="hidden" name="page" value="plugin" />'); - formSecurityToken(); - ptln(' </fieldset>'); - - $this->html_pluginlist(); - - ptln(' <fieldset class="buttons">'); - ptln(' <input type="submit" class="button" name="fn[enable]" value="'.$this->lang['btn_enable'].'" />'); - ptln(' </fieldset>'); - -// ptln(' </div>'); - ptln('</form>'); - } - - ptln('</div>'); - } - - function html_pluginlist() { - global $ID; - global $plugin_protected; - - foreach ($this->manager->plugin_list as $plugin) { - - $disabled = plugin_isdisabled($plugin); - $protected = in_array($plugin,$plugin_protected); - - $checked = ($disabled) ? '' : ' checked="checked"'; - $check_disabled = ($protected) ? ' disabled="disabled"' : ''; - - // determine display class(es) - $class = array(); - if (in_array($plugin, $this->downloaded)) $class[] = 'new'; - if ($disabled) $class[] = 'disabled'; - if ($protected) $class[] = 'protected'; - - $class = count($class) ? ' class="'.join(' ', $class).'"' : ''; - - ptln(' <fieldset'.$class.'>'); - ptln(' <legend>'.$plugin.'</legend>'); - ptln(' <input type="checkbox" class="enable" name="enabled[]" value="'.$plugin.'"'.$checked.$check_disabled.' />'); - ptln(' <h3 class="legend">'.$plugin.'</h3>'); - - $this->html_button($plugin, 'info', false, 6); - if (in_array('settings', $this->manager->functions)) { - $this->html_button($plugin, 'settings', !@file_exists(DOKU_PLUGIN.$plugin.'/settings.php'), 6); - } - $this->html_button($plugin, 'update', !$this->plugin_readlog($plugin, 'url'), 6); - $this->html_button($plugin, 'delete', $protected, 6); - - ptln(' </fieldset>'); - } - } - - function html_button($plugin, $btn, $disabled=false, $indent=0) { - $disabled = ($disabled) ? 'disabled="disabled"' : ''; - ptln('<input type="submit" class="button" '.$disabled.' name="fn['.$btn.']['.$plugin.']" value="'.$this->lang['btn_'.$btn].'" />',$indent); - } - - /** - * Refresh plugin list - */ - function refresh() { - global $MSG,$config_cascade; - - //are there any undisplayed messages? keep them in session for display - if (isset($MSG) && count($MSG)){ - //reopen session, store data and close session again - @session_start(); - $_SESSION[DOKU_COOKIE]['msg'] = $MSG; - session_write_close(); - } - - // expire dokuwiki caches - // touching local.php expires wiki page, JS and CSS caches - @touch(reset($config_cascade['main']['local'])); - - // update latest plugin date - FIXME - header('Location: '.wl($ID).'?do=admin&page=plugin'); - exit(); - } - - function download($url, $overwrite=false) { - global $lang; - - // check the url - $matches = array(); - if (!preg_match("/[^\/]*$/", $url, $matches) || !$matches[0]) { - $this->manager->error = $this->lang['error_badurl']."\n"; - return false; - } - - $file = $matches[0]; - - if (!($tmp = io_mktmpdir())) { - $this->manager->error = $this->lang['error_dircreate']."\n"; - return false; - } - - if (!$file = io_download($url, "$tmp/", true, $file)) { - $this->manager->error = sprintf($this->lang['error_download'],$url)."\n"; - } - - if (!$this->manager->error && !ap_decompress("$tmp/$file", $tmp)) { - $this->manager->error = sprintf($this->lang['error_decompress'],$file)."\n"; - } - - // search $tmp for the folder(s) that has been created - // move the folder(s) to lib/plugins/ - if (!$this->manager->error) { - if ($dh = @opendir("$tmp/")) { - while (false !== ($f = readdir($dh))) { - if ($f == '.' || $f == '..' || $f == 'tmp') continue; - if (!is_dir("$tmp/$f")) continue; - - // check to make sure we aren't overwriting anything - if (!$overwrite && @file_exists(DOKU_PLUGIN.$f)) { - // remember our settings, ask the user to confirm overwrite, FIXME - continue; - } - - $instruction = @file_exists(DOKU_PLUGIN.$f) ? 'update' : 'install'; - - if (ap_copy("$tmp/$f", DOKU_PLUGIN.$f)) { - $this->downloaded[] = $f; - $this->plugin_writelog($f, $instruction, array($url)); - } else { - $this->manager->error .= sprintf($this->lang['error_copy']."\n", $f); - } - } - closedir($dh); - } else { - $this->manager->error = $this->lang['error']."\n"; - } - } - - // cleanup - if ($tmp) ap_delete($tmp); - - if (!$this->manager->error) { - msg('Plugin package ('.count($this->downloaded).' plugin'.(count($this->downloaded) != 1?'s':'').': '.join(',',$this->downloaded).') successfully installed.',1); - $this->refresh(); - return true; - } - - return false; - } - - // log - function plugin_writelog($plugin, $cmd, $data) { - - $file = DOKU_PLUGIN.$plugin.'/manager.dat'; - - switch ($cmd) { - case 'install' : - $url = $data[0]; - $date = date('r'); - if (!$fp = @fopen($file, 'w')) return; - fwrite($fp, "installed=$date\nurl=$url\n"); - fclose($fp); - break; - - case 'update' : - $date = date('r'); - if (!$fp = @fopen($file, 'a')) return; - fwrite($fp, "updated=$date\n"); - fclose($fp); - break; - } - } - - function plugin_readlog($plugin, $field) { - static $log = array(); - $file = DOKU_PLUGIN.plugin_directory($plugin).'/manager.dat'; - - if (!isset($log[$plugin])) { - $tmp = @file_get_contents($file); - if (!$tmp) return ''; - $log[$plugin] = & $tmp; - } - - if ($field == 'ALL') { - return $log[$plugin]; - } - - $match = array(); - if (preg_match_all('/'.$field.'=(.*)$/m',$log[$plugin], $match)) - return implode("\n", $match[1]); - - return ''; - } - } - - class ap_download extends ap_manage { - - var $overwrite = false; - - function process() { - global $lang; - - $plugin_url = $_REQUEST['url']; - $this->download($plugin_url, $this->overwrite); - return ''; - } - - function html() { - parent::html(); - - ptln('<div class="pm_info">'); - ptln('<h2>'.$this->lang['downloading'].'</h2>'); - - if ($this->manager->error) { - ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); - } else if (count($this->downloaded) == 1) { - ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>'); - } else if (count($this->downloaded)) { // more than one plugin in the download - ptln('<p>'.$this->lang['downloads'].'</p>'); - ptln('<ul>'); - foreach ($this->downloaded as $plugin) { - ptln('<li><div class="li">'.$plugin.'</div></li>',2); - } - ptln('</ul>'); - } else { // none found in download - ptln('<p>'.$this->lang['download_none'].'</p>'); - } - ptln('</div>'); - } - + return $this->plugin_list; } - class ap_delete extends ap_manage { - - function process() { - - if (!ap_delete(DOKU_PLUGIN.plugin_directory($this->manager->plugin))) { - $this->manager->error = sprintf($this->lang['error_delete'],$this->manager->plugin); - } else { - msg("Plugin {$this->manager->plugin} successfully deleted."); - $this->refresh(); - } - } - - function html() { - parent::html(); - - ptln('<div class="pm_info">'); - ptln('<h2>'.$this->lang['deleting'].'</h2>'); - - if ($this->manager->error) { - ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); - } else { - ptln('<p>'.sprintf($this->lang['deleted'],$this->plugin).'</p>'); - } - ptln('</div>'); - } - } - - class ap_info extends ap_manage { - - var $plugin_info = array(); // the plugin itself - var $details = array(); // any component plugins - - function process() { - - // sanity check - if (!$this->manager->plugin) { return; } - - $component_list = ap_plugin_components($this->manager->plugin); - usort($component_list, 'ap_component_sort'); - - foreach ($component_list as $component) { - if ($obj = & plugin_load($component['type'],$component['name']) === NULL) continue; - - $this->details[] = array_merge($obj->getInfo(), array('type' => $component['type'])); - unset($obj); - } - - // review details to simplify things - foreach($this->details as $info) { - foreach($info as $item => $value) { - if (!isset($this->plugin_info[$item])) { $this->plugin_info[$item] = $value; continue; } - if ($this->plugin_info[$item] != $value) $this->plugin_info[$item] = ''; - } - } - } - - function html() { - - // output the standard menu stuff - parent::html(); - - // sanity check - if (!$this->manager->plugin) { return; } - - ptln('<div class="pm_info">'); - ptln("<h2>".$this->manager->getLang('plugin')." {$this->manager->plugin}</h2>"); - - // collect pertinent information from the log - $installed = $this->plugin_readlog($this->manager->plugin, 'installed'); - $source = $this->plugin_readlog($this->manager->plugin, 'url'); - $updated = $this->plugin_readlog($this->manager->plugin, 'updated'); - if (strrpos($updated, "\n") !== false) $updated = substr($updated, strrpos($updated, "\n")+1); - - ptln("<dl>",2); - ptln("<dt>".$this->manager->getLang('source').'</dt><dd>'.($source ? $source : $this->manager->getLang('unknown'))."</dd>",4); - ptln("<dt>".$this->manager->getLang('installed').'</dt><dd>'.($installed ? $installed : $this->manager->getLang('unknown'))."</dd>",4); - if ($updated) ptln("<dt>".$this->manager->getLang('lastupdate').'</dt><dd>'.$updated."</dd>",4); - ptln("</dl>",2); - - if (count($this->details) == 0) { - ptln("<p>".$this->manager->getLang('noinfo')."</p>",2); - } else { - - ptln("<dl>",2); - if ($this->plugin_info['name']) ptln("<dt>".$this->manager->getLang('name')."</dt><dd>".$this->out($this->plugin_info['name'])."</dd>",4); - if ($this->plugin_info['date']) ptln("<dt>".$this->manager->getLang('date')."</dt><dd>".$this->out($this->plugin_info['date'])."</dd>",4); - if ($this->plugin_info['type']) ptln("<dt>".$this->manager->getLang('type')."</dt><dd>".$this->out($this->plugin_info['type'])."</dd>",4); - if ($this->plugin_info['desc']) ptln("<dt>".$this->manager->getLang('desc')."</dt><dd>".$this->out($this->plugin_info['desc'])."</dd>",4); - if ($this->plugin_info['author']) ptln("<dt>".$this->manager->getLang('author')."</dt><dd>".$this->manager->email($this->plugin_info['email'], $this->plugin_info['author'])."</dd>",4); - if ($this->plugin_info['url']) ptln("<dt>".$this->manager->getLang('www')."</dt><dd>".$this->manager->external_link($this->plugin_info['url'], '', 'urlextern')."</dd>",4); - ptln("</dl>",2); - - if (count($this->details) > 1) { - ptln("<h3>".$this->manager->getLang('components')."</h3>",2); - ptln("<div>",2); - - foreach ($this->details as $info) { - - ptln("<dl>",4); - ptln("<dt>".$this->manager->getLang('name')."</dt><dd>".$this->out($info['name'])."</dd>",6); - if (!$this->plugin_info['date']) ptln("<dt>".$this->manager->getLang('date')."</dt><dd>".$this->out($info['date'])."</dd>",6); - if (!$this->plugin_info['type']) ptln("<dt>".$this->manager->getLang('type')."</dt><dd>".$this->out($info['type'])."</dd>",6); - if (!$this->plugin_info['desc']) ptln("<dt>".$this->manager->getLang('desc')."</dt><dd>".$this->out($info['desc'])."</dd>",6); - if (!$this->plugin_info['author']) ptln("<dt>".$this->manager->getLang('author')."</dt><dd>".$this->manager->email($info['email'], $info['author'])."</dd>",6); - if (!$this->plugin_info['url']) ptln("<dt>".$this->manager->getLang('www')."</dt><dd>".$this->manager->external_link($info['url'], '', 'urlextern')."</dd>",6); - ptln("</dl>",4); - - } - ptln("</div>",2); - } - } - ptln("</div>"); - } - - // simple output filter, make html entities safe and convert new lines to <br /> - function out($text) { - return str_replace("\n",'<br />',htmlspecialchars($text)); - } - - } - - //--------------[ to do ]--------------------------------------- - class ap_update extends ap_manage { - - var $overwrite = true; - - function process() { - global $lang; - - $plugin_url = $this->plugin_readlog($this->plugin, 'url'); - $this->download($plugin_url, $this->overwrite); - return ''; - } - - function html() { - parent::html(); - - ptln('<div class="pm_info">'); - ptln('<h2>'.$this->lang['updating'].'</h2>'); - - if ($this->manager->error) { - ptln('<div class="error">'.str_replace("\n","<br />", $this->manager->error).'</div>'); - } else if (count($this->downloaded) == 1) { - ptln('<p>'.sprintf($this->lang['updated'],$this->downloaded[0]).'</p>'); - } else if (count($this->downloaded)) { // more than one plugin in the download - ptln('<p>'.$this->lang['updates'].'</p>'); - ptln('<ul>'); - foreach ($this->downloaded as $plugin) { - ptln('<li><div class="li">'.$plugin.'</div></li>',2); - } - ptln('</ul>'); - } else { // none found in download - ptln('<p>'.$this->lang['update_none'].'</p>'); - } - ptln('</div>'); - } - } - class ap_settings extends ap_manage {} - - class ap_enable extends ap_manage { - - var $enabled = array(); - - function process() { - global $plugin_protected; - $count_enabled = $count_disabled = 0; - - $this->enabled = isset($_REQUEST['enabled']) ? $_REQUEST['enabled'] : array(); - - foreach ($this->manager->plugin_list as $plugin) { - if (in_array($plugin, $plugin_protected)) continue; - - $new = in_array($plugin, $this->enabled); - $old = !plugin_isdisabled($plugin); - - if ($new != $old) { - switch ($new) { - // enable plugin - case true : - if(plugin_enable($plugin)){ - msg(sprintf($this->lang['enabled'],$plugin),1); - $count_enabled++; - }else{ - msg(sprintf($this->lang['notenabled'],$plugin),-1); - } - break; - case false: - if(plugin_disable($plugin)){ - msg(sprintf($this->lang['disabled'],$plugin),1); - $count_disabled++; - }else{ - msg(sprintf($this->lang['notdisabled'],$plugin),-1); - } - break; - } - } - } - - // refresh plugins, including expiring any dokuwiki cache(s) - if ($count_enabled || $count_disabled) { - $this->refresh(); - } - } - - } - - //--------------[ utilities ]----------------------------------- - - function is_css($f) { return (substr($f, -4) == '.css'); } - - // generate an admin plugin href - function apl($pl, $fn) { - global $ID; - return wl($ID,"do=admin&page=plugin".($pl?"&plugin=$pl":"").($fn?"&fn=$fn":"")); - } - - // decompress wrapper - function ap_decompress($file, $target) { - global $conf; - - // decompression library doesn't like target folders ending in "/" - if (substr($target, -1) == "/") $target = substr($target, 0, -1); - $ext = substr($file, strrpos($file,'.')+1); - - // .tar, .tar.bz, .tar.gz, .tgz - if (in_array($ext, array('tar','bz','bz2','gz','tgz'))) { - - require_once(DOKU_INC."inc/TarLib.class.php"); - - if (strpos($ext, 'bz') !== false) $compress_type = COMPRESS_BZIP; - else if (strpos($ext,'gz') !== false) $compress_type = COMPRESS_GZIP; - else $compress_type = COMPRESS_NONE; - - $tar = new TarLib($file, $compress_type); - if($tar->_initerror < 0){ - if($conf['allowdebug']){ - msg('TarLib Error: '.$tar->TarErrorStr($tar->_initerror),-1); - } - return false; - } - $ok = $tar->Extract(FULL_ARCHIVE, $target, '', 0777); - - if($ok<1){ - if($conf['allowdebug']){ - msg('TarLib Error: '.$tar->TarErrorStr($ok),-1); - } - return false; - } - return true; - } else if ($ext == 'zip') { - - require_once(DOKU_INC."inc/ZipLib.class.php"); - - $zip = new ZipLib(); - $ok = $zip->Extract($file, $target); - - // FIXME sort something out for handling zip error messages meaningfully - return ($ok==-1?false:true); - - } else if ($ext == "rar") { - // not yet supported -- fix me - return false; - } - - // unsupported file type - return false; - } - - // copy with recursive sub-directory support - function ap_copy($src, $dst) { - global $conf; - - if (is_dir($src)) { - if (!$dh = @opendir($src)) return false; - - if ($ok = io_mkdir_p($dst)) { - while ($ok && (false !== ($f = readdir($dh)))) { - if ($f == '..' || $f == '.') continue; - $ok = ap_copy("$src/$f", "$dst/$f"); - } - } - - closedir($dh); - return $ok; - - } else { - $exists = @file_exists($dst); - - if (!@copy($src,$dst)) return false; - if (!$exists && !empty($conf['fperm'])) chmod($dst, $conf['fperm']); - @touch($dst,filemtime($src)); - } - - return true; - } - - // delete, with recursive sub-directory support - function ap_delete($path) { - - if (!is_string($path) || $path == "") return false; - - if (is_dir($path)) { - if (!$dh = @opendir($path)) return false; - - while ($f = readdir($dh)) { - if ($f == '..' || $f == '.') continue; - ap_delete("$path/$f"); - } - - closedir($dh); - return @rmdir($path); - } else { - return @unlink($path); - } - - return false; - } - - // return a list (name & type) of all the component plugins that make up this plugin - // can this move to pluginutils? - function ap_plugin_components($plugin) { +} - global $plugin_types; - $components = array(); - $path = DOKU_PLUGIN.plugin_directory($plugin).'/'; - foreach ($plugin_types as $type) { - if (@file_exists($path.$type.'.php')) { $components[] = array('name'=>$plugin, 'type'=>$type); continue; } - if ($dh = @opendir($path.$type.'/')) { - while (false !== ($cp = readdir($dh))) { - if ($cp == '.' || $cp == '..' || strtolower(substr($cp,-4)) != '.php') continue; - $components[] = array('name'=>$plugin.'_'.substr($cp, 0, -4), 'type'=>$type); - } - closedir($dh); - } - } - return $components; - } - function ap_component_sort($a, $b) { - if ($a['name'] == $b['name']) return 0; - return ($a['name'] < $b['name']) ? -1 : 1; - } diff --git a/lib/plugins/plugin/classes/ap_delete.class.php b/lib/plugins/plugin/classes/ap_delete.class.php new file mode 100644 index 0000000000000000000000000000000000000000..231147479078e76bf25c3a9796e20c1ba962ca2a --- /dev/null +++ b/lib/plugins/plugin/classes/ap_delete.class.php @@ -0,0 +1,28 @@ +<?php +class ap_delete extends ap_manage { + + function process() { + + if (!$this->dir_delete(DOKU_PLUGIN.plugin_directory($this->manager->plugin))) { + $this->manager->error = sprintf($this->lang['error_delete'],$this->manager->plugin); + } else { + msg("Plugin {$this->manager->plugin} successfully deleted."); + $this->refresh(); + } + } + + function html() { + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['deleting'].'</h2>'); + + if ($this->manager->error) { + ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); + } else { + ptln('<p>'.sprintf($this->lang['deleted'],$this->plugin).'</p>'); + } + ptln('</div>'); + } +} + diff --git a/lib/plugins/plugin/classes/ap_download.class.php b/lib/plugins/plugin/classes/ap_download.class.php new file mode 100644 index 0000000000000000000000000000000000000000..465cb2da178473100564f1b218929fdccbf5412e --- /dev/null +++ b/lib/plugins/plugin/classes/ap_download.class.php @@ -0,0 +1,201 @@ +<?php +class ap_download extends ap_manage { + + var $overwrite = false; + + /** + * Initiate the plugin download + */ + function process() { + global $lang; + + $plugin_url = $_REQUEST['url']; + $this->download($plugin_url, $this->overwrite); + return ''; + } + + /** + * Print results of the download + */ + function html() { + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['downloading'].'</h2>'); + + if ($this->manager->error) { + ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>'); + } else if (count($this->downloaded) == 1) { + ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>'); + } else if (count($this->downloaded)) { // more than one plugin in the download + ptln('<p>'.$this->lang['downloads'].'</p>'); + ptln('<ul>'); + foreach ($this->downloaded as $plugin) { + ptln('<li><div class="li">'.$plugin.'</div></li>',2); + } + ptln('</ul>'); + } else { // none found in download + ptln('<p>'.$this->lang['download_none'].'</p>'); + } + ptln('</div>'); + } + + /** + * Process the downloaded file + */ + function download($url, $overwrite=false) { + global $lang; + + // check the url + $matches = array(); + if (!preg_match("/[^\/]*$/", $url, $matches) || !$matches[0]) { + $this->manager->error = $this->lang['error_badurl']."\n"; + return false; + } + + $file = $matches[0]; + + if (!($tmp = io_mktmpdir())) { + $this->manager->error = $this->lang['error_dircreate']."\n"; + return false; + } + + if (!$file = io_download($url, "$tmp/", true, $file)) { + $this->manager->error = sprintf($this->lang['error_download'],$url)."\n"; + } + + if (!$this->manager->error && !$this->decompress("$tmp/$file", $tmp)) { + $this->manager->error = sprintf($this->lang['error_decompress'],$file)."\n"; + } + + // search $tmp for the folder(s) that has been created + // move the folder(s) to lib/plugins/ + if (!$this->manager->error) { + if ($dh = @opendir("$tmp/")) { + while (false !== ($f = readdir($dh))) { + if ($f == '.' || $f == '..' || $f == 'tmp') continue; + if (!is_dir("$tmp/$f")) continue; + + // check to make sure we aren't overwriting anything + if (!$overwrite && @file_exists(DOKU_PLUGIN.$f)) { + // remember our settings, ask the user to confirm overwrite, FIXME + continue; + } + + $instruction = @file_exists(DOKU_PLUGIN.$f) ? 'update' : 'install'; + + if ($this->dircopy("$tmp/$f", DOKU_PLUGIN.$f)) { + $this->downloaded[] = $f; + $this->plugin_writelog($f, $instruction, array($url)); + } else { + $this->manager->error .= sprintf($this->lang['error_copy']."\n", $f); + } + } + closedir($dh); + } else { + $this->manager->error = $this->lang['error']."\n"; + } + } + + // cleanup + if ($tmp) $this->dir_delete($tmp); + + if (!$this->manager->error) { + msg('Plugin package ('.count($this->downloaded).' plugin'.(count($this->downloaded) != 1?'s':'').': '.join(',',$this->downloaded).') successfully installed.',1); + $this->refresh(); + return true; + } + + return false; + } + + + /** + * Decompress a given file to the given target directory + * + * Determines the compression type from the file extension + */ + function decompress($file, $target) { + global $conf; + + // decompression library doesn't like target folders ending in "/" + if (substr($target, -1) == "/") $target = substr($target, 0, -1); + $ext = substr($file, strrpos($file,'.')+1); + + // .tar, .tar.bz, .tar.gz, .tgz + if (in_array($ext, array('tar','bz','bz2','gz','tgz'))) { + + require_once(DOKU_INC."inc/TarLib.class.php"); + + if (strpos($ext, 'bz') !== false) $compress_type = COMPRESS_BZIP; + else if (strpos($ext,'gz') !== false) $compress_type = COMPRESS_GZIP; + else $compress_type = COMPRESS_NONE; + + $tar = new TarLib($file, $compress_type); + if($tar->_initerror < 0){ + if($conf['allowdebug']){ + msg('TarLib Error: '.$tar->TarErrorStr($tar->_initerror),-1); + } + return false; + } + $ok = $tar->Extract(FULL_ARCHIVE, $target, '', 0777); + + if($ok<1){ + if($conf['allowdebug']){ + msg('TarLib Error: '.$tar->TarErrorStr($ok),-1); + } + return false; + } + return true; + } else if ($ext == 'zip') { + + require_once(DOKU_INC."inc/ZipLib.class.php"); + + $zip = new ZipLib(); + $ok = $zip->Extract($file, $target); + + // FIXME sort something out for handling zip error messages meaningfully + return ($ok==-1?false:true); + + } else if ($ext == "rar") { + // not yet supported -- fix me + return false; + } + + // unsupported file type + return false; + } + + /** + * Copy with recursive sub-directory support + */ + function dircopy($src, $dst) { + global $conf; + + if (is_dir($src)) { + if (!$dh = @opendir($src)) return false; + + if ($ok = io_mkdir_p($dst)) { + while ($ok && (false !== ($f = readdir($dh)))) { + if ($f == '..' || $f == '.') continue; + $ok = $this->dircopy("$src/$f", "$dst/$f"); + } + } + + closedir($dh); + return $ok; + + } else { + $exists = @file_exists($dst); + + if (!@copy($src,$dst)) return false; + if (!$exists && !empty($conf['fperm'])) chmod($dst, $conf['fperm']); + @touch($dst,filemtime($src)); + } + + return true; + } + + +} + diff --git a/lib/plugins/plugin/classes/ap_enable.class.php b/lib/plugins/plugin/classes/ap_enable.class.php new file mode 100644 index 0000000000000000000000000000000000000000..35450a9073dd43768f3896007fbc9743f0cef386 --- /dev/null +++ b/lib/plugins/plugin/classes/ap_enable.class.php @@ -0,0 +1,49 @@ +<?php + +class ap_enable extends ap_manage { + + var $enabled = array(); + + function process() { + global $plugin_protected; + $count_enabled = $count_disabled = 0; + + $this->enabled = isset($_REQUEST['enabled']) ? $_REQUEST['enabled'] : array(); + + foreach ($this->manager->plugin_list as $plugin) { + if (in_array($plugin, $plugin_protected)) continue; + + $new = in_array($plugin, $this->enabled); + $old = !plugin_isdisabled($plugin); + + if ($new != $old) { + switch ($new) { + // enable plugin + case true : + if(plugin_enable($plugin)){ + msg(sprintf($this->lang['enabled'],$plugin),1); + $count_enabled++; + }else{ + msg(sprintf($this->lang['notenabled'],$plugin),-1); + } + break; + case false: + if(plugin_disable($plugin)){ + msg(sprintf($this->lang['disabled'],$plugin),1); + $count_disabled++; + }else{ + msg(sprintf($this->lang['notdisabled'],$plugin),-1); + } + break; + } + } + } + + // refresh plugins, including expiring any dokuwiki cache(s) + if ($count_enabled || $count_disabled) { + $this->refresh(); + } + } + +} + diff --git a/lib/plugins/plugin/classes/ap_info.class.php b/lib/plugins/plugin/classes/ap_info.class.php new file mode 100644 index 0000000000000000000000000000000000000000..dd1765f6ca388ebceb8d46f4b841adb74a494ecf --- /dev/null +++ b/lib/plugins/plugin/classes/ap_info.class.php @@ -0,0 +1,129 @@ +<?php + +class ap_info extends ap_manage { + + var $plugin_info = array(); // the plugin itself + var $details = array(); // any component plugins + + function process() { + + // sanity check + if (!$this->manager->plugin) { return; } + + $component_list = $this->get_plugin_components($this->manager->plugin); + usort($component_list, array($this,'component_sort')); + + foreach ($component_list as $component) { + if ($obj = & plugin_load($component['type'],$component['name']) === NULL) continue; + + $this->details[] = array_merge($obj->getInfo(), array('type' => $component['type'])); + unset($obj); + } + + // review details to simplify things + foreach($this->details as $info) { + foreach($info as $item => $value) { + if (!isset($this->plugin_info[$item])) { $this->plugin_info[$item] = $value; continue; } + if ($this->plugin_info[$item] != $value) $this->plugin_info[$item] = ''; + } + } + } + + function html() { + + // output the standard menu stuff + parent::html(); + + // sanity check + if (!$this->manager->plugin) { return; } + + ptln('<div class="pm_info">'); + ptln("<h2>".$this->manager->getLang('plugin')." {$this->manager->plugin}</h2>"); + + // collect pertinent information from the log + $installed = $this->plugin_readlog($this->manager->plugin, 'installed'); + $source = $this->plugin_readlog($this->manager->plugin, 'url'); + $updated = $this->plugin_readlog($this->manager->plugin, 'updated'); + if (strrpos($updated, "\n") !== false) $updated = substr($updated, strrpos($updated, "\n")+1); + + ptln("<dl>",2); + ptln("<dt>".$this->manager->getLang('source').'</dt><dd>'.($source ? $source : $this->manager->getLang('unknown'))."</dd>",4); + ptln("<dt>".$this->manager->getLang('installed').'</dt><dd>'.($installed ? $installed : $this->manager->getLang('unknown'))."</dd>",4); + if ($updated) ptln("<dt>".$this->manager->getLang('lastupdate').'</dt><dd>'.$updated."</dd>",4); + ptln("</dl>",2); + + if (count($this->details) == 0) { + ptln("<p>".$this->manager->getLang('noinfo')."</p>",2); + } else { + + ptln("<dl>",2); + if ($this->plugin_info['name']) ptln("<dt>".$this->manager->getLang('name')."</dt><dd>".$this->out($this->plugin_info['name'])."</dd>",4); + if ($this->plugin_info['date']) ptln("<dt>".$this->manager->getLang('date')."</dt><dd>".$this->out($this->plugin_info['date'])."</dd>",4); + if ($this->plugin_info['type']) ptln("<dt>".$this->manager->getLang('type')."</dt><dd>".$this->out($this->plugin_info['type'])."</dd>",4); + if ($this->plugin_info['desc']) ptln("<dt>".$this->manager->getLang('desc')."</dt><dd>".$this->out($this->plugin_info['desc'])."</dd>",4); + if ($this->plugin_info['author']) ptln("<dt>".$this->manager->getLang('author')."</dt><dd>".$this->manager->email($this->plugin_info['email'], $this->plugin_info['author'])."</dd>",4); + if ($this->plugin_info['url']) ptln("<dt>".$this->manager->getLang('www')."</dt><dd>".$this->manager->external_link($this->plugin_info['url'], '', 'urlextern')."</dd>",4); + ptln("</dl>",2); + + if (count($this->details) > 1) { + ptln("<h3>".$this->manager->getLang('components')."</h3>",2); + ptln("<div>",2); + + foreach ($this->details as $info) { + + ptln("<dl>",4); + ptln("<dt>".$this->manager->getLang('name')."</dt><dd>".$this->out($info['name'])."</dd>",6); + if (!$this->plugin_info['date']) ptln("<dt>".$this->manager->getLang('date')."</dt><dd>".$this->out($info['date'])."</dd>",6); + if (!$this->plugin_info['type']) ptln("<dt>".$this->manager->getLang('type')."</dt><dd>".$this->out($info['type'])."</dd>",6); + if (!$this->plugin_info['desc']) ptln("<dt>".$this->manager->getLang('desc')."</dt><dd>".$this->out($info['desc'])."</dd>",6); + if (!$this->plugin_info['author']) ptln("<dt>".$this->manager->getLang('author')."</dt><dd>".$this->manager->email($info['email'], $info['author'])."</dd>",6); + if (!$this->plugin_info['url']) ptln("<dt>".$this->manager->getLang('www')."</dt><dd>".$this->manager->external_link($info['url'], '', 'urlextern')."</dd>",6); + ptln("</dl>",4); + + } + ptln("</div>",2); + } + } + ptln("</div>"); + } + + // simple output filter, make html entities safe and convert new lines to <br /> + function out($text) { + return str_replace("\n",'<br />',htmlspecialchars($text)); + } + + + /** + * return a list (name & type) of all the component plugins that make up this plugin + * + * @todo can this move to pluginutils? + */ + function get_plugin_components($plugin) { + + global $plugin_types; + + $components = array(); + $path = DOKU_PLUGIN.plugin_directory($plugin).'/'; + + foreach ($plugin_types as $type) { + if (@file_exists($path.$type.'.php')) { $components[] = array('name'=>$plugin, 'type'=>$type); continue; } + + if ($dh = @opendir($path.$type.'/')) { + while (false !== ($cp = readdir($dh))) { + if ($cp == '.' || $cp == '..' || strtolower(substr($cp,-4)) != '.php') continue; + $components[] = array('name'=>$plugin.'_'.substr($cp, 0, -4), 'type'=>$type); + } + closedir($dh); + } + } + return $components; + } + + /** + * usort callback to sort plugin components + */ + function component_sort($a, $b) { + if ($a['name'] == $b['name']) return 0; + return ($a['name'] < $b['name']) ? -1 : 1; + } +} diff --git a/lib/plugins/plugin/classes/ap_manage.class.php b/lib/plugins/plugin/classes/ap_manage.class.php new file mode 100644 index 0000000000000000000000000000000000000000..aea04f487d3a8ad8097b98c58e53dc41b9eb0ba4 --- /dev/null +++ b/lib/plugins/plugin/classes/ap_manage.class.php @@ -0,0 +1,203 @@ +<?php + +class ap_manage { + + var $manager = NULL; + var $lang = array(); + var $plugin = ''; + var $downloaded = array(); + + function ap_manage(&$manager, $plugin) { + $this->manager = & $manager; + $this->plugin = $plugin; + $this->lang = & $manager->lang; + } + + function process() { + return ''; + } + + function html() { + print $this->manager->locale_xhtml('admin_plugin'); + $this->html_menu(); + } + + // build our standard menu + function html_menu($listPlugins = true) { + global $ID; + + ptln('<div class="pm_menu">'); + + ptln('<div class="common">'); + ptln(' <h2>'.$this->lang['download'].'</h2>'); + ptln(' <form action="'.wl($ID).'" method="post">'); + ptln(' <fieldset class="hidden">',4); + ptln(' <input type="hidden" name="do" value="admin" />'); + ptln(' <input type="hidden" name="page" value="plugin" />'); + formSecurityToken(); + ptln(' </fieldset>'); + ptln(' <fieldset>'); + ptln(' <legend>'.$this->lang['download'].'</legend>'); + ptln(' <label for="dw__url">'.$this->lang['url'].'<input name="url" id="dw__url" class="edit" type="text" maxlength="200" /></label>'); + ptln(' <input type="submit" class="button" name="fn[download]" value="'.$this->lang['btn_download'].'" />'); + ptln(' </fieldset>'); + ptln(' </form>'); + ptln('</div>'); + + if ($listPlugins) { + ptln('<h2>'.$this->lang['manage'].'</h2>'); + + ptln('<form action="'.wl($ID).'" method="post" class="plugins">'); + + ptln(' <fieldset class="hidden">'); + ptln(' <input type="hidden" name="do" value="admin" />'); + ptln(' <input type="hidden" name="page" value="plugin" />'); + formSecurityToken(); + ptln(' </fieldset>'); + + $this->html_pluginlist(); + + ptln(' <fieldset class="buttons">'); + ptln(' <input type="submit" class="button" name="fn[enable]" value="'.$this->lang['btn_enable'].'" />'); + ptln(' </fieldset>'); + + // ptln(' </div>'); + ptln('</form>'); + } + + ptln('</div>'); + } + + function html_pluginlist() { + global $ID; + global $plugin_protected; + + foreach ($this->manager->plugin_list as $plugin) { + + $disabled = plugin_isdisabled($plugin); + $protected = in_array($plugin,$plugin_protected); + + $checked = ($disabled) ? '' : ' checked="checked"'; + $check_disabled = ($protected) ? ' disabled="disabled"' : ''; + + // determine display class(es) + $class = array(); + if (in_array($plugin, $this->downloaded)) $class[] = 'new'; + if ($disabled) $class[] = 'disabled'; + if ($protected) $class[] = 'protected'; + + $class = count($class) ? ' class="'.join(' ', $class).'"' : ''; + + ptln(' <fieldset'.$class.'>'); + ptln(' <legend>'.$plugin.'</legend>'); + ptln(' <input type="checkbox" class="enable" name="enabled[]" value="'.$plugin.'"'.$checked.$check_disabled.' />'); + ptln(' <h3 class="legend">'.$plugin.'</h3>'); + + $this->html_button($plugin, 'info', false, 6); + if (in_array('settings', $this->manager->functions)) { + $this->html_button($plugin, 'settings', !@file_exists(DOKU_PLUGIN.$plugin.'/settings.php'), 6); + } + $this->html_button($plugin, 'update', !$this->plugin_readlog($plugin, 'url'), 6); + $this->html_button($plugin, 'delete', $protected, 6); + + ptln(' </fieldset>'); + } + } + + function html_button($plugin, $btn, $disabled=false, $indent=0) { + $disabled = ($disabled) ? 'disabled="disabled"' : ''; + ptln('<input type="submit" class="button" '.$disabled.' name="fn['.$btn.']['.$plugin.']" value="'.$this->lang['btn_'.$btn].'" />',$indent); + } + + /** + * Refresh plugin list + */ + function refresh() { + global $MSG,$config_cascade; + + //are there any undisplayed messages? keep them in session for display + if (isset($MSG) && count($MSG)){ + //reopen session, store data and close session again + @session_start(); + $_SESSION[DOKU_COOKIE]['msg'] = $MSG; + session_write_close(); + } + + // expire dokuwiki caches + // touching local.php expires wiki page, JS and CSS caches + @touch(reset($config_cascade['main']['local'])); + + // update latest plugin date - FIXME + header('Location: '.wl($ID).'?do=admin&page=plugin'); + exit(); + } + + // log + function plugin_writelog($plugin, $cmd, $data) { + + $file = DOKU_PLUGIN.$plugin.'/manager.dat'; + + switch ($cmd) { + case 'install' : + $url = $data[0]; + $date = date('r'); + if (!$fp = @fopen($file, 'w')) return; + fwrite($fp, "installed=$date\nurl=$url\n"); + fclose($fp); + break; + + case 'update' : + $date = date('r'); + if (!$fp = @fopen($file, 'a')) return; + fwrite($fp, "updated=$date\n"); + fclose($fp); + break; + } + } + + function plugin_readlog($plugin, $field) { + static $log = array(); + $file = DOKU_PLUGIN.plugin_directory($plugin).'/manager.dat'; + + if (!isset($log[$plugin])) { + $tmp = @file_get_contents($file); + if (!$tmp) return ''; + $log[$plugin] = & $tmp; + } + + if ($field == 'ALL') { + return $log[$plugin]; + } + + $match = array(); + if (preg_match_all('/'.$field.'=(.*)$/m',$log[$plugin], $match)) + return implode("\n", $match[1]); + + return ''; + } + + /** + * delete, with recursive sub-directory support + */ + function dir_delete($path) { + if (!is_string($path) || $path == "") return false; + + if (is_dir($path)) { + if (!$dh = @opendir($path)) return false; + + while ($f = readdir($dh)) { + if ($f == '..' || $f == '.') continue; + $this->dir_delete("$path/$f"); + } + + closedir($dh); + return @rmdir($path); + } else { + return @unlink($path); + } + + return false; + } + + +} diff --git a/lib/plugins/plugin/classes/ap_update.class.php b/lib/plugins/plugin/classes/ap_update.class.php new file mode 100644 index 0000000000000000000000000000000000000000..9ac002991470cf2cfe4d226bd0192e389493b055 --- /dev/null +++ b/lib/plugins/plugin/classes/ap_update.class.php @@ -0,0 +1,38 @@ +<?php + +class ap_update extends ap_manage { + + var $overwrite = true; + + function process() { + global $lang; + + $plugin_url = $this->plugin_readlog($this->plugin, 'url'); + $this->download($plugin_url, $this->overwrite); + return ''; + } + + function html() { + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['updating'].'</h2>'); + + if ($this->manager->error) { + ptln('<div class="error">'.str_replace("\n","<br />", $this->manager->error).'</div>'); + } else if (count($this->downloaded) == 1) { + ptln('<p>'.sprintf($this->lang['updated'],$this->downloaded[0]).'</p>'); + } else if (count($this->downloaded)) { // more than one plugin in the download + ptln('<p>'.$this->lang['updates'].'</p>'); + ptln('<ul>'); + foreach ($this->downloaded as $plugin) { + ptln('<li><div class="li">'.$plugin.'</div></li>',2); + } + ptln('</ul>'); + } else { // none found in download + ptln('<p>'.$this->lang['update_none'].'</p>'); + } + ptln('</div>'); + } +} +