3 /* Reminder: always indent with 4 spaces (no tabs). */
4 // +---------------------------------------------------------------------------+
6 // +---------------------------------------------------------------------------+
9 // | Geeklog plugin administration page. |
10 // +---------------------------------------------------------------------------+
11 // | Copyright (C) 2000-2009 by the following authors: |
13 // | Authors: Tony Bibbs - tony AT tonybibbs DOT com |
14 // | Mark Limburg - mlimburg AT users DOT sourceforge DOT net |
15 // | Jason Whittenburg - jwhitten AT securitygeeks DOT com |
16 // | Dirk Haun - dirk AT haun-online DOT de |
17 // | Matt West - matt AT mattdanger DOT net |
18 // +---------------------------------------------------------------------------+
20 // | This program is free software; you can redistribute it and/or |
21 // | modify it under the terms of the GNU General Public License |
22 // | as published by the Free Software Foundation; either version 2 |
23 // | of the License, or (at your option) any later version. |
25 // | This program is distributed in the hope that it will be useful, |
26 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
27 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
28 // | GNU General Public License for more details. |
30 // | You should have received a copy of the GNU General Public License |
31 // | along with this program; if not, write to the Free Software Foundation, |
32 // | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
34 // +---------------------------------------------------------------------------+
37 * This is the plugin administration page. Here you can install, uninstall,
38 * upgrade, enable, disable, and upload plugins.
43 * Geeklog common function library
45 require_once '../lib-common.php';
48 * Security check to ensure user even belongs on this page
50 require_once 'auth.inc.php';
52 // Uncomment the line below if you need to debug the HTTP variables being passed
53 // to the script. This will sometimes cause errors but it will allow you to see
54 // the data being passed in a POST operation
55 // echo COM_debug($_POST);
57 // define upload error codes introduced in later PHP versions
58 if (!defined('UPLOAD_ERR_NO_TMP_DIR')) { define('UPLOAD_ERR_NO_TMP_DIR', 6); }
59 if (!defined('UPLOAD_ERR_CANT_WRITE')) { define('UPLOAD_ERR_CANT_WRITE', 7); }
60 if (!defined('UPLOAD_ERR_EXTENSION')) { define('UPLOAD_ERR_EXTENSION', 8); }
64 if (!SEC_hasRights('plugin.edit')) {
65 $display .= COM_siteHeader('menu', $MESSAGE[30])
66 . COM_showMessageText($MESSAGE[29], $MESSAGE[30])
68 COM_accessLog("User {$_USER['username']} tried to illegally access the plugin administration screen.");
74 * Shows the plugin editor form
76 * @param string $pi_name Plugin name
77 * @param int $confirmed Flag indicated the user has confirmed an action
78 * @return string HTML for plugin editor form or error message
81 function plugineditor($pi_name, $confirmed = 0)
83 global $_CONF, $_TABLES, $_USER, $LANG32, $LANG_ADMIN;
87 if (strlen($pi_name) == 0) {
88 $retval .= COM_showMessageText($LANG32[12], $LANG32[13]);
93 $result = DB_query("SELECT pi_homepage,pi_version,pi_gl_version,pi_enabled FROM {$_TABLES['plugins']} WHERE pi_name = '$pi_name'");
94 if (DB_numRows($result) <> 1) {
95 // Serious problem, we got a pi_name that doesn't exist
96 // or returned more than one row
97 $retval .= COM_startBlock ($LANG32[13], '',
98 COM_getBlockTemplate ('_msg_block', 'header'));
99 $retval .= COM_errorLog ('Error in editing plugin ' . $pi_name
100 . '. Either the plugin does not exist or there is more than one row with with same pi_name. Bailing out to prevent trouble.');
101 $retval .= COM_endBlock (COM_getBlockTemplate ('_msg_block', 'footer'));
106 $A = DB_fetchArray($result);
108 $plg_templates = new Template($_CONF['path_layout'] . 'admin/plugins');
109 $plg_templates->set_file('editor', 'editor.thtml');
110 $plg_templates->set_var('xhtml', XHTML);
111 $plg_templates->set_var('site_url', $_CONF['site_url']);
112 $plg_templates->set_var('site_admin_url', $_CONF['site_admin_url']);
113 $plg_templates->set_var('layout_url', $_CONF['layout_url']);
114 $plg_templates->set_var('start_block_editor', COM_startBlock ($LANG32[13],
115 '', COM_getBlockTemplate ('_admin_block', 'header')));
116 $plg_templates->set_var('lang_save', $LANG_ADMIN['save']);
117 $plg_templates->set_var('lang_cancel', $LANG_ADMIN['cancel']);
118 $plg_templates->set_var('lang_delete', $LANG_ADMIN['delete']);
119 $plg_templates->set_var('pi_icon', PLG_getIcon($pi_name));
120 if (SEC_hasRights('plugin.install')) {
121 $plg_templates->set_var('delete_option', '<input type="submit" value="'
122 . $LANG_ADMIN['delete'] . '" name="mode"' . XHTML . '>');
124 $plg_templates->set_var('delete_option', '');
126 $plugin_code_version = PLG_chkVersion($pi_name);
127 if (empty($plugin_code_version)) {
128 $code_version = $LANG_ADMIN['na'];
130 $code_version = $plugin_code_version;
132 $pi_installed_version = $A['pi_version'];
133 if (empty ($plugin_code_version) ||
134 ($pi_installed_version == $code_version)) {
135 $plg_templates->set_var ('update_option', '');
137 $plg_templates->set_var ('update_option', '<input type="submit" value="'
138 . $LANG32[34] . '" name="mode"' . XHTML . '>');
140 $plg_templates->set_var('confirmed', $confirmed);
141 $plg_templates->set_var('lang_pluginname', $LANG32[26]);
142 $plg_templates->set_var('pi_name', $pi_name);
143 $plg_templates->set_var('pi_display_name', plugin_get_pluginname($pi_name));
144 $plg_templates->set_var('lang_pluginhomepage', $LANG32[27]);
145 $plg_templates->set_var('pi_homepage', $A['pi_homepage']);
146 $plg_templates->set_var('lang_pluginversion', $LANG32[28]);
147 $plg_templates->set_var('lang_plugincodeversion', $LANG32[33]);
148 $plg_templates->set_var('pi_version', $A['pi_version']);
149 $plg_templates->set_var('lang_geeklogversion', $LANG32[29]);
150 $plg_templates->set_var('pi_gl_version', $A['pi_gl_version']);
151 $plg_templates->set_var('pi_codeversion', $code_version );
152 $plg_templates->set_var('lang_enabled', $LANG32[19]);
153 if ($A['pi_enabled'] == 1) {
154 $plg_templates->set_var('enabled_checked', 'checked="checked"');
156 if (file_exists($_CONF['path'] . 'plugins/' . $pi_name
157 . '/functions.inc')) {
158 $plg_templates->set_var('enabled_checked', '');
160 $plg_templates->set_var('enabled_checked', 'disabled="disabled"');
163 $plg_templates->set_var('gltoken', SEC_createToken());
164 $plg_templates->set_var('gltoken_name', CSRF_TOKEN);
165 $plg_templates->set_var('end_block',
166 COM_endBlock (COM_getBlockTemplate ('_admin_block', 'footer')));
168 $retval .= $plg_templates->finish($plg_templates->parse('output', 'editor'));
174 * Toggle plugin status from enabled to disabled and back
176 * @param array $pi_name_arr array of plugin states
180 function changePluginStatus($pi_name_arr)
182 global $_TABLES, $_DB_table_prefix;
184 // first, get a list of all plugins
185 $rst = DB_query("SELECT pi_name, pi_enabled FROM {$_TABLES['plugins']}");
186 $plg_count = DB_numRows($rst);
187 for ($i = 0; $i < $plg_count; $i++) { // iterate and check/change match with array
188 $P = DB_fetchArray($rst);
189 if (isset($pi_name_arr[$P['pi_name']]) && ($P['pi_enabled'] == 0)) { // enable it
190 PLG_enableStateChange($P['pi_name'], true);
191 DB_change($_TABLES['plugins'], 'pi_enabled', 1,
192 'pi_name', $P['pi_name']);
193 PLG_pluginStateChange($P['pi_name'], 'enabled');
194 } elseif (!isset($pi_name_arr[$P['pi_name']]) && $P['pi_enabled'] == 1) { // disable it
195 PLG_enableStateChange($P['pi_name'], false);
196 DB_change($_TABLES['plugins'], 'pi_enabled', 0,
197 'pi_name', $P['pi_name']);
198 PLG_pluginStateChange($P['pi_name'], 'disabled');
207 * @param string $pi_name Plugin name
208 * @param string $pi_version Plugin version number
209 * @param string $pi_gl_version Geeklog version plugin is compatible with
210 * @param int $enabled Flag that indicates if plugin is enabled
211 * @param string $pi_homepage URL to homepage for plugin
212 * @return string HTML redirect or error message
215 function saveplugin($pi_name, $pi_version, $pi_gl_version, $enabled, $pi_homepage)
217 global $_CONF, $_TABLES, $LANG32;
221 if (!empty ($pi_name)) {
222 if ($enabled == 'on') {
227 $pi_name = addslashes ($pi_name);
228 $pi_version = addslashes ($pi_version);
229 $pi_gl_version = addslashes ($pi_gl_version);
230 $pi_homepage = addslashes ($pi_homepage);
232 $currentState = DB_getItem($_TABLES['plugins'], 'pi_enabled',
233 "pi_name= '{$pi_name}' LIMIT 1");
234 if ($currentState != $enabled) {
235 PLG_enableStateChange($pi_name, ($enabled == 1) ? true : false);
238 DB_save($_TABLES['plugins'], 'pi_name, pi_version, pi_gl_version, pi_enabled, pi_homepage', "'$pi_name', '$pi_version', '$pi_gl_version', $enabled, '$pi_homepage'");
240 if ($currentState != $enabled) {
241 PLG_pluginStateChange($pi_name,
242 ($enabled == 1) ? 'enabled' : 'disabled');
245 $retval = COM_refresh($_CONF['site_admin_url'] . '/plugins.php?msg=28');
247 $retval .= COM_siteHeader('menu', $LANG32[13]);
248 $retval .= COM_startBlock ($LANG32[13], '',
249 COM_getBlockTemplate ('_msg_block', 'header'));
250 $retval .= COM_errorLog ('error saving plugin, no pi_name provided', 1);
251 $retval .= COM_endBlock (COM_getBlockTemplate ('_msg_block', 'footer'));
252 $retval .= plugineditor ($pi_name);
253 $retval .= COM_siteFooter ();
260 * Creates list of uninstalled plugins (if any) and offers install link to them.
262 * @param string $token Security token to use in list
263 * @return string HTML containing list of uninstalled plugins
266 function show_newplugins($token)
268 global $_CONF, $_TABLES, $LANG32;
270 require_once $_CONF['path_system'] . 'lib-admin.php';
273 $plugins_dir = $_CONF['path'] . 'plugins/';
274 $fd = opendir($plugins_dir);
278 while (($dir = @readdir($fd)) == TRUE) {
279 if (($dir <> '.') && ($dir <> '..') && ($dir <> 'CVS') &&
280 (substr($dir, 0 , 1) <> '.') && is_dir($plugins_dir . $dir)) {
282 // Check and see if this plugin is installed - if there is a record.
283 // If not then it's a new plugin
284 if (DB_count($_TABLES['plugins'], 'pi_name', $dir) == 0) {
286 $plugin_new_style = false;
287 // additionally, check if a 'functions.inc' exists
288 if (file_exists($plugins_dir . $dir . '/functions.inc')) {
289 // new plugins will have a autoinstall.php
290 if (file_exists($plugins_dir . $dir . '/autoinstall.php')) {
292 $plugin_new_style = true;
294 // and finally, since we're going to link to it, check
295 // if an install script exists
296 $adminurl = $_CONF['site_admin_url'];
297 if (strrpos($adminurl, '/') == strlen($adminurl)) {
298 $adminurl = substr($adminurl, 0, -1);
300 $pos = strrpos($adminurl, '/');
301 if ($pos === false) {
302 // didn't work out - use the URL
303 $admindir = $_CONF['site_admin_url'];
305 $admindir = $_CONF['path_html']
306 . substr($adminurl, $pos + 1);
308 $fh = @fopen($admindir . '/plugins/' . $dir
309 . '/install.php', 'r');
313 $plugin_new_style = false;
317 if ($plugin_new_style) {
318 $url = $_CONF['site_admin_url'] . '/plugins.php'
319 . '?mode=autoinstall&plugin=' . $dir;
321 $url = $_CONF['site_admin_url'] . '/plugins/' . $dir
322 . '/install.php?action=install';
324 $url .= '&' . CSRF_TOKEN . '=' . $token;
326 'pi_name' => plugin_get_pluginname($dir),
328 'install_link' => COM_createLink($LANG32[22], $url)
337 $header_arr = array( # display 'text' and use table field 'field'
338 array('text' => '#', 'field' => 'number'),
339 array('text' => $LANG32[16], 'field' => 'pi_name'),
340 array('text' => '', 'field' => 'install_link')
343 $text_arr = array('title' => $LANG32[14]);
344 $retval .= ADMIN_simpleList('', $header_arr, $text_arr, $data_arr);
350 * Updates a plugin (call its upgrade function).
352 * @param string $pi_name name of the plugin to uninstall
353 * @return string HTML for error or success message
356 function do_update($pi_name)
358 global $_CONF, $LANG32;
362 if (! empty($pi_name)) {
363 $result = PLG_upgrade($pi_name);
365 if ($result === TRUE) { // Catch returns that are just true/false
366 PLG_pluginStateChange($pi_name, 'upgraded');
367 $retval = COM_refresh($_CONF['site_admin_url']
368 . '/plugins.php?msg=60');
369 } else { // Plugin returned a message number
370 $retval = COM_refresh($_CONF['site_admin_url']
371 . '/plugins.php?msg=' . $result . '&plugin='
375 } else { // Plugin function returned a false
376 $retval = COM_showMessage(95);
378 } else { // no plugin name given
379 $retval = COM_showMessageText($LANG32[12], $LANG32[13]);
382 $retval = COM_siteHeader('menu', $LANG32[13])
391 * Uninstall a plugin (call its uninstall function).
393 * @param string $pi_name name of the plugin to uninstall
394 * @return string HTML for error or success message
397 function do_uninstall($pi_name)
403 if (empty($pi_name) || (strlen($pi_name) == 0)) {
407 // if the plugin is disabled, load the functions.inc now
408 if (!function_exists('plugin_uninstall_' . $pi_name) &&
409 !function_exists('plugin_autouninstall_' . $pi_name)) {
410 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
413 if (PLG_uninstall($pi_name)) {
414 PLG_pluginStateChange($pi_name, 'uninstalled');
416 $retval = 45; // success msg
418 $retval = 95; // error msg
425 * List available plugins
427 * @param string $token Security token
428 * @return string formatted list of plugins
431 function listplugins($token)
433 global $_CONF, $_TABLES, $LANG32, $LANG_ADMIN, $_IMAGE_TYPE;
435 require_once $_CONF['path_system'] . 'lib-admin.php';
438 $header_arr = array( # display 'text' and use table field 'field'
439 array('text' => $LANG_ADMIN['edit'], 'field' => 'edit', 'sort' => false),
440 array('text' => $LANG32[16], 'field' => 'pi_name', 'sort' => true),
441 array('text' => $LANG32[17], 'field' => 'pi_version', 'sort' => true),
442 array('text' => $LANG32[18], 'field' => 'pi_gl_version', 'sort' => true),
443 array('text' => $LANG_ADMIN['enabled'], 'field' => 'enabled', 'sort' => false)
446 $defsort_arr = array('field' => 'pi_name', 'direction' => 'asc');
449 array('url' => $_CONF['site_admin_url'],
450 'text' => $LANG_ADMIN['admin_home']));
452 $retval .= COM_startBlock($LANG32[5], '',
453 COM_getBlockTemplate('_admin_block', 'header'));
455 $retval .= ADMIN_createMenu(
458 $_CONF['layout_url'] . '/images/icons/plugins.' . $_IMAGE_TYPE
462 'has_extras' => true,
463 'instructions' => $LANG32[11],
464 'form_url' => $_CONF['site_admin_url'] . '/plugins.php'
468 'table' => 'plugins',
469 'sql' => "SELECT pi_name, pi_version, pi_gl_version, "
470 ."pi_enabled, pi_homepage FROM {$_TABLES['plugins']} WHERE 1=1",
471 'query_fields' => array('pi_name'),
472 'default_filter' => ''
475 // this is a dummy variable so we know the form has been used if all plugins
476 // should be disabled in order to disable the last one.
477 $form_arr = array('bottom' => '<input type="hidden" name="pluginenabler" value="true"' . XHTML . '>');
479 $retval .= ADMIN_list('plugins', 'ADMIN_getListField_plugins', $header_arr,
480 $text_arr, $query_arr, $defsort_arr, '', $token, '', $form_arr);
481 $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
487 * Check if an error occured while uploading a file
489 * @param array $mFile $_FILE['uploaded_file']
490 * @return mixed Returns the error string if an error occured,
491 * returns false if no error occured
494 function plugin_getUploadError($mFile)
500 if (isset($mFile['error']) && ($mFile['error'] !== UPLOAD_ERR_OK)) { // If an error occured while uploading the file.
502 if ($mFile['error'] > UPLOAD_ERR_EXTENSION) { // If the error code isn't known
504 $retval = $LANG32[99]; // Unknown error
508 $retval = $LANG32[$mFile['error'] + 100]; // Print the error
512 } else { // If no upload error occurred
522 * Check if uploads are possible
524 * @return boolean true: uploads possible; false: not possible
527 function plugin_upload_enabled()
531 $path_admin = $_CONF['path_html'] . substr($_CONF['site_admin_url'],
532 strlen($_CONF['site_url']) + 1) . '/';
534 // If 'file_uploads' is enabled in php.ini
535 // and the plugin directories are writable by the web server.
536 $upload_enabled = (ini_get('file_uploads')
537 && is_writable($_CONF['path'] . 'plugins/')
538 && is_writable($_CONF['path_html'])
539 && is_writable($path_admin . 'plugins/'))
543 return $upload_enabled;
547 * Display upload form
549 * @param string $token Security token
550 * @return string HTML for the upload form
553 function plugin_show_uploadform($token)
555 global $_CONF, $LANG28, $LANG32;
559 $retval .= COM_startBlock($LANG32[39], '',
560 COM_getBlockTemplate('_admin_block', 'header'));
562 // Show the upload form
563 $retval .= '<p>' . $LANG32[40] . '</p>' . LB
564 . '<form name="plugins_upload" action="' . $_CONF['site_admin_url'] . '/plugins.php" method="post" enctype="multipart/form-data">' . LB
565 . '<div>' . $LANG28[29] . ': '
566 . '<input type="file" dir="ltr" name="plugin" size="40"' . XHTML
568 . '<input type="submit" name="upload" value="' . $LANG32[41] . '"'
570 . '<input type="hidden" name="' . CSRF_TOKEN . '" value="' . $token
571 . '"' . XHTML . '>' . '</div>' . LB . '</form>' . LB;
573 $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
579 * Handle uploaded plugin
581 * @return string HTML: redirect or main plugin screen + error message
584 function plugin_upload()
586 global $_CONF, $_TABLES;
590 $path_admin = $_CONF['path_html'] . substr($_CONF['site_admin_url'],
591 strlen($_CONF['site_url']) + 1) . '/';
593 $upload_success = false;
595 // If an error occured while uploading the file.
596 $error_msg = plugin_getUploadError($_FILES['plugin']);
597 if (!empty($error_msg)) {
599 $retval .= plugin_main($error_msg);
603 require_once $_CONF['path_system'] . 'classes/unpacker.class.php';
605 $plugin_file = $_CONF['path_data'] . $_FILES['plugin']['name']; // Name the plugin file
607 $archive = new unpacker($_FILES['plugin']['tmp_name'],
608 $_FILES['plugin']['type']);
609 $tmp = $archive->getlist(); // Grab the contents of the tarball to see what the plugin name is
610 $dirname = preg_replace('/\/.*$/', '', $tmp[0]['filename']);
612 if (empty($dirname)) { // If $dirname is blank it's probably because the user uploaded a non Tarball file.
614 $retval = COM_refresh($_CONF['site_admin_url'] . '/plugins.php?msg=100');
618 $pi_did_exist = false; // plugin directory already existed
619 $pi_had_entry = false; // plugin had an entry in the database
620 $pi_was_enabled = false; // plugin was enabled
622 if (file_exists($_CONF['path'] . 'plugins/' . $dirname)) {
623 $pi_did_exist = true;
625 // plugin directory already exists
626 $pstatus = DB_query("SELECT pi_name, pi_enabled FROM {$_TABLES['plugins']} WHERE pi_name = '$dirname'");
627 $A = DB_fetchArray($pstatus);
628 if (isset($A['pi_name'])) {
629 $pi_had_entry = true;
630 $pi_was_enabled = ($A['pi_enabled'] == 1);
633 if ($pi_was_enabled) {
634 // disable temporarily while we move the files around
635 DB_change($_TABLES['plugins'], 'pi_enabled', 0,
636 'pi_name', $dirname);
639 require_once 'System.php';
641 $plugin_dir = $_CONF['path'] . 'plugins/' . $dirname;
642 if (file_exists($plugin_dir . '.previous')) {
643 @System::rm('-rf ' . $plugin_dir . '.previous');
645 if (file_exists($plugin_dir)) {
646 rename($plugin_dir, $plugin_dir . '.previous');
649 $public_dir = $_CONF['path_html'] . $dirname;
650 if (file_exists($public_dir . '.previous')) {
651 @System::rm('-rf ' . $public_dir . '.previous');
653 if (file_exists($public_dir)) {
654 rename($public_dir, $public_dir . '.previous');
657 $admin_dir = $path_admin . 'plugins/' . $dirname;
658 if (file_exists($admin_dir . '.previous')) {
659 @System::rm('-rf ' . $admin_dir . '.previous');
661 if (file_exists($admin_dir)) {
662 rename($admin_dir, $admin_dir . '.previous');
668 * This doesn't work if the public_html & public_html/admin/plugins directories aren't 777
671 // Extract the tarball to data so we can get the $pi_name name from admin/install.php
672 $archive->unpack($_CONF['path'] . 'data/',
673 array($dirname . '/admin/install.php'));
674 $plugin_inst = $_CONF['path'] . 'data/' . $dirname . '/admin/install.php';
676 $fhandle = @fopen($plugin_inst, 'r');
678 $fdata = fread($fhandle, filesize($plugin_inst));
682 // Remove the plugin from data/
683 require_once 'System.php';
684 @System::rm('-rf ' . $_CONF['path'] . 'data/' . $dirname);
687 * One time I wanted to install a muffler on my car and
688 * needed to match up the outside diameter of the car's
689 * exhaust pipe to the inside diameter of the muffler.
690 * Unfortunately, when I went to the auto parts store they
691 * didn't have a coupling adapter that would perfectly
692 * match the two pipes, only a bunch of smaller adapters.
693 * I ended up using about 4 small adapters to step down
694 * one size at a time to the size of the muffler's input.
696 * It's kind of like this regular expression:
699 $fdata = preg_replace('/\n/', '', $fdata);
700 $fdata = preg_replace('/ /', '', $fdata);
701 $pi_name = preg_replace('/^.*\$pi\_name=\'/', '', $fdata);
702 $pi_name = preg_replace('/\'.*$/', '', $pi_name);
704 // Some plugins don't have $pi_name set in their install.php file,
705 // This means our regex won't work and we should just use $dirname
706 if (preg_match('/\<\?php/', $pi_name) || preg_match('/--/', $pi_name)) {
710 } elseif (empty($pi_name)) {
716 // Extract the uploaded archive to the plugins directory
717 $upload_success = $archive->unpack($_CONF['path'] . 'plugins/');
719 $plg_path = $_CONF['path'] . 'plugins/' . $pi_name . '/';
720 if ($upload_success) {
722 if (file_exists($plg_path . 'public_html')) {
723 rename($plg_path . 'public_html',
724 $_CONF['path_html'] . $pi_name);
726 if (file_exists($plg_path . 'admin')) {
727 rename($plg_path . 'admin',
728 $path_admin . 'plugins/' . $pi_name);
733 unset($archive); // Collect some garbage
735 // cleanup when uploading a new version
737 $plugin_dir = $_CONF['path'] . 'plugins/' . $dirname;
738 if (file_exists($plugin_dir . '.previous')) {
739 @System::rm('-rf ' . $plugin_dir . '.previous');
742 $public_dir = $_CONF['path_html'] . $dirname;
743 if (file_exists($public_dir . '.previous')) {
744 @System::rm('-rf ' . $public_dir . '.previous');
747 $admin_dir = $path_admin . 'plugins/' . $dirname;
748 if (file_exists($admin_dir . '.previous')) {
749 @System::rm('-rf ' . $admin_dir . '.previous');
752 if ($pi_was_enabled) {
753 DB_change($_TABLES['plugins'], 'pi_enabled', 1,
754 'pi_name', $dirname);
758 $msg_with_plugin_name = false;
760 if ($pi_was_enabled) {
761 // check if we have to perform an update
762 $pi_version = DB_getItem($_TABLES['plugins'], 'pi_version',
763 "pi_name = '$dirname'");
764 $code_version = PLG_chkVersion($dirname);
765 if (! empty($code_version) &&
766 ($code_version != $pi_version)) {
767 $result = PLG_upgrade($dirname);
768 if ($result === true) {
769 PLG_pluginStateChange($dirname, 'upgraded');
770 $msg = 60; // successfully updated
772 $msg_with_plugin_name = true;
773 $msg = $result; // message provided by the plugin
776 $msg = 98; // successfully uploaded
779 $msg = 98; // successfully uploaded
781 } elseif (file_exists($plg_path . 'autoinstall.php')) {
782 // if the plugin has an autoinstall.php, install it now
783 if (plugin_autoinstall($pi_name)) {
784 PLG_pluginStateChange($pi_name, 'installed');
785 $msg = 44; // successfully installed
787 $msg = 72; // an error occured while installing the plugin
790 $msg = 98; // successfully uploaded
793 $url = $_CONF['site_admin_url'] . '/plugins.php?msg=' . $msg;
794 if ($msg_with_plugin_name) {
795 $url .= '&plugin=' . $dirname;
797 $retval = COM_refresh($url);
805 * Show main plugin screen: installed and uninstalled plugins, upload form
807 * @param string $message (optional) message to display
808 * @return string HTML for the plugin screen
811 function plugin_main($message = '')
817 $retval .= COM_siteHeader('menu', $LANG32[5]);
818 if (!empty($message)) {
819 $retval .= COM_showMessageText($message);
821 $retval .= COM_showMessageFromParameter();
824 $token = SEC_createToken();
825 $retval .= listplugins($token);
826 if (SEC_hasRights('plugin.install')) {
827 $retval .= show_newplugins($token);
830 // If the web server will allow the user to upload a plugin
831 if (plugin_upload_enabled() &&
832 SEC_hasRights('plugin.install,plugin.upload')) {
833 $retval .= plugin_show_uploadform($token);
836 $retval .= COM_siteFooter();
842 * Prepare and perform plugin auto install
844 * @param string $plugin Plugin name (internal name, i.e. directory name)
845 * @return boolean true on success, false otherwise
848 function plugin_autoinstall($plugin)
850 global $_CONF, $LANG32;
852 $plugin = COM_applyFilter($plugin);
853 $plugin = COM_sanitizeFilename($plugin);
854 $autoinstall = $_CONF['path'] . 'plugins/' . $plugin . '/autoinstall.php';
856 if (empty($plugin) || !file_exists($autoinstall)) {
857 COM_errorLog('autoinstall.php not found', 1);
862 require_once $autoinstall;
864 $check_compatible = 'plugin_compatible_with_this_version_' . $plugin;
865 if (function_exists($check_compatible)) {
866 if (! $check_compatible($plugin)) {
867 COM_errorLog($LANG32[9]);
873 $auto_install = 'plugin_autoinstall_' . $plugin;
874 if (! function_exists($auto_install)) {
875 COM_errorLog("Function '$auto_install' not found", 1);
880 $inst_parms = $auto_install($plugin);
881 if (($inst_parms === false) || empty($inst_parms)) {
882 COM_errorLog('No install parameters', 1);
887 return plugin_do_autoinstall($plugin, $inst_parms);
891 * Do the actual plugin auto install
893 * @param string $plugin Plugin name
894 * @param array $inst_parms Installation parameters for the plugin
895 * @param boolean $verbose true: enable verbose logging
896 * @return boolean true on success, false otherwise
899 function plugin_do_autoinstall($plugin, $inst_parms, $verbose = true)
901 global $_CONF, $_TABLES, $_USER, $_DB_dbms, $_DB_table_prefix;
903 $base_path = $_CONF['path'] . 'plugins/' . $plugin . '/';
906 COM_errorLog("Attempting to install the '$plugin' plugin", 1);
909 // sanity checks in $inst_parms
910 if (isset($inst_parms['info'])) {
911 $pi_name = $inst_parms['info']['pi_name'];
912 $pi_version = $inst_parms['info']['pi_version'];
913 $pi_gl_version = $inst_parms['info']['pi_gl_version'];
914 $pi_homepage = $inst_parms['info']['pi_homepage'];
916 if (empty($pi_name) || ($pi_name != $plugin) || empty($pi_version) ||
917 empty($pi_gl_version) || empty($pi_homepage)) {
918 COM_errorLog('Incomplete plugin info', 1);
923 // add plugin tables, if any
924 if (! empty($inst_parms['tables'])) {
925 $tables = $inst_parms['tables'];
926 foreach ($tables as $table) {
927 $_TABLES[$table] = $_DB_table_prefix . $table;
931 // Create the plugin's group(s), if any
934 if (! empty($inst_parms['groups'])) {
935 $groups = $inst_parms['groups'];
936 foreach ($groups as $name => $desc) {
938 COM_errorLog("Attempting to create '$name' group", 1);
941 $grp_name = addslashes($name);
942 $grp_desc = addslashes($desc);
943 DB_query("INSERT INTO {$_TABLES['groups']} (grp_name, grp_descr) VALUES ('$grp_name', '$grp_desc')", 1);
945 COM_errorLog('Error creating plugin group', 1);
946 PLG_uninstall($plugin);
951 // keep the new group's ID for use in the mappings section (below)
952 $groups[$name] = DB_insertId();
954 // assume that the first group is the plugin's Admin group
955 if ($admin_group_id == 0) {
956 $admin_group_id = $groups[$name];
961 // Create the plugin's table(s)
963 $DEFVALUES = array();
964 if (file_exists($base_path . 'sql/' . $_DB_dbms . '_install.php')) {
965 require_once $base_path . 'sql/' . $_DB_dbms . '_install.php';
968 if (count($_SQL) > 0) {
970 if (($_DB_dbms == 'mysql') &&
971 (DB_getItem($_TABLES['vars'], 'value', "name = 'database_engine'")
976 foreach ($_SQL as $sql) {
977 $sql = str_replace('#group#', $admin_group_id, $sql);
979 $sql = str_replace('MyISAM', 'InnoDB', $sql);
983 COM_errorLog('Error creating plugin table', 1);
984 PLG_uninstall($plugin);
991 // Add the plugin's features
993 COM_errorLog("Attempting to add '$plugin' features", 1);
998 if (!empty($inst_parms['features'])) {
999 $features = $inst_parms['features'];
1000 if (!empty($inst_parms['mappings'])) {
1001 $mappings = $inst_parms['mappings'];
1004 foreach ($features as $feature => $desc) {
1005 $ft_name = addslashes($feature);
1006 $ft_desc = addslashes($desc);
1007 DB_query("INSERT INTO {$_TABLES['features']} (ft_name, ft_descr) "
1008 . "VALUES ('$ft_name', '$ft_desc')", 1);
1010 COM_errorLog('Error adding plugin feature', 1);
1011 PLG_uninstall($plugin);
1016 $feat_id = DB_insertId();
1018 if (isset($mappings[$feature])) {
1019 foreach ($mappings[$feature] as $group) {
1021 COM_errorLog("Adding '$feature' feature to the '$group' group", 1);
1024 DB_query("INSERT INTO {$_TABLES['access']} (acc_ft_id, acc_grp_id) VALUES ($feat_id, {$groups[$group]})");
1026 COM_errorLog('Error mapping plugin feature', 1);
1027 PLG_uninstall($plugin);
1036 // Add plugin's Admin group to the Root user group
1037 // (assumes that the Root group's ID is always 1)
1038 if (count($groups) > 0) {
1040 COM_errorLog("Attempting to give all users in the Root group access to the '$plugin' Admin group", 1);
1043 foreach($groups as $key=>$value){
1044 DB_query("INSERT INTO {$_TABLES['group_assignments']} VALUES "
1045 . "($value, NULL, 1)");
1047 COM_errorLog('Error adding plugin admin group to Root group', 1);
1048 PLG_uninstall($plugin);
1054 // Pre-populate tables or run any other SQL queries
1055 if (count($DEFVALUES) > 0) {
1057 COM_errorLog('Inserting default data', 1);
1059 foreach ($DEFVALUES as $sql) {
1060 $sql = str_replace('#group#', $admin_group_id, $sql);
1063 COM_errorLog('Error adding plugin default data', 1);
1064 PLG_uninstall($plugin);
1071 // Load the online configuration records
1072 $load_config = 'plugin_load_configuration_' . $plugin;
1073 if (function_exists($load_config)) {
1074 if (! $load_config($plugin)) {
1075 COM_errorLog('Error loading plugin configuration', 1);
1076 PLG_uninstall($plugin);
1081 require_once $_CONF['path'] . 'system/classes/config.class.php';
1082 $config =& config::get_instance();
1083 $config->initConfig(); // force re-reading, including new plugin conf
1086 // Finally, register the plugin with Geeklog
1088 COM_errorLog("Registering '$plugin' plugin", 1);
1091 // silently delete an existing entry
1092 DB_delete($_TABLES['plugins'], 'pi_name', $plugin);
1094 DB_query("INSERT INTO {$_TABLES['plugins']} (pi_name, pi_version, pi_gl_version, pi_homepage, pi_enabled) VALUES "
1095 . "('$plugin', '$pi_version', '$pi_gl_version', '$pi_homepage', 1)");
1098 COM_errorLog('Failed to register plugin', 1);
1099 PLG_uninstall($plugin);
1104 // give the plugin a chance to perform any post-install operations
1105 $post_install = 'plugin_postinstall_' . $plugin;
1106 if (function_exists($post_install)) {
1107 if (! $post_install($plugin)) {
1108 COM_errorLog('Plugin postinstall failed', 1);
1109 PLG_uninstall($plugin);
1116 COM_errorLog("Successfully installed the '$plugin' plugin!", 1);
1119 // load plugin here already, for any plugins wanting to act on
1120 // PLG_pluginStateChange($plugin, 'installed') when we return from here
1121 require_once $_CONF['path'] . 'plugins/' . $plugin . '/functions.inc';
1127 * See if we can figure out the plugin's real name
1129 * @param string $plugin internal name / directory name
1130 * @return string real or beautified name
1133 function plugin_get_pluginname($plugin)
1139 $plugins_dir = $_CONF['path'] . 'plugins/';
1140 $autoinstall = $plugins_dir . $plugin . '/autoinstall.php';
1142 // for new plugins, get the name from the autoinstall.php
1143 if (file_exists($autoinstall)) {
1145 require_once $autoinstall;
1147 $fn = 'plugin_autoinstall_' . $plugin;
1148 if (function_exists($fn)) {
1149 $info = $fn($plugin);
1150 if (is_array($info) && isset($info['info']) &&
1151 isset($info['info']['pi_display_name'])) {
1152 $retval = $info['info']['pi_display_name'];
1158 if (empty($retval)) {
1159 // give up and fake it
1160 $retval = ucwords(str_replace('_', ' ', $plugin));
1169 if (isset($_POST['pluginenabler'])) { // JavaScript-triggered POST request
1170 if (isset($_POST['updatethisplugin'])) {
1171 // translate into a standard update request (see below)
1172 $_POST['mode'] = $LANG32[34];
1173 $_POST['pi_name'] = $_POST['updatethisplugin'];
1174 } elseif (SEC_checkToken()) {
1175 if (isset($_POST['enabledplugins'])) {
1176 changePluginStatus($_POST['enabledplugins']);
1178 changePluginStatus(array());
1181 // force a refresh so that the information of the plugin that was just
1182 // enabled / disabled (menu entries, etc.) is displayed properly
1183 header('Location: ' . $_CONF['site_admin_url'] . '/plugins.php');
1189 if (isset($_POST['mode'])) {
1190 $mode = $_POST['mode'];
1191 } elseif (isset($_GET['mode'])) {
1192 $mode = $_GET['mode'];
1194 if (($mode == $LANG_ADMIN['delete']) && !empty($LANG_ADMIN['delete'])) {
1195 $pi_name = COM_applyFilter($_POST['pi_name']);
1196 if ((! empty($pi_name)) && SEC_hasRights('plugin.install')) {
1197 if (($_POST['confirmed'] == 1) && SEC_checkToken()) {
1198 $msg = do_uninstall($pi_name);
1199 if ($msg === false) {
1200 echo COM_refresh($_CONF['site_admin_url'] . '/plugins.php');
1202 echo COM_refresh($_CONF['site_admin_url'] . '/plugins.php?msg='
1206 } else { // ask user for confirmation
1207 $display .= COM_siteHeader('menu', $LANG32[30]);
1208 $display .= COM_startBlock($LANG32[30], '',
1209 COM_getBlockTemplate('_msg_block', 'header'));
1210 $display .= $LANG32[31];
1211 $display .= COM_endBlock(COM_getBlockTemplate('_msg_block',
1213 $display .= plugineditor($pi_name, 1);
1214 $display .= COM_siteFooter();
1217 $display = COM_refresh($_CONF['site_admin_url'] . '/plugins.php');
1220 } elseif ((($mode == $LANG32[34]) && !empty($LANG32[34])) && SEC_checkToken()) { // update
1221 $pi_name = COM_applyFilter($_POST['pi_name']);
1222 $display .= do_update($pi_name);
1224 } elseif ($mode == 'edit') {
1225 $display .= COM_siteHeader('menu', $LANG32[13]);
1226 $display .= plugineditor(COM_applyFilter($_GET['pi_name']));
1227 $display .= COM_siteFooter();
1229 } elseif ((($mode == $LANG_ADMIN['save']) && !empty($LANG_ADMIN['save'])) && SEC_checkToken()) {
1231 if (isset($_POST['enabled'])) {
1232 $enabled = COM_applyFilter($_POST['enabled']);
1234 $display .= saveplugin (COM_applyFilter($_POST['pi_name']),
1235 COM_applyFilter($_POST['pi_version']),
1236 COM_applyFilter($_POST['pi_gl_version']),
1237 $enabled, COM_applyFilter($_POST['pi_homepage']));
1239 } elseif (($mode == 'autoinstall') && SEC_checkToken()) {
1240 if (SEC_hasRights('plugin.install')) {
1242 if (isset($_GET['plugin'])) {
1243 $plugin = COM_applyFilter($_GET['plugin']);
1245 if (plugin_autoinstall($plugin)) {
1246 PLG_pluginStateChange($plugin, 'installed');
1247 $display .= COM_refresh($_CONF['site_admin_url']
1248 . '/plugins.php?msg=44');
1250 $display .= COM_refresh($_CONF['site_admin_url']
1251 . '/plugins.php?msg=72');
1254 $display = COM_refresh($_CONF['site_admin_url'] . '/plugins.php');
1257 } elseif (isset($_FILES['plugin']) && SEC_checkToken() &&
1258 SEC_hasRights('plugin.install,plugin.upload')) {
1259 $display .= plugin_upload();
1261 } else { // 'cancel' or no mode at all
1262 $display .= plugin_main();
1266 COM_output($display);