diff --git a/admin/index.php b/admin/index.php index f40ad17512a..8b66c1f25b4 100644 --- a/admin/index.php +++ b/admin/index.php @@ -48,6 +48,7 @@ $id = optional_param('id', '', PARAM_TEXT); $confirmupgrade = optional_param('confirmupgrade', 0, PARAM_BOOL); $confirmrelease = optional_param('confirmrelease', 0, PARAM_BOOL); $confirmplugins = optional_param('confirmplugincheck', 0, PARAM_BOOL); +$showallplugins = optional_param('showallplugins', 0, PARAM_BOOL); $agreelicense = optional_param('agreelicense', 0, PARAM_BOOL); // Check some PHP server settings @@ -255,17 +256,19 @@ if ($version > $CFG->version) { // upgrade $PAGE->set_title($strplugincheck); $PAGE->set_heading($strplugincheck); $PAGE->set_cacheable(false); - echo $OUTPUT->header(); - echo $OUTPUT->heading($strplugincheck); - echo $OUTPUT->box_start('generalbox', 'notice'); - print_string('pluginchecknotice'); - echo $OUTPUT->box_end(); - print_plugin_tables(); + $output = $PAGE->get_renderer('core', 'admin'); + $pluginman = plugin_manager::instance(); + + echo $output->header(); + echo $output->box_start('generalbox'); + echo $output->container(get_string('pluginchecknotice', 'core_plugin'), 'generalbox', 'notice'); + echo $output->plugins_check($pluginman->get_plugins(), array('full' => $showallplugins)); + echo $output->box_end(); print_upgrade_reload('index.php?confirmupgrade=1&confirmrelease=1'); $button = new single_button(new moodle_url('index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1)), get_string('upgradestart', 'admin'), 'get'); $button->class = 'continuebutton'; - echo $OUTPUT->render($button); - echo $OUTPUT->footer(); + echo $output->render($button); + echo $output->footer(); die(); } else { @@ -293,17 +296,19 @@ if (moodle_needs_upgrading()) { $PAGE->set_title($strplugincheck); $PAGE->set_heading($strplugincheck); $PAGE->set_cacheable(false); - echo $OUTPUT->header(); - echo $OUTPUT->heading($strplugincheck); - echo $OUTPUT->box_start('generalbox', 'notice'); - print_string('pluginchecknotice'); - echo $OUTPUT->box_end(); - print_plugin_tables(); + $output = $PAGE->get_renderer('core', 'admin'); + $pluginman = plugin_manager::instance(); + + echo $output->header(); + echo $output->box_start('generalbox'); + echo $output->container(get_string('pluginchecknotice', 'core_plugin'), 'generalbox', 'notice'); + echo $output->plugins_check($pluginman->get_plugins(), array('full' => $showallplugins)); + echo $output->box_end(); print_upgrade_reload('index.php'); $button = new single_button(new moodle_url('index.php', array('confirmplugincheck'=>1)), get_string('upgradestart', 'admin'), 'get'); $button->class = 'continuebutton'; - echo $OUTPUT->render($button); - echo $OUTPUT->footer(); + echo $output->render($button); + echo $output->footer(); die(); } } diff --git a/admin/plugins.php b/admin/plugins.php new file mode 100644 index 00000000000..165dfaca945 --- /dev/null +++ b/admin/plugins.php @@ -0,0 +1,41 @@ +. + +/** + * UI for general plugins management + * + * @package core + * @subpackage admin + * @copyright 2011 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(dirname(__FILE__)) . '/config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); + +require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); +admin_externalpage_setup('pluginsoverview'); +$output = $PAGE->get_renderer('core', 'admin'); +$pluginman = plugin_manager::instance(); + +echo $output->header(); +echo $output->heading(get_string('pluginsoverview', 'core_admin')); +echo $output->box_start('generalbox'); +echo $output->plugins_control_panel($pluginman->get_plugins()); +echo $output->box_end(); +echo $output->footer(); diff --git a/admin/renderer.php b/admin/renderer.php new file mode 100644 index 00000000000..08b81e3b03b --- /dev/null +++ b/admin/renderer.php @@ -0,0 +1,296 @@ +. + +/** + * Renderer for core_admin subsystem + * + * @package core + * @subpackage admin + * @copyright 2011 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/pluginlib.php'); + +/** + * Standard HTML output renderer for core_admin subsystem + */ +class core_admin_renderer extends plugin_renderer_base { + + /** + * Displays all known plugins and information about their installation or upgrade + * + * This default implementation renders all plugins into one big table. The rendering + * options support: + * (bool)full = false: whether to display up-to-date plugins, too + * + * @param array $plugininfo as returned by {@see plugin_manager::get_plugins()} + * @param array $options rendering options + * @return string HTML code + */ + public function plugins_check(array $plugininfo, array $options = null) { + + if (empty($plugininfo)) { + return ''; + } + + if (empty($options)) { + $options = array( + 'full' => false, + ); + } + + $pluginman = plugin_manager::instance(); + + $table = new html_table(); + $table->id = 'plugins-check'; + $table->head = array( + get_string('displayname', 'core_plugin'), + get_string('rootdir', 'core_plugin'), + get_string('source', 'core_plugin'), + get_string('versiondb', 'core_plugin'), + get_string('versiondisk', 'core_plugin'), + get_string('status', 'core_plugin'), + ); + $table->colclasses = array( + 'displayname', 'rootdir', 'source', 'versiondb', 'versiondisk', 'status', + ); + $table->data = array(); + + $numofhighlighted = array(); // number of highlighted rows per this subsection + + foreach ($plugininfo as $type => $plugins) { + + $header = new html_table_cell($pluginman->plugintype_name_plural($type)); + $header->header = true; + $header->colspan = count($table->head); + $header = new html_table_row(array($header)); + $header->attributes['class'] = 'plugintypeheader type-' . $type; + + $numofhighlighted[$type] = 0; + + if (empty($plugins) and $options['full']) { + $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin')); + $msg->colspan = count($table->head); + $row = new html_table_row(array($msg)); + $row->attributes['class'] .= 'msg msg-noneinstalled'; + $table->data[] = $header; + $table->data[] = $row; + continue; + } + + $plugintyperows = array(); + + foreach ($plugins as $name => $plugin) { + $row = new html_table_row(); + $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name; + + if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) { + $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon')); + } else { + $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon')); + } + $displayname = $icon . ' ' . $plugin->displayname; + $displayname = new html_table_cell($displayname); + + $rootdir = new html_table_cell($plugin->get_dir()); + + if ($isstandard = $plugin->is_standard()) { + $row->attributes['class'] .= ' standard'; + $source = new html_table_cell(get_string('sourcestd', 'core_plugin')); + } else { + $row->attributes['class'] .= ' extension'; + $source = new html_table_cell(get_string('sourceext', 'core_plugin')); + } + + $versiondb = new html_table_cell($plugin->versiondb); + $versiondisk = new html_table_cell($plugin->versiondisk); + + $statuscode = $plugin->get_status(); + $row->attributes['class'] .= ' status-' . $statuscode; + + $status = new html_table_cell(get_string('status_' . $statuscode, 'core_plugin')); + + if ($isstandard and in_array($statuscode, array(plugin_manager::PLUGIN_STATUS_NODB, plugin_manager::PLUGIN_STATUS_UPTODATE))) { + if (empty($options['full'])) { + continue; + } + } else { + $numofhighlighted[$type]++; + } + + $row->cells = array($displayname, $rootdir, $source, $versiondb, $versiondisk, $status); + $plugintyperows[] = $row; + } + + if (empty($numofhighlighted[$type]) and empty($options['full'])) { + continue; + } + + $table->data[] = $header; + $table->data = array_merge($table->data, $plugintyperows); + } + + $sumofhighlighted = array_sum($numofhighlighted); + + if ($sumofhighlighted == 0) { + $out = $this->output->container_start('nonehighlighted', 'plugins-check-info'); + $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin')); + if (empty($options['full'])) { + $out .= html_writer::link(new moodle_url('/admin/index.php', + array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)), + get_string('nonehighlightedinfo', 'core_plugin')); + } + $out .= $this->output->container_end(); + + } else { + $out = $this->output->container_start('somehighlighted', 'plugins-check-info'); + $out .= $this->output->heading(get_string('somehighlighted', 'core_plugin', $sumofhighlighted)); + if (empty($options['full'])) { + $out .= html_writer::link(new moodle_url('/admin/index.php', + array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)), + get_string('somehighlightedinfo', 'core_plugin')); + } + $out .= $this->output->container_end(); + } + + if ($sumofhighlighted > 0 or $options['full']) { + $out .= html_writer::table($table); + } + + return $out; + } + + /** + * Displays all known plugins and links to manage them + * + * This default implementation renders all plugins into one big table. + * + * @param array $plugininfo as returned by {@see plugin_manager::get_plugins()} + * @return string HTML code + */ + public function plugins_control_panel(array $plugininfo) { + + if (empty($plugininfo)) { + return ''; + } + + $pluginman = plugin_manager::instance(); + + $table = new html_table(); + $table->id = 'plugins-control-panel'; + $table->head = array( + get_string('displayname', 'core_plugin'), + get_string('systemname', 'core_plugin'), + get_string('source', 'core_plugin'), + get_string('version', 'core_plugin'), + get_string('availability', 'core_plugin'), + get_string('settings', 'core_plugin'), + get_string('uninstall','core_plugin'), + ); + $table->colclasses = array( + 'displayname', 'systemname', 'source', 'version', 'availability', 'settings', 'uninstall', + ); + + foreach ($plugininfo as $type => $plugins) { + + $header = new html_table_cell($pluginman->plugintype_name_plural($type)); + $header->header = true; + $header->colspan = count($table->head); + $header = new html_table_row(array($header)); + $header->attributes['class'] = 'plugintypeheader type-' . $type; + $table->data[] = $header; + + if (empty($plugins)) { + $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin')); + $msg->colspan = count($table->head); + $row = new html_table_row(array($msg)); + $row->attributes['class'] .= 'msg msg-noneinstalled'; + $table->data[] = $row; + continue; + } + + foreach ($plugins as $name => $plugin) { + $row = new html_table_row(); + $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name; + + if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) { + $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon')); + } else { + $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon')); + } + if ($plugin->get_status() === plugin_manager::PLUGIN_STATUS_MISSING) { + $msg = html_writer::tag('span', get_string('status_missing', 'core_plugin'), array('class' => 'notifyproblem')); + $row->attributes['class'] .= ' missingfromdisk'; + } else { + $msg = ''; + } + $displayname = $icon . ' ' . $plugin->displayname . ' ' . $msg; + $displayname = new html_table_cell($displayname); + + $systemname = new html_table_cell($plugin->type . '_' . $plugin->name); + + if ($plugin->is_standard()) { + $row->attributes['class'] .= ' standard'; + $source = new html_table_cell(get_string('sourcestd', 'core_plugin')); + } else { + $row->attributes['class'] .= ' extension'; + $source = new html_table_cell(get_string('sourceext', 'core_plugin')); + } + + $version = new html_table_cell($plugin->versiondb); + + $isenabled = $plugin->is_enabled(); + if (is_null($isenabled)) { + $availability = new html_table_cell(''); + } else if ($isenabled) { + $row->attributes['class'] .= ' enabled'; + $icon = $this->output->pix_icon('i/hide', get_string('pluginenabled', 'core_plugin')); + $availability = new html_table_cell($icon . ' ' . get_string('pluginenabled', 'core_plugin')); + } else { + $row->attributes['class'] .= ' disabled'; + $icon = $this->output->pix_icon('i/show', get_string('plugindisabled', 'core_plugin')); + $availability = new html_table_cell($icon . ' ' . get_string('plugindisabled', 'core_plugin')); + } + + $settingsurl = $plugin->get_settings_url(); + if (is_null($settingsurl)) { + $settings = new html_table_cell(''); + } else { + $settings = html_writer::link($settingsurl, get_string('settings', 'core_plugin')); + $settings = new html_table_cell($settings); + } + + $uninstallurl = $plugin->get_uninstall_url(); + if (is_null($uninstallurl)) { + $uninstall = new html_table_cell(''); + } else { + $uninstall = html_writer::link($uninstallurl, get_string('uninstall', 'core_plugin')); + $uninstall = new html_table_cell($uninstall); + } + + $row->cells = array( + $displayname, $systemname, $source, $version, $availability, $settings, $uninstall + ); + $table->data[] = $row; + } + } + + return html_writer::table($table); + } +} diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index af1266eed14..bf8060500e5 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -5,6 +5,7 @@ */ if ($hassiteconfig) { + $ADMIN->add('modules', new admin_page_pluginsoverview()); $ADMIN->add('modules', new admin_category('modsettings', get_string('activitymodules'))); $ADMIN->add('modsettings', new admin_page_managemods()); $modules = $DB->get_records('modules', array(), "name ASC"); diff --git a/lang/en/admin.php b/lang/en/admin.php index 7c67ea9a5e6..b5032679b4d 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -800,6 +800,7 @@ $string['pleaserefreshregistration'] = 'Your site has been registered with moodl $string['pleaseregister'] = 'Please register your site to remove this button'; $string['plugin'] = 'Plugin'; $string['plugins'] = 'Plugins'; +$string['pluginsoverview'] = 'Plugins overview'; $string['profilecategory'] = 'Category'; $string['profilecategoryname'] = 'Category name (must be unique)'; $string['profilecategorynamenotunique'] = 'This category name is already in use'; diff --git a/lib/adminlib.php b/lib/adminlib.php index ffbd3d2e025..d93c83d44ca 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -4586,6 +4586,21 @@ class admin_setting_special_registerauth extends admin_setting_configselect { } +/** + * General plugins manager + */ +class admin_page_pluginsoverview extends admin_externalpage { + + /** + * Sets basic information about the external page + */ + public function __construct() { + global $CFG; + parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'), + "$CFG->wwwroot/$CFG->admin/plugins.php"); + } +} + /** * Module manage page * @@ -6116,194 +6131,6 @@ function db_replace($search, $replace) { return true; } -/** - * Prints tables of detected plugins, one table per plugin type, - * and prints whether they are part of the standard Moodle - * distribution or not. - */ -function print_plugin_tables() { - global $DB; - $plugins_standard = array(); - $plugins_standard['mod'] = array('assignment', - 'chat', - 'choice', - 'data', - 'feedback', - 'folder', - 'forum', - 'glossary', - 'imscp', - 'label', - 'lesson', - 'page', - 'quiz', - 'resource', - 'scorm', - 'survey', - 'url', - 'wiki', - 'workshop'); - - $plugins_standard['blocks'] = array('activity_modules', - 'admin_bookmarks', - 'blog_menu', - 'blog_recent', - 'blog_tags', - 'calendar_month', - 'calendar_upcoming', - 'comments', - 'community', - 'completionstatus', - 'course_list', - 'course_overview', - 'course_summary', - 'feedback', - 'glossary_random', - 'html', - 'login', - 'mentees', - 'messages', - 'mnet_hosts', - 'myprofile', - 'navigation', - 'news_items', - 'online_users', - 'participants', - 'private_files', - 'quiz_results', - 'recent_activity', - 'rss_client', - 'search', - 'search_forums', - 'section_links', - 'selfcompletion', - 'settings', - 'site_main_menu', - 'social_activities', - 'tag_flickr', - 'tag_youtube', - 'tags'); - - $plugins_standard['filter'] = array('activitynames', - 'algebra', - 'censor', - 'emailprotect', - 'emoticon', - 'filter', - 'mediaplugin', - 'multilang', - 'tex', - 'tidy', - 'urltolink'); - - $plugins_installed = array(); - $installed_mods = $DB->get_records('modules', null, 'name'); - $installed_blocks = $DB->get_records('block', null, 'name'); - - foreach($installed_mods as $mod) { - $plugins_installed['mod'][] = $mod->name; - } - - foreach($installed_blocks as $block) { - $plugins_installed['blocks'][] = $block->name; - } - $plugins_installed['filter'] = array(); - - $plugins_ondisk = array(); - $plugins_ondisk['mod'] = array_keys(get_plugin_list('mod')); - $plugins_ondisk['blocks'] = array_keys(get_plugin_list('block')); - $plugins_ondisk['filter'] = array_keys(get_plugin_list('filter')); - - $strstandard = get_string('standard'); - $strnonstandard = get_string('nonstandard'); - $strmissingfromdisk = '(' . get_string('missingfromdisk') . ')'; - $strabouttobeinstalled = '(' . get_string('abouttobeinstalled') . ')'; - - $html = ''; - - $html .= ''; - - foreach ($plugins_ondisk as $cat => $list_ondisk) { - if ($cat == 'mod') { - $strcaption = get_string('activitymodule'); - } elseif ($cat == 'filter') { - $strcaption = get_string('managefilters'); - } else { - $strcaption = get_string($cat); - } - - $html .= ''; - } - - $html .= '
' . "\n"; - $html .= '\n" - . '\n" - . '\n\n"; - - $row = 1; - - foreach ($list_ondisk as $k => $plugin) { - $status = 'ok'; - $standard = 'standard'; - $note = ''; - - if (!in_array($plugin, $plugins_standard[$cat])) { - $standard = 'nonstandard'; - $status = 'warning'; - } - - // Get real name and full path of plugin - $plugin_name = "[[$plugin]]"; - - $plugin_path = "$cat/$plugin"; - - $plugin_name = get_plugin_name($plugin, $cat); - - // Determine if the plugin is about to be installed - if ($cat != 'filter' && !in_array($plugin, $plugins_installed[$cat])) { - $note = $strabouttobeinstalled; - $plugin_name = $plugin; - } - - $html .= "\n" - . "\n" - . "\n" - . "\n\n"; - $row++; - - // If the plugin was both on disk and in the db, unset the value from the installed plugins list - if ($key = array_search($plugin, $plugins_installed[$cat])) { - unset($plugins_installed[$cat][$key]); - } - } - - // If there are plugins left in the plugins_installed list, it means they are missing from disk - foreach ($plugins_installed[$cat] as $k => $missing_plugin) { - // Make sure the plugin really is missing from disk - if (!in_array($missing_plugin, $plugins_ondisk[$cat])) { - $standard = 'standard'; - $status = 'warning'; - - if (!in_array($missing_plugin, $plugins_standard[$cat])) { - $standard = 'nonstandard'; - } - - $plugin_name = $missing_plugin; - $html .= "\n" - . "\n" - . "\n" - . "\n\n"; - $row++; - } - } - - $html .= '
' . $strcaption . '
' . get_string('directory') . "' . get_string('name') . "' . get_string('status') . "
$plugin_path$plugin_name" . ${'str' . $standard} . " $note
?$plugin_name" . ${'str' . $standard} . " $strmissingfromdisk

'; - - echo $html; -} - - /** * Manage repository settings * diff --git a/lib/moodlelib.php b/lib/moodlelib.php index ca2cd0750f0..547fe6b91f9 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -9659,41 +9659,6 @@ function object_array_unique($array, $keep_key_assoc = true) { return $keep_key_assoc ? $array : array_values($array); } -/** - * Returns the language string for the given plugin. - * - * @param string $plugin the plugin code name - * @param string $type the type of plugin (mod, block, filter) - * @return string The plugin language string - */ -function get_plugin_name($plugin, $type='mod') { - $plugin_name = ''; - - switch ($type) { - case 'mod': - $plugin_name = get_string('modulename', $plugin); - break; - case 'blocks': - $plugin_name = get_string('pluginname', "block_$plugin"); - if (empty($plugin_name) || $plugin_name == '[[pluginname]]') { - if (($block = block_instance($plugin)) !== false) { - $plugin_name = $block->get_title(); - } else { - $plugin_name = "[[$plugin]]"; - } - } - break; - case 'filter': - $plugin_name = filter_get_name('filter/' . $plugin); - break; - default: - $plugin_name = $plugin; - break; - } - - return $plugin_name; -} - /** * Is a userid the primary administrator? * diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index 2e1e782a6f0..ef646e6ca10 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -847,13 +847,13 @@ class page_requirements_manager { * passed in $module. * * - * $PAGE->strings_for_js(Array('one', 'two', 'three'), 'mymod', Array('a', null, 3)); + * $PAGE->requires->strings_for_js(array('one', 'two', 'three'), 'mymod', array('a', null, 3)); * * // The above is identitical to calling * - * $PAGE->string_for_js('one', 'mymod', 'a'); - * $PAGE->string_for_js('two', 'mymod'); - * $PAGE->string_for_js('three', 'mymod', 3); + * $PAGE->requires->string_for_js('one', 'mymod', 'a'); + * $PAGE->requires->string_for_js('two', 'mymod'); + * $PAGE->requires->string_for_js('three', 'mymod', 3); * * * @param array $identifiers An array of desired strings diff --git a/lib/pluginlib.php b/lib/pluginlib.php new file mode 100644 index 00000000000..fa1d8bfb585 --- /dev/null +++ b/lib/pluginlib.php @@ -0,0 +1,1483 @@ +. + +/** + * Defines classes used for plugins management + * + * This library provides a unified interface to various plugin types in + * Moodle. It is mainly used by the plugins management admin page and the + * plugins check page during the upgrade. + * + * @package core + * @subpackage admin + * @copyright 2011 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Singleton class providing general plugins management functionality + */ +class plugin_manager { + + /** the plugin is shipped with standard Moodle distribution */ + const PLUGIN_SOURCE_STANDARD = 'std'; + /** the plugin is added extension */ + const PLUGIN_SOURCE_EXTENSION = 'ext'; + + /** the plugin uses neither database nor capabilities, no versions */ + const PLUGIN_STATUS_NODB = 'nodb'; + /** the plugin is up-to-date */ + const PLUGIN_STATUS_UPTODATE = 'uptodate'; + /** the plugin is about to be installed */ + const PLUGIN_STATUS_NEW = 'new'; + /** the plugin is about to be upgraded */ + const PLUGIN_STATUS_UPGRADE = 'upgrade'; + /** the version at the disk is lower than the one already installed */ + const PLUGIN_STATUS_DOWNGRADE = 'downgrade'; + /** the plugin is installed but missing from disk */ + const PLUGIN_STATUS_MISSING = 'missing'; + + /** @var plugin_manager holds the singleton instance */ + protected static $singletoninstance; + /** @var array of raw plugins information */ + protected $pluginsinfo = null; + /** @var array of raw subplugins information */ + protected $subpluginsinfo = null; + + /** + * Direct initiation not allowed, use the factory method {@link self::instance()} + * + * @todo we might want to specify just a single plugin type to work with + */ + protected function __construct() { + $this->get_plugins(true); + } + + /** + * Sorry, this is singleton + */ + protected function __clone() { + } + + /** + * Factory method for this class + * + * @return plugin_manager the singleton instance + */ + public static function instance() { + global $CFG; + + if (is_null(self::$singletoninstance)) { + self::$singletoninstance = new self(); + } + return self::$singletoninstance; + } + + /** + * Returns a tree of known plugins and information about them + * + * @param bool $disablecache force reload, cache can be used otherwise + * @return array + */ + public function get_plugins($disablecache=false) { + + if ($disablecache or is_null($this->pluginsinfo)) { + $this->pluginsinfo = array(); + $plugintypes = get_plugin_types(); + foreach ($plugintypes as $plugintype => $plugintyperootdir) { + if (in_array($plugintype, array('base', 'general'))) { + throw new coding_exception('Illegal usage of reserved word for plugin type'); + } + if (class_exists('plugintype_' . $plugintype)) { + $plugintypeclass = 'plugintype_' . $plugintype; + } else { + $plugintypeclass = 'plugintype_general'; + } + if (!in_array('plugintype_interface', class_implements($plugintypeclass))) { + throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugintype_interface'); + } + $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass); + $this->pluginsinfo[$plugintype] = $plugins; + } + } + + return $this->pluginsinfo; + } + + /** + * Returns list of plugins that define their subplugins and information about them + * + * At the moment, only activity modules can define subplugins. + * + * @param double $disablecache force reload, cache can be used otherwise + * @return array + */ + public function get_subplugins($disablecache=false) { + + if ($disablecache or is_null($this->subpluginsinfo)) { + $this->subpluginsinfo = array(); + $mods = get_plugin_list('mod'); + foreach ($mods as $mod => $moddir) { + $modsubplugins = array(); + if (file_exists($moddir . '/db/subplugins.php')) { + include($moddir . '/db/subplugins.php'); + foreach ($subplugins as $subplugintype => $subplugintyperootdir) { + $subplugin = new stdClass(); + $subplugin->type = $subplugintype; + $subplugin->typerootdir = $subplugintyperootdir; + $modsubplugins[$subplugintype] = $subplugin; + } + $this->subpluginsinfo['mod_' . $mod] = $modsubplugins; + } + } + } + + return $this->subpluginsinfo; + } + + /** + * Returns the name of the plugin that defines the given subplugin type + * + * If the given subplugin type is not actually a subplugin, returns false. + * + * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz + * @return false|string the name of the parent plugin, eg. mod_workshop + */ + public function get_parent_of_subplugin($subplugintype) { + + $parent = false; + foreach ($this->get_subplugins() as $pluginname => $subplugintypes) { + if (isset($subplugintypes[$subplugintype])) { + $parent = $pluginname; + break; + } + } + + return $parent; + } + + /** + * Returns a localized name of a given plugin + * + * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap + * @return string + */ + public function plugin_name($plugin) { + list($type, $name) = normalize_component($plugin); + return $this->pluginsinfo[$type][$name]->displayname; + } + + /** + * Returns a localized name of a plugin type in plural form + * + * Most plugin types define their names in core_plugin lang file. In case of subplugins, + * we try to ask the parent plugin for the name. In the worst case, we will return + * the value of the passed $type parameter. + * + * @param string $type the type of the plugin, e.g. mod or workshopform + * @return string + */ + public function plugintype_name_plural($type) { + + if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) { + // for most plugin types, their names are defined in core_plugin lang file + return get_string('type_' . $type . '_plural', 'core_plugin'); + + } else if ($parent = $this->get_parent_of_subplugin($type)) { + // if this is a subplugin, try to ask the parent plugin for the name + if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) { + return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent); + } else { + return $this->plugin_name($parent) . ' / ' . $type; + } + + } else { + return $type; + } + } + + /** + * Defines a white list of all plugins shipped in the standard Moodle distribution + * + * @return false|array array of standard plugins or false if the type is unknown + */ + public static function standard_plugins_list($type) { + static $standard_plugins = array( + + 'assignment' => array( + 'offline', 'online', 'upload', 'uploadsingle' + ), + + 'auth' => array( + 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet', + 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius', + 'shibboleth', 'webservice' + ), + + 'block' => array( + 'activity_modules', 'admin_bookmarks', 'blog_menu', + 'blog_recent', 'blog_tags', 'calendar_month', + 'calendar_upcoming', 'comments', 'community', + 'completionstatus', 'course_list', 'course_overview', + 'course_summary', 'feedback', 'glossary_random', 'html', + 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile', + 'navigation', 'news_items', 'online_users', 'participants', + 'private_files', 'quiz_results', 'recent_activity', + 'rss_client', 'search', 'search_forums', 'section_links', + 'selfcompletion', 'settings', 'site_main_menu', + 'social_activities', 'tag_flickr', 'tag_youtube', 'tags' + ), + + 'coursereport' => array( + 'completion', 'log', 'outline', 'participation', 'progress', 'stats' + ), + + 'datafield' => array( + 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu', + 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url' + ), + + 'datapreset' => array( + 'imagegallery' + ), + + 'editor' => array( + 'textarea', 'tinymce' + ), + + 'enrol' => array( + 'authorize', 'category', 'cohort', 'database', 'flatfile', + 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet', + 'paypal', 'self' + ), + + 'filter' => array( + 'activitynames', 'algebra', 'censor', 'emailprotect', + 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy', + 'urltolink', 'mod_data', 'mod_glossary' + ), + + 'format' => array( + 'scorm', 'social', 'topics', 'weeks' + ), + + 'gradeexport' => array( + 'ods', 'txt', 'xls', 'xml' + ), + + 'gradeimport' => array( + 'csv', 'xml' + ), + + 'gradereport' => array( + 'grader', 'outcomes', 'overview', 'user' + ), + + 'local' => array( + ), + + 'message' => array( + 'email', 'jabber', 'popup' + ), + + 'mnetservice' => array( + 'enrol' + ), + + 'mod' => array( + 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder', + 'forum', 'glossary', 'imscp', 'label', 'lesson', 'page', + 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop' + ), + + 'plagiarism' => array( + ), + + 'portfolio' => array( + 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa' + ), + + 'profilefield' => array( + 'checkbox', 'datetime', 'menu', 'text', 'textarea' + ), + + 'qformat' => array( + 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift', + 'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct', + 'xhtml', 'xml' + ), + + 'qtype' => array( + 'calculated', 'calculatedmulti', 'calculatedsimple', + 'description', 'essay', 'match', 'missingtype', 'multianswer', + 'multichoice', 'numerical', 'random', 'randomsamatch', + 'shortanswer', 'truefalse' + ), + + 'quiz' => array( + 'grading', 'overview', 'responses', 'statistics' + ), + + 'report' => array( + 'backups', 'capability', 'configlog', 'courseoverview', + 'customlang', 'log', 'profiling', 'questioninstances', + 'security', 'spamcleaner', 'stats', 'unittest', 'unsuproles' + ), + + 'repository' => array( + 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem', + 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot', + 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav', + 'wikimedia', 'youtube' + ), + + 'theme' => array( + 'anomaly', 'arialist', 'base', 'binarius', 'boxxie', 'brick', + 'canvas', 'formal_white', 'formfactor', 'fusion', + 'leatherbound', 'magazine', 'nimble', 'nonzero', 'overlay', + 'serenity', 'sky_high', 'splash', 'standard', 'standardold' + ), + + 'webservice' => array( + 'amf', 'rest', 'soap', 'xmlrpc' + ), + + 'workshopallocation' => array( + 'manual', 'random' + ), + + 'workshopeval' => array( + 'best' + ), + + 'workshopform' => array( + 'accumulative', 'comments', 'numerrors', 'rubric' + ) + ); + + if (isset($standard_plugins[$type])) { + return $standard_plugins[$type]; + + } else { + return false; + } + } +} + +/** + * All classes that represent a plugin of some type must implement this interface + */ +interface plugintype_interface { + + /** + * Gathers and returns the information about all plugins of the given type + * + * Passing the parameter $typeclass allows us to reach the same effect as with the + * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use + * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()} + * + * @param string $type the name of the plugintype, eg. mod, auth or workshopform + * @param string $typerootdir full path to the location of the plugin dir + * @param string $typeclass the name of the actually called class + * @return array of plugintype classes, indexed by the plugin name + */ + public static function get_plugins($type, $typerootdir, $typeclass); + + /** + * Sets $displayname property to a localized name of the plugin + * + * @return void + */ + public function set_display_name(); + + /** + * Sets $versiondisk property to a numerical value representing the + * version of the plugin's source code. + * + * If the value is null after calling this method, either the plugin + * does not use versioning (typically does not have any database + * data) or is missing from disk. + * + * @return void + */ + public function set_version_disk(); + + /** + * Sets $versiondb property to a numerical value representing the + * currently installed version of the plugin. + * + * If the value is null after calling this method, either the plugin + * does not use versioning (typically does not have any database + * data) or has not been installed yet. + * + * @return void + */ + public function set_version_db(); + + /** + * Sets $versionrequires property to a numerical value representing + * the version of Moodle core that this plugin requires. + * + * @return void + */ + public function set_version_requires(); + + /** + * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx + * constants. + * + * If the property's value is null after calling this method, then + * the type of the plugin has not been recognized and you should throw + * an exception. + * + * @return void + */ + public function set_source(); + + /** + * Returns true if the plugin is shipped with the official distribution + * of the current Moodle version, false otherwise. + * + * @return bool + */ + public function is_standard(); + + /** + * Returns the status of the plugin + * + * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants + */ + public function get_status(); + + /** + * Returns the information about plugin availability + * + * True means that the plugin is enabled. False means that the plugin is + * disabled. Null means that the information is not available, or the + * plugin does not support configurable availability or the availability + * can not be changed. + * + * @return null|bool + */ + public function is_enabled(); + + /** + * Returns the URL of the plugin settings screen + * + * Null value means that the plugin either does not have the settings screen + * or its location is not available via this library. + * + * @return null|moodle_url + */ + public function get_settings_url(); + + /** + * Returns the URL of the screen where this plugin can be uninstalled + * + * Visiting that URL must be safe, that is a manual confirmation is needed + * for actual uninstallation of the plugin. Null value means that the + * plugin either does not support uninstallation, or does not require any + * database cleanup or the location of the screen is not available via this + * library. + * + * @return null|moodle_url + */ + public function get_uninstall_url(); + + /** + * Returns relative directory of the plugin with heading '/' + * + * @example /mod/workshop + * @return string + */ + public function get_dir(); +} + +/** + * Defines public properties that all plugintype classes must have + * and provides default implementation of required methods. + */ +abstract class plugintype_base { + + /** @var string the plugintype name, eg. mod, auth or workshopform */ + public $type; + /** @var string full path to the location of all the plugins of this type */ + public $typerootdir; + /** @var string the plugin name, eg. assignment, ldap */ + public $name; + /** @var string the localized plugin name */ + public $displayname; + /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */ + public $source; + /** @var fullpath to the location of this plugin */ + public $rootdir; + /** @var int|string the version of the plugin's source code */ + public $versiondisk; + /** @var int|string the version of the installed plugin */ + public $versiondb; + /** @var int|float|string required version of Moodle core */ + public $versionrequires; + /** @var int number of instances of the plugin - not supported yet */ + public $instances; + /** @var int order of the plugin among other plugins of the same type - not supported yet */ + public $sortorder; + + /** + * @see plugintype_interface::get_plugins() + */ + public static function get_plugins($type, $typerootdir, $typeclass) { + + // get the information about plugins at the disk + $plugins = get_plugin_list($type); + $ondisk = array(); + foreach ($plugins as $pluginname => $pluginrootdir) { + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = $pluginname; + $plugin->rootdir = $pluginrootdir; + + $plugin->set_display_name(); + $plugin->set_version_disk(); + $plugin->set_version_db(); + $plugin->set_version_requires(); + $plugin->set_source(); + + $ondisk[$pluginname] = $plugin; + } + return $ondisk; + } + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + if (! get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) { + $this->displayname = '[pluginname,' . $this->type . '_' . $this->name . ']'; + } else { + $this->displayname = get_string('pluginname', $this->type . '_' . $this->name); + } + } + + /** + * @see plugintype_interface::set_version_disk() + */ + public function set_version_disk() { + + if (empty($this->rootdir)) { + return; + } + + $versionfile = $this->rootdir . '/version.php'; + + if (is_readable($versionfile)) { + include($versionfile); + if (isset($plugin->version)) { + $this->versiondisk = $plugin->version; + } + } + } + + /** + * @see plugintype_interface::set_version_db() + */ + public function set_version_db() { + + if ($ver = self::get_version_from_config_plugins($this->type . '_' . $this->name)) { + $this->versiondb = $ver; + } + } + + /** + * @see plugintype_interface::set_version_requires() + */ + public function set_version_requires() { + + if (empty($this->rootdir)) { + return; + } + + $versionfile = $this->rootdir . '/version.php'; + + if (is_readable($versionfile)) { + include($versionfile); + if (isset($plugin->requires)) { + $this->versionrequires = $plugin->requires; + } + } + } + + /** + * @see plugintype_interface::set_source() + */ + public function set_source() { + + $standard = plugin_manager::standard_plugins_list($this->type); + + if ($standard !== false) { + $standard = array_flip($standard); + if (isset($standard[$this->name])) { + $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD; + } else { + $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION; + } + } + } + + /** + * @see plugintype_interface::is_standard() + */ + public function is_standard() { + return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD; + } + + /** + * @see plugintype_interface::get_status() + */ + public function get_status() { + + if (is_null($this->versiondb) and is_null($this->versiondisk)) { + return plugin_manager::PLUGIN_STATUS_NODB; + + } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) { + return plugin_manager::PLUGIN_STATUS_NEW; + + } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) { + return plugin_manager::PLUGIN_STATUS_MISSING; + + } else if ((string)$this->versiondb === (string)$this->versiondisk) { + return plugin_manager::PLUGIN_STATUS_UPTODATE; + + } else if ($this->versiondb < $this->versiondisk) { + return plugin_manager::PLUGIN_STATUS_UPGRADE; + + } else if ($this->versiondb > $this->versiondisk) { + return plugin_manager::PLUGIN_STATUS_DOWNGRADE; + + } else { + // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-) + throw new coding_exception('Unable to determine plugin state, check the plugin versions'); + } + } + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + return null; + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + return null; + } + + /** + * @see plugintype_interface::get_uninstall_url() + */ + public function get_uninstall_url() { + return null; + } + + /** + * @see plugintype_interface::get_dir() + */ + public function get_dir() { + global $CFG; + + return substr($this->rootdir, strlen($CFG->dirroot)); + } + + /** + * Provides access to plugin versions from {config_plugins} + * + * @param string $plugin plugin name + * @param double $disablecache optional, defaults to false + * @return int|false the stored value or false if not found + */ + protected function get_version_from_config_plugins($plugin, $disablecache=false) { + global $DB; + static $pluginversions = null; + + if (is_null($pluginversions) or $disablecache) { + $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value'); + } + + if (!array_key_exists($plugin, $pluginversions)) { + return false; + } + + return $pluginversions[$plugin]; + } +} + +/** + * General class for all plugin types that do not have their own class + */ +class plugintype_general extends plugintype_base implements plugintype_interface { + +} + +/** + * Class for page side blocks + */ +class plugintype_block extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::get_plugins() + */ + public static function get_plugins($type, $typerootdir, $typeclass) { + + // get the information about blocks at the disk + $blocks = parent::get_plugins($type, $typerootdir, $typeclass); + + // add blocks missing from disk + $blocksinfo = self::get_blocks_info(); + foreach ($blocksinfo as $blockname => $blockinfo) { + if (isset($blocks[$blockname])) { + continue; + } + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = $blockname; + $plugin->rootdir = null; + $plugin->displayname = $blockname; + $plugin->versiondb = $blockinfo->version; + $plugin->set_source(); + + $blocks[$blockname] = $plugin; + } + + return $blocks; + } + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + + if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) { + $this->displayname = get_string('pluginname', 'block_' . $this->name); + + } else if (($block = block_instance($this->name)) !== false) { + $this->displayname = $block->get_title(); + + } else { + parent::set_display_name(); + } + } + + /** + * @see plugintype_interface::set_version_db() + */ + public function set_version_db() { + global $DB; + + $blocksinfo = self::get_blocks_info(); + if (isset($blocksinfo[$this->name]->version)) { + $this->versiondb = $blocksinfo[$this->name]->version; + } + } + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + + $blocksinfo = self::get_blocks_info(); + if (isset($blocksinfo[$this->name]->visible)) { + if ($blocksinfo[$this->name]->visible) { + return true; + } else { + return false; + } + } else { + return parent::is_enabled(); + } + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if (($block = block_instance($this->name)) === false) { + return parent::get_settings_url(); + + } else if ($block->has_config()) { + if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) { + return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name)); + } else { + $blocksinfo = self::get_blocks_info(); + return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id)); + } + + } else { + return parent::get_settings_url(); + } + } + + /** + * @see plugintype_interface::get_uninstall_url() + */ + public function get_uninstall_url() { + + $blocksinfo = self::get_blocks_info(); + return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey())); + } + + /** + * Provides access to the records in {block} table + * + * @param bool $disablecache do not use internal static cache + * @return array array of stdClasses + */ + protected static function get_blocks_info($disablecache=false) { + global $DB; + static $blocksinfocache = null; + + if (is_null($blocksinfocache) or $disablecache) { + $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible'); + } + + return $blocksinfocache; + } +} + +/** + * Class for text filters + */ +class plugintype_filter extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::get_plugins() + */ + public static function get_plugins($type, $typerootdir, $typeclass) { + global $CFG; + + $filters = array(); + + // get the list of filters from both /filter and /mod location + $installed = filter_get_all_installed(); + + foreach ($installed as $filterlegacyname => $displayname) { + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = self::normalize_legacy_name($filterlegacyname); + $plugin->rootdir = $CFG->dirroot . '/' . $filterlegacyname; + $plugin->displayname = $displayname; + + $plugin->set_version_disk(); + $plugin->set_version_db(); + $plugin->set_version_requires(); + $plugin->set_source(); + + $filters[$plugin->name] = $plugin; + } + + // make sure that all installed filters are registered + $globalstates = self::get_global_states(); + $needsreload = false; + foreach (array_keys($installed) as $filterlegacyname) { + if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) { + filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED); + $needsreload = true; + } + } + if ($needsreload) { + $globalstates = self::get_global_states(true); + } + + // make sure that all registered filters are installed, just in case + foreach ($globalstates as $name => $info) { + if (!isset($filters[$name])) { + // oops, there is a record in filter_active but the filter is not installed + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = $name; + $plugin->rootdir = $CFG->dirroot . '/' . $info->legacyname; + $plugin->displayname = $info->legacyname; + + $plugin->set_version_db(); + + if (is_null($plugin->versiondb)) { + // this is a hack to stimulate 'Missing from disk' error + // because $plugin->versiondisk will be null !== false + $plugin->versiondb = false; + } + + $filters[$plugin->name] = $plugin; + } + } + + return $filters; + } + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + // do nothing, the name is set in self::get_plugins() + } + + /** + * @see plugintype_interface::set_version_disk() + */ + public function set_version_disk() { + + if (strpos($this->name, 'mod_') === 0) { + // filters bundled with modules do not use versioning + return; + } + + return parent::set_version_disk(); + } + + /** + * @see plugintype_interface::set_version_requires() + */ + public function set_version_requires() { + + if (strpos($this->name, 'mod_') === 0) { + // filters bundled with modules do not use versioning + return; + } + + return parent::set_version_requires(); + } + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + + $globalstates = self::get_global_states(); + + foreach ($globalstates as $filterlegacyname => $info) { + $name = self::normalize_legacy_name($filterlegacyname); + if ($name === $this->name) { + if ($info->active == TEXTFILTER_DISABLED) { + return false; + } else { + // it may be 'On' or 'Off, but available' + return null; + } + } + } + + return null; + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + $globalstates = self::get_global_states(); + $legacyname = $globalstates[$this->name]->legacyname; + if (filter_has_global_settings($legacyname)) { + return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname))); + } else { + return null; + } + } + + /** + * @see plugintype_interface::get_uninstall_url() + */ + public function get_uninstall_url() { + + if (strpos($this->name, 'mod_') === 0) { + return null; + } else { + $globalstates = self::get_global_states(); + $legacyname = $globalstates[$this->name]->legacyname; + return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete')); + } + } + + /** + * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle + * + * @param string $legacyfiltername legacy filter name + * @return string frankenstyle-like name + */ + protected static function normalize_legacy_name($legacyfiltername) { + + $name = str_replace('/', '_', $legacyfiltername); + if (strpos($name, 'filter_') === 0) { + $name = substr($name, 7); + if (empty($name)) { + throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername); + } + } + + return $name; + } + + /** + * Provides access to the results of {@link filter_get_global_states()} + * but indexed by the normalized filter name + * + * The legacy filter name is available as ->legacyname property. + * + * @param bool $disablecache + * @return array + */ + protected static function get_global_states($disablecache=false) { + global $DB; + static $globalstatescache = null; + + if ($disablecache or is_null($globalstatescache)) { + + if (!$DB->get_manager()->table_exists('filter_active')) { + // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()} + // does not exist yet + $globalstatescache = array(); + + } else { + foreach (filter_get_global_states() as $legacyname => $info) { + $name = self::normalize_legacy_name($legacyname); + $filterinfo = new stdClass(); + $filterinfo->legacyname = $legacyname; + $filterinfo->active = $info->active; + $filterinfo->sortorder = $info->sortorder; + $globalstatescache[$name] = $filterinfo; + } + } + } + + return $globalstatescache; + } +} + +/** + * Class for activity modules + */ +class plugintype_mod extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::get_plugins() + */ + public static function get_plugins($type, $typerootdir, $typeclass) { + + // get the information about plugins at the disk + $modules = parent::get_plugins($type, $typerootdir, $typeclass); + + // add modules missing from disk + $modulesinfo = self::get_modules_info(); + foreach ($modulesinfo as $modulename => $moduleinfo) { + if (isset($modules[$modulename])) { + continue; + } + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = $modulename; + $plugin->rootdir = null; + $plugin->displayname = $modulename; + $plugin->versiondb = $moduleinfo->version; + $plugin->set_source(); + + $modules[$modulename] = $plugin; + } + + return $modules; + } + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) { + $this->displayname = get_string('pluginname', $this->type . '_' . $this->name); + } else { + $this->displayname = get_string('modulename', $this->type . '_' . $this->name); + } + } + + /** + * @see plugintype_interface::set_version_disk() + */ + public function set_version_disk() { + + if (empty($this->rootdir)) { + return; + } + + $versionfile = $this->rootdir . '/version.php'; + + if (is_readable($versionfile)) { + include($versionfile); + if (isset($module->version)) { + $this->versiondisk = $module->version; + } + } + } + + /** + * @see plugintype_interface::set_version_db() + */ + public function set_version_db() { + global $DB; + + $modulesinfo = self::get_modules_info(); + if (isset($modulesinfo[$this->name]->version)) { + $this->versiondb = $modulesinfo[$this->name]->version; + } + } + + /** + * @see plugintype_interface::set_version_requires() + */ + public function set_version_requires() { + + if (empty($this->rootdir)) { + return; + } + + $versionfile = $this->rootdir . '/version.php'; + + if (is_readable($versionfile)) { + include($versionfile); + if (isset($module->requires)) { + $this->versionrequires = $module->requires; + } + } + } + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + + $modulesinfo = self::get_modules_info(); + if (isset($modulesinfo[$this->name]->visible)) { + if ($modulesinfo[$this->name]->visible) { + return true; + } else { + return false; + } + } else { + return parent::is_enabled(); + } + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if (!empty($this->rootdir) and (file_exists($this->rootdir . '/settings.php') or file_exists($this->rootdir . '/settingstree.php'))) { + return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name)); + } else { + return parent::get_settings_url(); + } + } + + /** + * @see plugintype_interface::get_uninstall_url() + */ + public function get_uninstall_url() { + + if ($this->name !== 'forum') { + return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey())); + } else { + return null; + } + } + + /** + * Provides access to the records in {modules} table + * + * @param bool $disablecache do not use internal static cache + * @return array array of stdClasses + */ + protected static function get_modules_info($disablecache=false) { + global $DB; + static $modulesinfocache = null; + + if (is_null($modulesinfocache) or $disablecache) { + $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible'); + } + + return $modulesinfocache; + } +} + +/** + * Class for question types + */ +class plugintype_qtype extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + $this->displayname = get_string($this->name, 'qtype_' . $this->name); + } +} + +/** + * Class for question formats + */ +class plugintype_qformat extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::set_display_name() + */ + public function set_display_name() { + $this->displayname = get_string($this->name, 'qformat_' . $this->name); + } +} + +/** + * Class for authentication plugins + */ +class plugintype_auth extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + global $CFG; + /** @var null|array list of enabled authentication plugins */ + static $enabled = null; + + if (in_array($this->name, array('nologin', 'manual'))) { + // these two are always enabled and can't be disabled + return null; + } + + if (is_null($enabled)) { + $enabled = explode(',', $CFG->auth); + } + + return isset($enabled[$this->name]); + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) { + return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name)); + } else { + return new moodle_url('/admin/auth_config.php', array('auth' => $this->name)); + } + } +} + +/** + * Class for enrolment plugins + */ +class plugintype_enrol extends plugintype_base implements plugintype_interface { + + /** + * We do not actually need whole enrolment classes here so we do not call + * {@link enrol_get_plugins()}. Note that this may produce slightly different + * results, for example if the enrolment plugin does not contain lib.php + * but it is listed in $CFG->enrol_plugins_enabled + * + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + global $CFG; + /** @var null|array list of enabled enrolment plugins */ + static $enabled = null; + + if (is_null($enabled)) { + $enabled = explode(',', $CFG->enrol_plugins_enabled); + } + + return isset($enabled[$this->name]); + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if ($this->is_enabled() or (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php'))) { + return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name)); + } else { + return parent::get_settings_url(); + } + } + + /** + * @see plugintype_interface::get_uninstall_url() + */ + public function get_uninstall_url() { + return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey())); + } +} + +/** + * Class for messaging processors + */ +class plugintype_message extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if ($this->name === 'jabber') { + return new moodle_url('/admin/settings.php', array('section' => 'jabber')); + } + + if ($this->name === 'email') { + return new moodle_url('/admin/settings.php', array('section' => 'mail')); + } + + } +} + +/** + * Class for repositories + */ +class plugintype_repository extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + + $enabled = self::get_enabled_repositories(); + + return isset($enabled[$this->name]); + } + + /** + * @see plugintype_interface::get_settings_url() + */ + public function get_settings_url() { + + if ($this->is_enabled()) { + return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name)); + } else { + return parent::get_settings_url(); + } + } + + /** + * Provides access to the records in {repository} table + * + * @param bool $disablecache do not use internal static cache + * @return array array of stdClasses + */ + protected static function get_enabled_repositories($disablecache=false) { + global $DB; + static $repositories = null; + + if (is_null($repositories) or $disablecache) { + $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder'); + } + + return $repositories; + } +} + +/** + * Class for portfolios + */ +class plugintype_portfolio extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + + $enabled = self::get_enabled_portfolios(); + + return isset($enabled[$this->name]); + } + + /** + * Provides access to the records in {portfolio_instance} table + * + * @param bool $disablecache do not use internal static cache + * @return array array of stdClasses + */ + protected static function get_enabled_portfolios($disablecache=false) { + global $DB; + static $portfolios = null; + + if (is_null($portfolios) or $disablecache) { + $portfolios = array(); + $instances = $DB->get_recordset('portfolio_instance', null, 'plugin'); + foreach ($instances as $instance) { + if (isset($portfolios[$instance->plugin])) { + if ($instance->visible) { + $portfolios[$instance->plugin]->visible = $instance->visible; + } + } else { + $portfolios[$instance->plugin] = $instance; + } + } + } + + return $portfolios; + } +} + +/** + * Class for themes + */ +class plugintype_theme extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + global $CFG; + + if ((!empty($CFG->theme) and $CFG->theme === $this->name) or + (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) { + return true; + } else { + return parent::is_enabled(); + } + } +} + +/** + * Class representing an MNet service + */ +class plugintype_mnetservice extends plugintype_base implements plugintype_interface { + + /** + * @see plugintype_interface::is_enabled() + */ + public function is_enabled() { + global $CFG; + + if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') { + return false; + } else { + return parent::is_enabled(); + } + } +} diff --git a/theme/anomaly/style/general.css b/theme/anomaly/style/general.css index 4a7a2a36fb4..b850a89846c 100644 --- a/theme/anomaly/style/general.css +++ b/theme/anomaly/style/general.css @@ -121,8 +121,6 @@ html, body {background-color:#C8C9C7;} .group .r1 .cell, .admin table .r1 .cell {background-color:#EEE;} -.admin .plugincompattable .r1 .cell {background-color:#FFF;} - .singlebutton, .buttons {text-align:center;margin:20px;} .buttons form {display:inline;} @@ -222,4 +220,4 @@ html, body {background-color:#C8C9C7;} /** Overide for RTL layout **/ .dir-rtl #page-header .navbar .breadcrumb {float:right;} -.dir-rtl #page-header .navbar .navbutton {float:left;} \ No newline at end of file +.dir-rtl #page-header .navbar .navbutton {float:left;} diff --git a/theme/base/style/admin.css b/theme/base/style/admin.css index 77fec1df685..447ff464d69 100644 --- a/theme/base/style/admin.css +++ b/theme/base/style/admin.css @@ -3,11 +3,6 @@ **/ .formtable tbody th {font-weight: normal;text-align: right;} -.plugincompattable td.nonstandard, -.plugincompattable td.missingplugin {font-weight: bold;} -.plugincompattable td.standard, -.plugincompattable td.warning {font-style: normal;} - .path-admin .manageauthtable {width:100%;} #page-admin-index .c0 {vertical-align: top;} @@ -174,3 +169,26 @@ .dir-rtl #adminsettings .form-item .form-setting, .dir-rtl #adminsettings .form-item .form-label, .dir-rtl #adminsettings .form-item .form-description { float: right;text-align: right} + +/** Plugins check */ +#page-admin-index #plugins-check-info {text-align:center;margin:1em;} +#page-admin-index #plugins-check {margin-left:auto; margin-right:auto;} +#page-admin-index #plugins-check .displayname .pluginicon {width:16px;} +#page-admin-index #plugins-check .missingfromdisk .displayname {background-color:#ffd3d9;} +#page-admin-index #plugins-check .standard .source {color:#999;} +#page-admin-index #plugins-check .extension .source {background-color:#f3f2aa;} +#page-admin-index #plugins-check .msg td {text-align:center;} +#page-admin-index #plugins-check .status-downgrade .status {background-color:#ffd3d9;} +#page-admin-index #plugins-check .status-missing .status {background-color:#ffd3d9;} +#page-admin-index #plugins-check .status-new .status {background-color:#e7f1c3;} +#page-admin-index #plugins-check .status-nodb .status {color:#999;} +#page-admin-index #plugins-check .status-upgrade .status {background-color:#d2ebff;} +#page-admin-index #plugins-check .status-uptodate .status {color:#999;} + +/** Plugins management */ +#page-admin-plugins #plugins-control-panel {margin-left:auto; margin-right:auto;} +#page-admin-plugins #plugins-control-panel .displayname .pluginicon {width:16px;} +#page-admin-plugins #plugins-control-panel .missingfromdisk .displayname {background-color:#ffd3d9;} +#page-admin-plugins #plugins-control-panel .disabled .availability {background-color:#eee;} +#page-admin-plugins #plugins-control-panel .extension .source {background-color:#f3f2aa;} +#page-admin-plugins #plugins-control-panel .msg td {text-align:center;} diff --git a/theme/canvas/style/admin.css b/theme/canvas/style/admin.css index 650dcf86aa1..0b23550f15a 100644 --- a/theme/canvas/style/admin.css +++ b/theme/canvas/style/admin.css @@ -110,15 +110,10 @@ } .plugincheckwrapper {text-align: center;} -.plugincompattable {font-size: 70%;text-align: left;} -.plugincompattable caption {text-align: center;width: 100%;font-weight: bold;font-size: 130%;} -.plugincompattable td.ok {color: #008000;} -.plugincompattable td.warning {color: #DF7800;} -.plugincompattable td.error {color: #DF0000;} /** * Web services */ #page-admin-webservice-service_users .missingcaps {color: #ff6600;font-size: 90%;} #page-admin-setting-webservicetokens .missingcaps {color: #ff6600;font-size: 90%;} -#page-admin-webservice-service_functions .functiondesc {font-size: 90%;} \ No newline at end of file +#page-admin-webservice-service_functions .functiondesc {font-size: 90%;} diff --git a/theme/canvas/style/tables.css b/theme/canvas/style/tables.css index 0006ec6848b..e0425018a08 100644 --- a/theme/canvas/style/tables.css +++ b/theme/canvas/style/tables.css @@ -6,8 +6,6 @@ #page-admin-course-category .generalbox td, #attempts th, #attempts td, -.plugincompattable th, -.plugincompattable td, .environmenttable th, .environmenttable td, .forumheaderlist td, @@ -22,9 +20,8 @@ .results .header, #attempts .header, .generaltable .header, -.plugincompattable th, .environmenttable th, .forumheaderlist th { background: #f3f3f3; border-bottom-width: 2px; -} \ No newline at end of file +} diff --git a/theme/formal_white/style/core.css b/theme/formal_white/style/core.css index f360292b005..c6128d550a3 100644 --- a/theme/formal_white/style/core.css +++ b/theme/formal_white/style/core.css @@ -173,13 +173,6 @@ input[type="radio"] { margin-left: 21%; } -/* Admin management ------------------------*/ -.plugincompattable { - font-size: 100%; - text-align: left; -} - /* User -----------------------*/ @@ -540,4 +533,4 @@ padding: 0 /** * Redirect */ -.pagelayout-redirect #content {text-align:center;margin-top:10%;margin-bottom:10%;} \ No newline at end of file +.pagelayout-redirect #content {text-align:center;margin-top:10%;margin-bottom:10%;} diff --git a/theme/formal_white/style/formal_white.css b/theme/formal_white/style/formal_white.css index bd6eb9b4cd4..6a74e1ed5d9 100644 --- a/theme/formal_white/style/formal_white.css +++ b/theme/formal_white/style/formal_white.css @@ -57,8 +57,6 @@ h1.headerheading {margin:14px 11px 8px 11px;float:left;font-size:2em;z-index:1;} #page-admin-course-category .generalbox td, #attempts th, #attempts td, -.plugincompattable th, -.plugincompattable td, .environmenttable th, .environmenttable td, .forumheaderlist td, @@ -113,4 +111,4 @@ ul.topics .section span.commands {margin-left:2em;} /* distanza dei comandi dagl #page-footer .moodledocs {text-align:center;background-color:#EFEFEF;padding:0.7em 0 0.8em 0;} /** Custom CSS **/ -[[setting:customcss]] \ No newline at end of file +[[setting:customcss]] diff --git a/theme/standard/style/admin.css b/theme/standard/style/admin.css index 7beb53b6c84..39cf0fe3b82 100644 --- a/theme/standard/style/admin.css +++ b/theme/standard/style/admin.css @@ -38,11 +38,6 @@ #page-admin-report-questioninstances-index #settingsform p {margin-bottom: 0;} .plugincheckwrapper {text-align: center;} -.plugincompattable {font-size: 70%;text-align: left;} -.plugincompattable caption {text-align: center;width: 100%;font-weight: bold;font-size: 130%;} -.plugincompattable td.ok {color: #008000;} -.plugincompattable td.warning {color: #DF7800;} -.plugincompattable td.error {color: #DF0000;} #page-admin-index .explanation, .path-admin-roles .cell.c1,