# HG changeset patch # User Dan # Date 1207783622 14400 # Node ID b2fb50d572c7895b253b39eab6a51377a778f839 # Parent 3f2dfdb99be4cfb85e16199f569204401cf810dc New plugin manager half-implemented. Most of the UI/frontend code is done. Moved sql_parse.php to /includes/ to allow use after installation - TODO: check installer, etc. for breakage diff -r 3f2dfdb99be4 -r b2fb50d572c7 includes/clientside/css/enano-shared.css --- a/includes/clientside/css/enano-shared.css Tue Apr 08 20:32:30 2008 -0400 +++ b/includes/clientside/css/enano-shared.css Wed Apr 09 19:27:02 2008 -0400 @@ -783,3 +783,23 @@ position: relative; top: 10px; } + +/* pseudo-buttons made with tags */ +.abutton { + padding: 3px 5px; + background-color: #f0f0f0; + cursor: pointer; + margin: 0 3px; +} + +.abutton:hover { + color: #f0f0f0 !important; +} + +.abutton_green { color: #00aa00 !important; } +.abutton_green:hover { background-color: #00aa00; } +.abutton_blue { color: #0000aa !important; } +.abutton_blue:hover { background-color: #0000aa; } +.abutton_red { color: #aa0000 !important; } +.abutton_red:hover { background-color: #aa0000; } + diff -r 3f2dfdb99be4 -r b2fb50d572c7 includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Tue Apr 08 20:32:30 2008 -0400 +++ b/includes/clientside/static/ajax.js Wed Apr 09 19:27:02 2008 -0400 @@ -1517,3 +1517,111 @@ }); } +function ajaxPluginAction(action, plugin_filename, btnobj) +{ + // if installing or uninstalling, confirm + if ( action == 'install' || action == 'uninstall' ) + { + var prompt = miniPrompt(function(div) + { + var txtholder = document.createElement('div'); + txtholder.style.textAlign = 'center'; + txtholder.appendChild(document.createTextNode($lang.get('acppl_msg_confirm_' + action))); + txtholder.appendChild(document.createElement('br')); + txtholder.appendChild(document.createElement('br')); + + // create buttons + var btn_go = document.createElement('a'); + btn_go.className = 'abutton abutton_red'; + btn_go.href = '#'; + btn_go._action = action; + btn_go._filename = plugin_filename; + btn_go._button = btnobj; + btn_go.appendChild(document.createTextNode($lang.get('acppl_btn_' + action))); + btn_go.style.fontWeight = 'bold'; + txtholder.appendChild(btn_go); + + // space + txtholder.appendChild(document.createTextNode(' ')); + + // cancel + var btn_cancel = document.createElement('a'); + btn_cancel.className = 'abutton abutton_blue'; + btn_cancel.href = '#'; + btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel'))); + + txtholder.appendChild(btn_cancel); + div.appendChild(txtholder); + + btn_go.onclick = function() + { + ajaxPluginAction(this._action + '_confirm', this._filename, this._button); + miniPromptDestroy(this); + return false; + } + btn_cancel.onclick = function() + { + miniPromptDestroy(this); + return false; + } + }); + return true; + } + action = action.replace(/_confirm$/, ''); + var request = toJSONString({ + mode: action, + plugin: plugin_filename + }); + ajaxPost(makeUrlNS('Admin', 'PluginManager/action.json'), 'r=' + ajaxEscape(request), function() + { + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + if ( ajax.responseText == 'good' ) + { + ajaxPage( namespace_list['Admin'] + 'PluginManager' ); + } + else + { + var response = String(ajax.responseText + ''); + if ( response.substr(0, 1) != '{' ) + { + handle_invalid_json(response); + return false; + } + response = parseJSON(response); + if ( response.mode != 'error' ) + { + console.debug(response); + return false; + } + // wait for fade effect to finish its run + setTimeout(function() + { + miniPrompt(function(div) + { + 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 3f2dfdb99be4 -r b2fb50d572c7 includes/constants.php --- a/includes/constants.php Tue Apr 08 20:32:30 2008 -0400 +++ b/includes/constants.php Wed Apr 09 19:27:02 2008 -0400 @@ -87,6 +87,11 @@ define('PM_TRASH', 32); define('PM_DELIVERED', 64); +// Plugin status +define('PLUGIN_INSTALLED', 1); +define('PLUGIN_DISABLED', 2); +define('PLUGIN_OUTOFDATE', 4); + // Other stuff define('MAX_PMS_PER_BATCH', 7); // The maximum number of users that users can send PMs to in one go; restriction does not apply to users with mod_misc rights diff -r 3f2dfdb99be4 -r b2fb50d572c7 includes/sql_parse.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/sql_parse.php Wed Apr 09 19:27:02 2008 -0400 @@ -0,0 +1,149 @@ +sql_string = $sql; + } + else + { + if ( file_exists($sql) ) + { + $this->sql_string = @file_get_contents($sql); + if ( empty($this->sql_string) ) + { + throw new Exception('SQL file is blank or permissions are bad'); + } + } + else + { + throw new Exception('SQL file doesn\'t exist'); + } + } + $this->sql_array = false; + $this->tpl_strings = array(); + } + + /** + * Sets template variables. + * @param array Associative array of template variables to assign + */ + + public function assign_vars($vars) + { + if ( !is_array($vars) ) + return false; + $this->tpl_strings = array_merge($this->tpl_strings, $vars); + } + + /** + * Internal function to parse the SQL. + * @access private + */ + + private function parse_sql() + { + $this->sql_array = $this->sql_string; + foreach ( $this->tpl_strings as $key => $value ) + { + $this->sql_array = str_replace("{{{$key}}}", $value, $this->sql_array); + } + + // Build an array of queries + $this->sql_array = explode("\n", $this->sql_array); + + foreach ( $this->sql_array as $i => $sql ) + { + $query =& $this->sql_array[$i]; + $t = trim($query); + if ( empty($t) || preg_match('/^(\#|--)/i', $t) ) + { + unset($this->sql_array[$i]); + unset($query); + } + } + unset($query); + + $this->sql_array = array_values($this->sql_array); + $this->sql_array = implode("\n", $this->sql_array); + $this->sql_array = explode(";\n", $this->sql_array); + + foreach ( $this->sql_array as $i => $sql ) + { + $query =& $this->sql_array[$i]; + if ( substr($query, ( strlen($query) - 1 ), 1 ) != ';' ) + { + $query .= ';'; + } + } + unset($query); + } + + /** + * Returns the parsed array of SQL queries. + * @param bool Optional. Defaults to false. If true, a parse is performed even if it already happened. + * @return array + */ + + public function parse($force_reparse = false) + { + if ( !$this->sql_array || $force_reparse ) + $this->parse_sql(); + return $this->sql_array; + } +} + +?> diff -r 3f2dfdb99be4 -r b2fb50d572c7 includes/template.php --- a/includes/template.php Tue Apr 08 20:32:30 2008 -0400 +++ b/includes/template.php Wed Apr 09 19:27:02 2008 -0400 @@ -1017,6 +1017,12 @@ $this->load_theme($session->theme, $session->style); } + // I feel awful doing this. + if ( preg_match('/^W3C_Validator/', @$_SERVER['HTTP_USER_AGENT']) ) + { + header('Content-type: application/xhtml+xml'); + } + $headers_sent = true; if(!defined('ENANO_HEADERS_SENT')) define('ENANO_HEADERS_SENT', ''); diff -r 3f2dfdb99be4 -r b2fb50d572c7 install/includes/sql_parse.php --- a/install/includes/sql_parse.php Tue Apr 08 20:32:30 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -sql_string = $sql; - } - else - { - if ( file_exists($sql) ) - { - $this->sql_string = @file_get_contents($sql); - if ( empty($this->sql_string) ) - { - throw new Exception('SQL file is blank or permissions are bad'); - } - } - else - { - throw new Exception('SQL file doesn\'t exist'); - } - } - $this->sql_array = false; - $this->tpl_strings = array(); - } - - /** - * Sets template variables. - * @param array Associative array of template variables to assign - */ - - public function assign_vars($vars) - { - if ( !is_array($vars) ) - return false; - $this->tpl_strings = array_merge($this->tpl_strings, $vars); - } - - /** - * Internal function to parse the SQL. - * @access private - */ - - private function parse_sql() - { - $this->sql_array = $this->sql_string; - foreach ( $this->tpl_strings as $key => $value ) - { - $this->sql_array = str_replace("{{{$key}}}", $value, $this->sql_array); - } - - // Build an array of queries - $this->sql_array = explode("\n", $this->sql_array); - - foreach ( $this->sql_array as $i => $sql ) - { - $query =& $this->sql_array[$i]; - $t = trim($query); - if ( empty($t) || preg_match('/^(\#|--)/i', $t) ) - { - unset($this->sql_array[$i]); - unset($query); - } - } - unset($query); - - $this->sql_array = array_values($this->sql_array); - $this->sql_array = implode("\n", $this->sql_array); - $this->sql_array = explode(";\n", $this->sql_array); - - foreach ( $this->sql_array as $i => $sql ) - { - $query =& $this->sql_array[$i]; - if ( substr($query, ( strlen($query) - 1 ), 1 ) != ';' ) - { - $query .= ';'; - } - } - unset($query); - } - - /** - * Returns the parsed array of SQL queries. - * @param bool Optional. Defaults to false. If true, a parse is performed even if it already happened. - * @return array - */ - - public function parse($force_reparse = false) - { - if ( !$this->sql_array || $force_reparse ) - $this->parse_sql(); - return $this->sql_array; - } -} - -?> diff -r 3f2dfdb99be4 -r b2fb50d572c7 install/includes/stages/database_post.php --- a/install/includes/stages/database_post.php Tue Apr 08 20:32:30 2008 -0400 +++ b/install/includes/stages/database_post.php Wed Apr 09 19:27:02 2008 -0400 @@ -19,7 +19,7 @@ // Start up the DBAL require( ENANO_ROOT . '/includes/dbal.php' ); -require( ENANO_ROOT . '/install/includes/sql_parse.php' ); +require( ENANO_ROOT . '/includes/sql_parse.php' ); $dbal = new $driver(); $db_host =& $_POST['db_host']; $db_user =& $_POST['db_user']; diff -r 3f2dfdb99be4 -r b2fb50d572c7 install/includes/stages/finish.php --- a/install/includes/stages/finish.php Tue Apr 08 20:32:30 2008 -0400 +++ b/install/includes/stages/finish.php Wed Apr 09 19:27:02 2008 -0400 @@ -18,7 +18,7 @@ die(); require ( ENANO_ROOT . '/install/includes/libenanoinstall.php' ); -require ( ENANO_ROOT . '/install/includes/sql_parse.php' ); +require ( ENANO_ROOT . '/includes/sql_parse.php' ); require ( ENANO_ROOT . '/includes/common.php' ); if ( !in_array($dbdriver, $supported_drivers) ) diff -r 3f2dfdb99be4 -r b2fb50d572c7 install/includes/stages/install.php --- a/install/includes/stages/install.php Tue Apr 08 20:32:30 2008 -0400 +++ b/install/includes/stages/install.php Wed Apr 09 19:27:02 2008 -0400 @@ -18,7 +18,7 @@ die(); require ( ENANO_ROOT . '/install/includes/libenanoinstall.php' ); -require ( ENANO_ROOT . '/install/includes/sql_parse.php' ); +require ( ENANO_ROOT . '/includes/sql_parse.php' ); require ( ENANO_ROOT . '/includes/dbal.php' ); require ( ENANO_ROOT . '/config.new.php' ); diff -r 3f2dfdb99be4 -r b2fb50d572c7 install/upgrade.php --- a/install/upgrade.php Tue Apr 08 20:32:30 2008 -0400 +++ b/install/upgrade.php Wed Apr 09 19:27:02 2008 -0400 @@ -28,7 +28,7 @@ @ini_set('display_errors', 'on'); // Load installer files -require_once('includes/sql_parse.php'); +require_once('../includes/sql_parse.php'); require_once('includes/common.php'); require_once('includes/libenanoinstall.php'); diff -r 3f2dfdb99be4 -r b2fb50d572c7 language/english/admin.json --- a/language/english/admin.json Tue Apr 08 20:32:30 2008 -0400 +++ b/language/english/admin.json Wed Apr 09 19:27:02 2008 -0400 @@ -382,7 +382,26 @@ msg_demo_mode: 'Hmm, enabling executables, are we? Tsk tsk. I\'d love to know what\'s in that EXE file you want to upload. OK, maybe you didn\'t enable EXEs. But nevertheless, changing allowed filetypes is disabled in the demo.', }, acppl: { + lbl_plugin_name: '%plugin% by %author%', + lbl_status_installed: 'Installed', + lbl_status_uninstalled: 'Not installed', + lbl_status_system: 'System plugin', + lbl_status_need_upgrade: 'Disabled (needs upgrade)', + lbl_status_disabled: 'Disabled', + lbl_filename: 'Filename:', + lbl_plugin_site: 'Plugin homepage:', + lbl_author_site: 'Author homepage:', + lbl_version: 'Version:', + lbl_installed_version: 'Installed version:', + btn_install: 'Install', + btn_disable: 'Disable', + btn_enable: 'Enable', + 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_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.', }, acppm: { heading_main: 'Edit page properties', diff -r 3f2dfdb99be4 -r b2fb50d572c7 plugins/admin/PluginManager.php --- a/plugins/admin/PluginManager.php Tue Apr 08 20:32:30 2008 -0400 +++ b/plugins/admin/PluginManager.php Wed Apr 09 19:27:02 2008 -0400 @@ -255,9 +255,160 @@ } // 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; } } - echo '
' . print_r($plugin_list, true) . '
'; + // 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(); + ?> +
+ + $data ) + { + // print out all plugins + $rowid = ( $rowid == '1' ) ? '2' : '1'; + $plugin_name = ( preg_match('/^[a-z0-9_]+$/', $data['plugin name']) ) ? $lang->get($data['plugin name']) : $data['plugin name']; + $plugin_basics = $lang->get('acppl_lbl_plugin_name', array( + 'plugin' => $plugin_name, + 'author' => $data['author'] + )); + $color = ''; + $buttons = ''; + if ( $data['system plugin'] ) + { + $status = $lang->get('acppl_lbl_status_system'); + } + else if ( $data['installed'] && !( $data['status'] & PLUGIN_DISABLED ) && !( $data['status'] & PLUGIN_OUTOFDATE ) ) + { + // this plugin is all good + $color = '_green'; + $status = $lang->get('acppl_lbl_status_installed'); + $buttons = 'uninstall|disable'; + } + else if ( $data['installed'] && $data['status'] & PLUGIN_OUTOFDATE ) + { + $color = '_red'; + $status = $lang->get('acppl_lbl_status_need_upgrade'); + $buttons = 'uninstall|update'; + } + else if ( $data['installed'] && $data['status'] & PLUGIN_DISABLED ) + { + $color = '_red'; + $status = $lang->get('acppl_lbl_status_disabled'); + $buttons = 'uninstall|enable'; + } + else + { + $color = '_red'; + $status = $lang->get('acppl_lbl_status_uninstalled'); + $buttons = 'install'; + } + $uuid = md5($data['plugin name'] . $data['version'] . $filename); + $desc = ( preg_match('/^[a-z0-9_]+$/', $data['description']) ) ? $lang->get($data['description']) : $data['description']; + $desc = sanitize_html($desc); + + $additional = ''; + + // filename + $additional .= '' . $lang->get('acppl_lbl_filename') . ' ' . "{$filename}
"; + + // plugin's site + $data['plugin uri'] = htmlspecialchars($data['plugin uri']); + $additional .= '' . $lang->get('acppl_lbl_plugin_site') . ' ' . "{$data['plugin uri']}
"; + + // author's site + $data['author uri'] = htmlspecialchars($data['author uri']); + $additional .= '' . $lang->get('acppl_lbl_author_site') . ' ' . "{$data['author uri']}
"; + + // version + $additional .= '' . $lang->get('acppl_lbl_version') . ' ' . "{$data['version']}
"; + + // installed version + if ( $data['status'] & PLUGIN_OUTOFDATE ) + { + $additional .= '' . $lang->get('acppl_lbl_installed_version') . ' ' . "{$data['version installed']}
"; + } + + // build list of buttons + $buttons_html = ''; + if ( !empty($buttons) ) + { + $filename_js = addslashes($filename); + $buttons = explode('|', $buttons); + $colors = array( + 'install' => 'green', + 'disable' => 'blue', + 'enable' => 'blue', + 'upgrade' => 'green', + 'uninstall' => 'red' + ); + foreach ( $buttons as $button ) + { + $btnface = $lang->get("acppl_btn_$button"); + $buttons_html .= "$btnface\n"; + } + } + + echo " + + "; + } + ?> +
+
+ $status +
+
+ +
+ $desc +
+ $additional +
+ $buttons_html +
+ +
+
+
+
+ '; }