Commit 0a694de5 authored by Andrew Robert Nicols's avatar Andrew Robert Nicols
Browse files

Bug #824445: Improve error reporting for missing plugins



This set of changes attempts to handle missing plugins in a better fashion.
Rather than throwing an uncaught error which causes a unrecoverable site
error, which in turn means that no user can use any other plugin; this
patch disables the problem plugin and informs administrators of the issue.

To handle the case where core plugins, which under normal circumstances
cannot be disabled, are missing and disabled; the plugins.tpl template is
also changed to test for the contents of the activateform, instead of
whether the plugin can ordinarily be disabled. This means that if the issue
is then fixed, the plugin can once again be re-enabled, at which point the
[ hide ] link will no longer appear.

Change-Id: I6f9fa73ae0ac307b5f5f8f30b8205690926f54fc
Signed-off-by: default avatarAndrew Robert Nicols <andrew.nicols@luns.net.uk>
parent 921930ee
......@@ -55,18 +55,17 @@ foreach (array_keys($plugins) as $plugin) {
if ($plugin == 'blocktype') {
$key = blocktype_single_to_namespaced($i->name, $i->artefactplugin);
}
try {
safe_require($plugin, $key);
}
catch (SystemException $e) {
$message = get_string('missingplugin', 'admin', hsc("$plugin:$key")) . ':<br>' . $e->getMessage();
die_info($message);
if (!safe_require_plugin($plugin, $key)) {
if ($i->active) {
$SESSION->add_error_msg(get_string('missingplugindisabled', 'admin', hsc("$plugin:$key")));
}
continue;
}
$plugins[$plugin]['installed'][$key] = array(
'active' => $i->active,
'disableable' => call_static_method(generate_class_name($plugin, $key), 'can_be_disabled'),
);
if ($plugins[$plugin]['installed'][$key]['disableable']) {
if ($plugins[$plugin]['installed'][$key]['disableable'] || !$i->active) {
$plugins[$plugin]['installed'][$key]['activateform'] = activate_plugin_form($plugin, $i);
}
if ($plugin == 'artefact') {
......
......@@ -672,7 +672,9 @@ class BlockInstance {
public function render_viewing() {
safe_require('blocktype', $this->get('blocktype'));
if (!safe_require_plugin('blocktype', $this->get('blocktype'))) {
return;
}
$classname = generate_class_name('blocktype', $this->get('blocktype'));
try {
$content = call_static_method($classname, 'render_instance', $this);
......
......@@ -124,6 +124,7 @@ $string['institutionfilesdescription'] = 'Upload and manage files for use in Ins
$string['pluginadmin'] = 'Plugin Administration';
$string['pluginadmindescription'] = 'Install and configure plugins';
$string['missingplugin'] = 'An installed plugin (%s) could not be found';
$string['missingplugindisabled'] = 'An installed plugin (%s) could not be found and has been disabled';
$string['installedpluginsmissing'] = 'The following plugins are installed but can no longer be found';
$string['ensurepluginsexist'] = 'Please make sure all your installed plugins are available under %s, and readable by the webserver.';
......
......@@ -69,6 +69,19 @@ $string['plugindisabled'] = 'The plugin has been hidden.';
$string['pluginnotenabled'] = 'Plugin is hidden. You must make the %s plugin visible first.';
$string['pluginexplainaddremove'] = 'Plugins in Mahara are always installed and can be accessed if users know the URLs and would otherwise have access. Rather than enabling and disabling the functionality, plugins are hidden or made visible by clicking on the \'Hide\' or \'Show\' links beside the plugins below.';
$string['pluginexplainartefactblocktypes'] = 'When hiding an \'artefact\' type plugin, the Mahara system also stops the display of the blocks related to it.';
$string['pluginbrokenanddisabledtitle'] = 'A broken plugin (%s) was disabled';
$string['pluginbrokenanddisabled'] = 'A user attempted to load the %s plugin, but it could not be loaded.
To prevent further errors, the plugin has been disabled.
The error message generated by the plugin was:
----------------------------------------------------------------------------
%s
----------------------------------------------------------------------------
To re-enable the plugin, please visit the Extensions Page of your site.
';
$string['next'] = 'Next';
$string['nextpage'] = 'Next page';
......
......@@ -1133,6 +1133,43 @@ function safe_require($plugintype, $pluginname, $filename='lib.php', $function='
}
/**
* This function is a wrapper around safe_require which will attempt to
* handle missing plugins more gracefully.
*
* If a missing plugin is detected, then that plugin will be disabled, and
* an e-mail will be sent to site administrators to inform them of the
* issue.
*
* See @safe_require for further information on that function.
*
* @param string $plugintype the type of plugin (eg artefact)
* @param string $pluginname the name of the plugin (eg blog)
* @param string $filename the name of the file to include within the plugin structure
* @param string $function (optional, defaults to require) the require/include function to use
* @param string $nonfatal (optional, defaults to false) just returns false if the file doesn't exist
*/
function safe_require_plugin($plugintype, $pluginname, $filename='lib.php', $function='require_once', $nonfatal=false) {
try {
safe_require($plugintype, $pluginname, $filename, $function, $nonfatal);
return true;
}
catch (SystemException $e) {
if (get_field($plugintype . '_installed', 'active', 'name', $pluginname) == 1) {
set_field($plugintype . '_installed', 'active', 0, 'name', $pluginname);
// Alert site admins that the plugin is broken so was disabled
$message = new stdClass();
$message->users = get_column('usr', 'id', 'admin', 1);
$message->subject = get_string('pluginbrokenanddisabledtitle', 'mahara', $pluginname);
$message->message = get_string('pluginbrokenanddisabled', 'mahara', $pluginname, $e->getMessage());
require_once('activity.php');
activity_occurred('maharamessage', $message);
}
return false;
}
}
/**
* This function returns the list of plugintypes we currently care about.
*
......
......@@ -1075,7 +1075,9 @@ class View {
if (isset($localallowed) && is_array($localallowed) && !in_array($blocktypecategory->category, $localallowed)) {
continue;
}
safe_require('blocktype', $blocktypecategory->blocktype);
if (!safe_require_plugin('blocktype', $blocktypecategory->blocktype)) {
continue;
}
if (call_static_method(generate_class_name("blocktype", $blocktypecategory->blocktype), "allowed_in_view", $this)) {
if (!isset($categories[$blocktypecategory->category])) {
$categories[$blocktypecategory->category] = array(
......@@ -1630,7 +1632,9 @@ class View {
foreach($view_data as $column) {
foreach($column['blockinstances'] as $blockinstance) {
$pluginname = $blockinstance->get('blocktype');
safe_require('blocktype', $pluginname);
if (!safe_require_plugin('blocktype', $pluginname)) {
continue;
}
$instancejs = call_static_method(
generate_class_name('blocktype', $pluginname),
'get_instance_javascript',
......
......@@ -2157,17 +2157,19 @@ function main_nav() {
if ($plugins = plugins_installed('artefact')) {
foreach ($plugins as &$plugin) {
safe_require('artefact', $plugin->name);
$plugin_menu = call_static_method(generate_class_name('artefact',$plugin->name), 'menu_items');
$menu = array_merge($menu, $plugin_menu);
if (safe_require_plugin('artefact', $plugin->name)) {
$plugin_menu = call_static_method(generate_class_name('artefact',$plugin->name), 'menu_items');
$menu = array_merge($menu, $plugin_menu);
}
}
}
if ($plugins = plugins_installed('interaction')) {
foreach ($plugins as &$plugin) {
safe_require('interaction', $plugin->name);
$plugin_menu = call_static_method(generate_class_name('interaction',$plugin->name), 'menu_items');
$menu = array_merge($menu, $plugin_menu);
if (safe_require_plugin('interaction', $plugin->name)) {
$plugin_menu = call_static_method(generate_class_name('interaction',$plugin->name), 'menu_items');
$menu = array_merge($menu, $plugin_menu);
}
}
}
......
......@@ -13,13 +13,13 @@
<ul id="{$plugintype}.installed">
{foreach from=$installed key='plugin' item='data'}
<li id="{$plugintype}.{$plugin}">{$plugin}
{if $data.disableable}
{if $data.activateform}
[ {$data.activateform|safe}
{/if}
{if $data.config}
{if !$data.disableable} [ {else} | {/if}
{if !$data.activateform} [ {else} | {/if}
<a href="pluginconfig.php?plugintype={$plugintype}&amp;pluginname={$plugin}">{str tag='config'}</a>
{/if} {if $data.config || $data.disableable} ] {/if} </li>
{/if} {if $data.config || $data.activateform} ] {/if} </li>
{if $data.types}
<li><ul>
{foreach from=$data.types key='type' item='config'}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment