# HG changeset patch # User Dan # Date 1207795057 14400 # Node ID 21e11f564463fa2443b7e85bf217f2d09622e6d2 # Parent b2fb50d572c7895b253b39eab6a51377a778f839 (Hopefully) finished new plugin manager and implemented the utilization of it. Still HIGHLY experimental. diff -r b2fb50d572c7 -r 21e11f564463 includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Wed Apr 09 19:27:02 2008 -0400 +++ b/includes/clientside/static/ajax.js Wed Apr 09 22:37:37 2008 -0400 @@ -1568,6 +1568,12 @@ return true; } action = action.replace(/_confirm$/, ''); + // white-out the plugin info box + if ( btnobj ) + { + var td = btnobj.parentNode.parentNode.parentNode.parentNode; + var blackbox = whiteOutElement(td); + } var request = toJSONString({ mode: action, plugin: plugin_filename @@ -1576,52 +1582,53 @@ { if ( ajax.readyState == 4 && ajax.status == 200 ) { - if ( ajax.responseText == 'good' ) + var response = String(ajax.responseText + ''); + if ( response.substr(0, 1) != '{' ) { - ajaxPage( namespace_list['Admin'] + 'PluginManager' ); + handle_invalid_json(response); + return false; } - else + response = parseJSON(response); + if ( response.success ) { - var response = String(ajax.responseText + ''); - if ( response.substr(0, 1) != '{' ) + if ( blackbox ) { - handle_invalid_json(response); - return false; + blackbox.parentNode.removeChild(blackbox); } - response = parseJSON(response); - if ( response.mode != 'error' ) + ajaxPage( namespace_list['Admin'] + 'PluginManager' ); + return true; + } + // wait for fade effect to finish its run + setTimeout(function() { - console.debug(response); - return false; - } - // wait for fade effect to finish its run - setTimeout(function() - { - miniPrompt(function(div) + miniPrompt(function(div) + { + if ( blackbox ) { - var txtholder = document.createElement('div'); - txtholder.style.textAlign = 'center'; - txtholder.appendChild(document.createTextNode(response.error)); - txtholder.appendChild(document.createElement('br')); - txtholder.appendChild(document.createElement('br')); - - // close button - var btn_cancel = document.createElement('a'); - btn_cancel.className = 'abutton abutton_red'; - btn_cancel.href = '#'; - btn_cancel.appendChild(document.createTextNode($lang.get('etc_ok'))); - - txtholder.appendChild(btn_cancel); - div.appendChild(txtholder); - - btn_cancel.onclick = function() - { - miniPromptDestroy(this); - return false; - } - }); - }, 750); - } + blackbox.parentNode.removeChild(blackbox); + } + var txtholder = document.createElement('div'); + txtholder.style.textAlign = 'center'; + txtholder.appendChild(document.createTextNode(response.error)); + txtholder.appendChild(document.createElement('br')); + txtholder.appendChild(document.createElement('br')); + + // close button + var btn_cancel = document.createElement('a'); + btn_cancel.className = 'abutton abutton_red'; + btn_cancel.href = '#'; + btn_cancel.appendChild(document.createTextNode($lang.get('etc_ok'))); + + txtholder.appendChild(btn_cancel); + div.appendChild(txtholder); + + btn_cancel.onclick = function() + { + miniPromptDestroy(this); + return false; + } + }); + }, 750); } }); } diff -r b2fb50d572c7 -r 21e11f564463 includes/common.php --- a/includes/common.php Wed Apr 09 19:27:02 2008 -0400 +++ b/includes/common.php Wed Apr 09 22:37:37 2008 -0400 @@ -308,8 +308,8 @@ // Load plugins from common because we can't give plugins full abilities in object context foreach ( $plugins->load_list as $f ) { - if ( file_exists($f) ) - include_once $f; + if ( file_exists(ENANO_ROOT . '/plugins/' . $f) ) + include_once ENANO_ROOT . '/plugins/' . $f; } profiler_log('Loaded plugins'); diff -r b2fb50d572c7 -r 21e11f564463 includes/plugins.php --- a/includes/plugins.php Wed Apr 09 19:27:02 2008 -0400 +++ b/includes/plugins.php Wed Apr 09 22:37:37 2008 -0400 @@ -63,52 +63,35 @@ function loadAll() { + global $db, $session, $paths, $template, $plugins; // Common objects $dir = ENANO_ROOT.'/plugins/'; - $this->load_list = Array(); - - $plugins = Array(); + $this->load_list = $this->system_plugins; + $q = $db->sql_query('SELECT plugin_filename, plugin_version FROM ' . table_prefix . 'plugins WHERE plugin_flags & ~' . PLUGIN_DISABLED . ' = plugin_flags;'); + if ( !$q ) + $db->_die(); - // Open a known directory, and proceed to read its contents + while ( $row = $db->fetchrow() ) + { + $this->load_list[] = $row['plugin_filename']; + } + + $this->loaded_plugins = $this->get_plugin_list($this->load_list); - if (is_dir($dir)) + // check for out-of-date plugins + foreach ( $this->load_list as $i => $plugin ) { - if ($dh = opendir($dir)) + if ( in_array($plugin, $this->system_plugins) ) + continue; + if ( $this->loaded_plugins[$plugin]['status'] & PLUGIN_OUTOFDATE ) { - while (($file = readdir($dh)) !== false) - { - if(preg_match('#^(.*?)\.php$#is', $file)) - { - if(getConfig('plugin_'.$file) == '1' || in_array($file, $this->system_plugins)) - { - $this->load_list[] = $dir . $file; - $plugid = substr($file, 0, strlen($file)-4); - $f = @file_get_contents($dir . $file); - if ( empty($f) ) - continue; - $f = explode("\n", $f); - $f = array_slice($f, 2, 7); - $f[0] = substr($f[0], 13); - $f[1] = substr($f[1], 12); - $f[2] = substr($f[2], 13); - $f[3] = substr($f[3], 8 ); - $f[4] = substr($f[4], 9 ); - $f[5] = substr($f[5], 12); - $plugins[$plugid] = Array(); - $plugins[$plugid]['name'] = $f[0]; - $plugins[$plugid]['uri'] = $f[1]; - $plugins[$plugid]['desc'] = $f[2]; - $plugins[$plugid]['auth'] = $f[3]; - $plugins[$plugid]['vers'] = $f[4]; - $plugins[$plugid]['aweb'] = $f[5]; - } - } - } - closedir($dh); + // it's out of date, don't load + unset($this->load_list[$i]); + unset($this->loaded_plugins[$plugin]); } } - $this->loaded_plugins = $plugins; - //die('
'.htmlspecialchars(print_r($plugins, true)).'
'); + + $this->load_list = array_unique($this->load_list); } /** @@ -215,7 +198,6 @@ . '#m'; // Match out all blocks - $results = preg_match_all($regexp, $contents, $blocks); $return = array(); @@ -256,6 +238,549 @@ } return $return; } + + /** + * Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins + * on the site. + * @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned. + * @return array + */ + + function get_plugin_list($restrict = null) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Scan all plugins + $plugin_list = array(); + + if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) + { + while ( $dh = @readdir($dirh) ) + { + if ( !preg_match('/\.php$/i', $dh) ) + continue; + + if ( is_array($restrict) ) + if ( !in_array($dh, $restrict) ) + continue; + + $fullpath = ENANO_ROOT . "/plugins/$dh"; + // it's a PHP file, attempt to read metadata + // pass 1: try to read a !info block + $blockdata = $this->parse_plugin_blocks($fullpath, 'info'); + if ( empty($blockdata) ) + { + // no !info block, check for old header + $fh = @fopen($fullpath, 'r'); + if ( !$fh ) + // can't read, bail out + continue; + $plugin_data = array(); + for ( $i = 0; $i < 8; $i++ ) + { + $plugin_data[] = @fgets($fh, 8096); + } + // close our file handle + fclose($fh); + // is the header correct? + if ( trim($plugin_data[0]) != ' $value ) + { + $plugin_meta[ strtolower($key) ] = $value; + } + } + if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) + { + // parsing didn't work. + continue; + } + // check for required keys + $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri'); + foreach ( $required_keys as $key ) + { + if ( !isset($plugin_meta[$key]) ) + // not set, skip this plugin + continue 2; + } + // decide if it's a system plugin + $plugin_meta['system plugin'] = in_array($dh, $this->system_plugins); + // reset installed variable + $plugin_meta['installed'] = false; + $plugin_meta['status'] = 0; + // all checks passed + $plugin_list[$dh] = $plugin_meta; + } + } + // gather info about installed plugins + $q = $db->sql_query('SELECT plugin_id, plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;'); + if ( !$q ) + $db->_die(); + while ( $row = $db->fetchrow() ) + { + if ( !isset($plugin_list[ $row['plugin_filename'] ]) ) + { + // missing plugin file, don't report (for now) + continue; + } + $filename =& $row['plugin_filename']; + $plugin_list[$filename]['installed'] = true; + $plugin_list[$filename]['status'] = PLUGIN_INSTALLED; + $plugin_list[$filename]['plugin id'] = $row['plugin_id']; + if ( $row['plugin_version'] != $plugin_list[$filename]['version'] ) + { + $plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE; + $plugin_list[$filename]['version installed'] = $row['plugin_version']; + } + if ( $row['plugin_flags'] & PLUGIN_DISABLED ) + { + $plugin_list[$filename]['status'] |= PLUGIN_DISABLED; + } + } + $db->free_result(); + + // sort it all out by filename + ksort($plugin_list); + + // done + return $plugin_list; + } + + /** + * Installs a plugin. + * @param string Filename of plugin. + * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. + * @return array JSON-formatted but not encoded response + */ + + function install_plugin($filename, $plugin_list = null) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if ( !$plugin_list ) + $plugin_list = $this->get_plugin_list(); + + // we're gonna need this + require_once ( ENANO_ROOT . '/includes/sql_parse.php' ); + + switch ( true ): case true: + + // is the plugin in the directory and awaiting installation? + if ( !isset($plugin_list[$filename]) || ( + isset($plugin_list[$filename]) && $plugin_list[$filename]['installed'] + )) + { + $return = array( + 'mode' => 'error', + 'error' => 'Invalid plugin specified.', + 'debug' => $filename + ); + break; + } + + $dataset =& $plugin_list[$filename]; + + // load up the installer schema + $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'install' ); + + $sql = array(); + if ( !empty($schema) ) + { + // parse SQL + $parser = new SQL_Parser($schema[0]['value'], true); + $parser->assign_vars(array( + 'TABLE_PREFIX' => table_prefix + )); + $sql = $parser->parse(); + } + + // schema is final, check queries + foreach ( $sql as $query ) + { + if ( !$db->check_query($query) ) + { + // aww crap, a query is bad + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_bad_query'), + ); + break 2; + } + } + + // this is it, perform installation + foreach ( $sql as $query ) + { + if ( substr($query, 0, 1) == '@' ) + { + $query = substr($query, 1); + $db->sql_query($query); + } + else + { + if ( !$db->sql_query($query) ) + $db->die_json(); + } + } + + // register plugin + $version_db = $db->escape($dataset['version']); + $filename_db = $db->escape($filename); + $flags = PLUGIN_INSTALLED; + + $q = $db->sql_query('INSERT INTO ' . table_prefix . "plugins ( plugin_version, plugin_filename, plugin_flags )\n" + . " VALUES ( '$version_db', '$filename_db', $flags );"); + if ( !$q ) + $db->die_json(); + + $return = array( + 'success' => true + ); + + endswitch; + + return $return; + } + + /** + * Uninstalls a plugin, removing it completely from the database and calling any custom uninstallation code the plugin specifies. + * @param string Filename of plugin. + * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. + * @return array JSON-formatted but not encoded response + */ + + function uninstall_plugin($filename, $plugin_list = null) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if ( !$plugin_list ) + $plugin_list = $this->get_plugin_list(); + + // we're gonna need this + require_once ( ENANO_ROOT . '/includes/sql_parse.php' ); + + switch ( true ): case true: + + // is the plugin in the directory and already installed? + if ( !isset($plugin_list[$filename]) || ( + isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed'] + )) + { + $return = array( + 'mode' => 'error', + 'error' => 'Invalid plugin specified.', + ); + break; + } + // get plugin id + $dataset =& $plugin_list[$filename]; + if ( empty($dataset['plugin id']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'Couldn\'t retrieve plugin ID.', + ); + break; + } + + // load up the installer schema + $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'uninstall' ); + + $sql = array(); + if ( !empty($schema) ) + { + // parse SQL + $parser = new SQL_Parser($schema[0]['value'], true); + $parser->assign_vars(array( + 'TABLE_PREFIX' => table_prefix + )); + $sql = $parser->parse(); + } + + // schema is final, check queries + foreach ( $sql as $query ) + { + if ( !$db->check_query($query) ) + { + // aww crap, a query is bad + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_bad_query'), + ); + break 2; + } + } + + // this is it, perform uninstallation + foreach ( $sql as $query ) + { + if ( substr($query, 0, 1) == '@' ) + { + $query = substr($query, 1); + $db->sql_query($query); + } + else + { + if ( !$db->sql_query($query) ) + $db->die_json(); + } + } + + // deregister plugin + $q = $db->sql_query('DELETE FROM ' . table_prefix . "plugins WHERE plugin_id = {$dataset['plugin id']};"); + if ( !$q ) + $db->die_json(); + + $return = array( + 'success' => true + ); + + endswitch; + + return $return; + } + + /** + * Very intelligently upgrades a plugin to the version specified in the filesystem. + * @param string Filename of plugin. + * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. + * @return array JSON-formatted but not encoded response + */ + + function upgrade_plugin($filename, $plugin_list = null) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if ( !$plugin_list ) + $plugin_list = $this->get_plugin_list(); + + // we're gonna need this + require_once ( ENANO_ROOT . '/includes/sql_parse.php' ); + + switch ( true ): case true: + + // is the plugin in the directory and already installed? + if ( !isset($plugin_list[$filename]) || ( + isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed'] + )) + { + $return = array( + 'mode' => 'error', + 'error' => 'Invalid plugin specified.', + ); + break; + } + // get plugin id + $dataset =& $plugin_list[$filename]; + if ( empty($dataset['plugin id']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'Couldn\'t retrieve plugin ID.', + ); + break; + } + + // + // Here we go with the main upgrade process. This is the same logic that the + // Enano official upgrader uses, in fact it's the same SQL parser. We need + // list of all versions of the plugin to continue, though. + // + + if ( !isset($dataset['version list']) || ( isset($dataset['version list']) && !is_array($dataset['version list']) ) ) + { + // no version list - update the version number but leave the rest alone + $version = $db->escape($dataset['version']); + $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};"); + if ( !$q ) + $db->die_json(); + + // send an error and notify the user even though it was technically a success + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_not_supported'), + ); + break; + } + + // build target list + $versions = $dataset['version list']; + $indices = array_flip($versions); + $installed = $dataset['version installed']; + + // is the current version upgradeable? + if ( !isset($indices[$installed]) ) + { + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_bad_version'), + ); + break; + } + + // does the plugin support upgrading to its own version? + if ( !isset($indices[$installed]) ) + { + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_bad_target_version'), + ); + break; + } + + // list out which versions to do + $index_start = @$indices[$installed] + 1; + $index_stop = @$indices[$dataset['version']]; + + // Are we trying to go backwards? + if ( $index_stop <= $index_start ) + { + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_to_older'), + ); + break; + } + + // build the list of version sets + $ver_previous = $installed; + $targets = array(); + for ( $i = $index_start; $i <= $index_stop; $i++ ) + { + $targets[] = array($ver_previous, $versions[$i]); + $ver_previous = $versions[$i]; + } + + // parse out upgrade sections in plugin file + $plugin_blocks = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'upgrade' ); + $sql_blocks = array(); + foreach ( $plugin_blocks as $block ) + { + if ( !isset($block['from']) || !isset($block['to']) ) + { + continue; + } + $key = "{$block['from']} TO {$block['to']}"; + $sql_blocks[$key] = $block['value']; + } + + // do version list check + // for now we won't fret if a specific version set isn't found, we'll just + // not do that version and assume there were no DB changes. + foreach ( $targets as $i => $target ) + { + list($from, $to) = $target; + $key = "$from TO $to"; + if ( !isset($sql_blocks[$key]) ) + { + unset($targets[$i]); + } + } + $targets = array_values($targets); + + // parse and finalize schema + $schema = array(); + foreach ( $targets as $i => $target ) + { + list($from, $to) = $target; + $key = "$from TO $to"; + try + { + $parser = new SQL_Parser($sql_blocks[$key], true); + } + catch ( Exception $e ) + { + $return = array( + 'mode' => 'error', + 'error' => 'SQL parser init exception', + 'debug' => "$e" + ); + break 2; + } + $parser->assign_vars(array( + 'TABLE_PREFIX' => table_prefix + )); + $parsed = $parser->parse(); + foreach ( $parsed as $query ) + { + $schema[] = $query; + } + } + + // schema is final, check queries + foreach ( $schema as $query ) + { + if ( !$db->check_query($query) ) + { + // aww crap, a query is bad + $return = array( + 'mode' => 'error', + 'error' => $lang->get('acppm_err_upgrade_bad_query'), + ); + break 2; + } + } + + // this is it, perform upgrade + foreach ( $schema as $query ) + { + if ( substr($query, 0, 1) == '@' ) + { + $query = substr($query, 1); + $db->sql_query($query); + } + else + { + if ( !$db->sql_query($query) ) + $db->die_json(); + } + } + + // update version number + $version = $db->escape($dataset['version']); + $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};"); + if ( !$q ) + $db->die_json(); + + // all done :-) + $return = array( + 'success' => true + ); + + endswitch; + + return $return; + } } ?> diff -r b2fb50d572c7 -r 21e11f564463 includes/sql_parse.php --- a/includes/sql_parse.php Wed Apr 09 19:27:02 2008 -0400 +++ b/includes/sql_parse.php Wed Apr 09 22:37:37 2008 -0400 @@ -50,11 +50,12 @@ /** * Constructor. * @param string If this contains newlines, it will be treated as the target SQL. If not, will be treated as a filename. + * @param string If true, force as raw SQL, i.e. don't treat as a filename no matter what */ - public function __construct($sql) + public function __construct($sql, $force_file = false) { - if ( strpos($sql, "\n") ) + if ( strpos($sql, "\n") || $force_file ) { $this->sql_string = $sql; } diff -r b2fb50d572c7 -r 21e11f564463 language/english/admin.json --- a/language/english/admin.json Wed Apr 09 19:27:02 2008 -0400 +++ b/language/english/admin.json Wed Apr 09 22:37:37 2008 -0400 @@ -400,8 +400,18 @@ btn_upgrade: 'Upgrade', btn_uninstall: 'Uninstall', - msg_confirm_uninstall: 'Please confirm that you want to uninstall this plugin and that it doesn\'t provide any shared functions that other plugins depend on.', + msg_confirm_uninstall: 'Uninstalling this plugin may cause the loss of data that was created with it. You should only uninstall a plugin if you are certain you\'ll have no further use for it; in fact, you don\'t even need to uninstall a plugin if you\'re deleting it from the filesystem.', msg_confirm_install: 'Plugins are not supported by the Enano project and could harm your site if malicious. You should only install plugins from sources that you trust.', + + err_upgrade_not_supported: 'This plugin doesn\'t support automatic upgrades. The version number has been updated so the plugin will be re-enabled, but you should check the plugin file to see if the author provided instructions for finishing the upgrade.', + err_upgrade_bad_version: 'This plugin cannot be upgraded because you are running a version of the plugin that is not listed in the plugin\'s version list.', + err_upgrade_bad_target_version: 'This plugin cannot be upgraded because it does not support its own version. Please contact the author and ask them to fix this.', + err_upgrade_to_older: 'You are trying to upgrade to an older release of this plugin. This is unsupported and must be done manually.', + err_upgrade_bad_query: 'There is a problem with one of the SQL queries the plugin is trying to make.', + + msg_old_entries_title: 'Import old plugin installation data', + msg_old_entries_body: 'There is still data from the old plugin structure in your database. You can import this to the new structure automatically using the button below.', + btn_import_old: 'Import old plugin settings', }, acppm: { heading_main: 'Edit page properties', diff -r b2fb50d572c7 -r 21e11f564463 plugins/admin/PluginManager.php --- a/plugins/admin/PluginManager.php Wed Apr 09 19:27:02 2008 -0400 +++ b/plugins/admin/PluginManager.php Wed Apr 09 22:37:37 2008 -0400 @@ -26,7 +26,7 @@ * * The format for the special comment blocks is: - /**!blocktype( param1 = "value1" [ param2 = "value2" ... ] )** + /**!blocktype( param1 = "value1"; [ param2 = "value2"; ... ] )** ... block content ... @@ -58,7 +58,9 @@ /**!language** { + // each entry at this level should be an ISO-639-1 language code. eng: { + // from here on in is the standard langauge file format categories: [ 'meta', 'foo', 'bar' ], strings: { meta: { @@ -86,13 +88,17 @@ * And finally, the format for upgrade schemas: - /**!upgrade from = "0.1-alpha1" to = "0.1-alpha2" ** + /**!upgrade from = "0.1-alpha1"; to = "0.1-alpha2"; ** **!* / + * As a courtesy to your users, we ask that you also include an "uninstall" block that reverses any changes your plugin makes + * to the database upon installation. The syntax is identical to that of the install block. + * * Remember that upgrades will always be done incrementally, so if the user is upgrading 0.1-alpha2 to 0.1, Enano's plugin * engine will run the 0.1-alpha2 to 0.1-beta1 upgrader, then the 0.1-beta1 to 0.1 upgrader, going by the versions listed in - * the example metadata block above. + * the example metadata block above. As with the standard Enano installer, prefixing a query with '@' will cause it to be + * performed "blindly", e.g. not checked for errors. * * All of this information is effective as of Enano 1.1.4. */ @@ -111,6 +117,8 @@ return; } + $plugin_list = $plugins->get_plugin_list(); + // Are we processing an AJAX request from the smartform? if ( $paths->getParam(0) == 'action.json' ) { @@ -133,6 +141,120 @@ { switch ( $request['mode'] ) { + case 'install': + // did they specify a plugin to operate on? + if ( !isset($request['plugin']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'No plugin specified.', + ); + break; + } + + $return = $plugins->install_plugin($request['plugin'], $plugin_list); + break; + case 'upgrade': + // did they specify a plugin to operate on? + if ( !isset($request['plugin']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'No plugin specified.', + ); + break; + } + + $return = $plugins->upgrade_plugin($request['plugin'], $plugin_list); + break; + case 'uninstall': + // did they specify a plugin to operate on? + if ( !isset($request['plugin']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'No plugin specified.', + ); + break; + } + + $return = $plugins->uninstall_plugin($request['plugin'], $plugin_list); + break; + case 'disable': + case 'enable': + $flags_col = ( $request['mode'] == 'disable' ) ? + "plugin_flags | " . PLUGIN_DISABLED : + "plugin_flags & ~" . PLUGIN_DISABLED; + // did they specify a plugin to operate on? + if ( !isset($request['plugin']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'No plugin specified.', + ); + break; + } + // is the plugin in the directory and already installed? + if ( !isset($plugin_list[$request['plugin']]) || ( + isset($plugin_list[$request['plugin']]) && !$plugin_list[$request['plugin']]['installed'] + )) + { + $return = array( + 'mode' => 'error', + 'error' => 'Invalid plugin specified.', + ); + break; + } + // get plugin id + $dataset =& $plugin_list[$request['plugin']]; + if ( empty($dataset['plugin id']) ) + { + $return = array( + 'mode' => 'error', + 'error' => 'Couldn\'t retrieve plugin ID.', + ); + break; + } + // perform update + $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = $flags_col WHERE plugin_id = {$dataset['plugin id']};"); + if ( !$q ) + $db->die_json(); + + $return = array( + 'success' => true + ); + break; + case 'import': + // import all of the plugin_* config entries + $q = $db->sql_query('SELECT config_name, config_value FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';"); + if ( !$q ) + $db->die_json(); + + while ( $row = $db->fetchrow($q) ) + { + $plugin_filename = preg_replace('/^plugin_/', '', $row['config_name']); + if ( isset($plugin_list[$plugin_filename]) && !@$plugin_list[$plugin_filename]['installed'] ) + { + $return = $plugins->install_plugin($plugin_filename, $plugin_list); + if ( !$return['success'] ) + break 2; + if ( $row['config_value'] == '0' ) + { + $fn_db = $db->escape($plugin_filename); + $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = plugin_flags | " . PLUGIN_DISABLED . " WHERE plugin_filename = '$fn_db';"); + if ( !$q ) + $db->die_json(); + } + } + } + $db->free_result($q); + + $q = $db->sql_query('DELETE FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';"); + if ( !$q ) + $db->die_json(); + + $return = array('success' => true); + break; default: // The requested action isn't something this script knows how to do $return = array( @@ -178,121 +300,7 @@ // Not a JSON request, output normal HTML interface // - // Scan all plugins - $plugin_list = array(); - - if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) - { - while ( $dh = @readdir($dirh) ) - { - if ( !preg_match('/\.php$/i', $dh) ) - continue; - $fullpath = ENANO_ROOT . "/plugins/$dh"; - // it's a PHP file, attempt to read metadata - // pass 1: try to read a !info block - $blockdata = $plugins->parse_plugin_blocks($fullpath, 'info'); - if ( empty($blockdata) ) - { - // no !info block, check for old header - $fh = @fopen($fullpath, 'r'); - if ( !$fh ) - // can't read, bail out - continue; - $plugin_data = array(); - for ( $i = 0; $i < 8; $i++ ) - { - $plugin_data[] = @fgets($fh, 8096); - } - // close our file handle - fclose($fh); - // is the header correct? - if ( trim($plugin_data[0]) != ' $value ) - { - $plugin_meta[ strtolower($key) ] = $value; - } - } - if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) - { - // parsing didn't work. - continue; - } - // check for required keys - $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri'); - foreach ( $required_keys as $key ) - { - if ( !isset($plugin_meta[$key]) ) - // not set, skip this plugin - continue 2; - } - // decide if it's a system plugin - $plugin_meta['system plugin'] = in_array($dh, $plugins->system_plugins); - // reset installed variable - $plugin_meta['installed'] = false; - $plugin_meta['status'] = 0; - // all checks passed - $plugin_list[$dh] = $plugin_meta; - } - } - // gather info about installed plugins - $q = $db->sql_query('SELECT plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;'); - if ( !$q ) - $db->_die(); - while ( $row = $db->fetchrow() ) - { - if ( !isset($plugin_list[ $row['plugin_filename'] ]) ) - { - // missing plugin file, don't report (for now) - continue; - } - $filename =& $row['plugin_filename']; - $plugin_list[$filename]['installed'] = true; - $plugin_list[$filename]['status'] = PLUGIN_INSTALLED; - if ( $row['plugin_version'] != $plugin_list[$filename]['version'] ) - { - $plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE; - $plugin_list[$filename]['version installed'] = $row['plugin_version']; - } - if ( $row['plugin_flags'] & PLUGIN_DISABLED ) - { - $plugin_list[$filename]['status'] |= PLUGIN_DISABLED; - } - } - $db->free_result(); - - // sort it all out by filename - ksort($plugin_list); - // start printing things out - acp_start_form(); ?>
@@ -324,7 +332,7 @@ { $color = '_red'; $status = $lang->get('acppl_lbl_status_need_upgrade'); - $buttons = 'uninstall|update'; + $buttons = 'uninstall|upgrade'; } else if ( $data['installed'] && $data['status'] & PLUGIN_DISABLED ) { @@ -389,7 +397,7 @@
$status
-
$plugin_basics
@@ -410,5 +418,18 @@
'; + // are there still old style plugin entries? + $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';"); + if ( !$q ) + $db->_die(); + + $count = $db->numrows(); + $db->free_result($q); + + if ( $count > 0 ) + { + echo '

' . $lang->get('acppl_msg_old_entries_title') . '

'; + echo '

' . $lang->get('acppl_msg_old_entries_body') . '

'; + echo '

' . $lang->get('acppl_btn_import_old') . '

'; + } }