diff --git a/admin/auth.php b/admin/auth.php index 934acd371ef01..4dc0a0d15d78e 100644 --- a/admin/auth.php +++ b/admin/auth.php @@ -10,6 +10,7 @@ require_once('../config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); +require_once($CFG->libdir.'/pluginlib.php'); require_login(); require_capability('moodle/site:config', context_system::instance()); @@ -52,6 +53,7 @@ set_config('registerauth', ''); } session_gc(); // remove stale sessions + plugin_manager::reset_caches(); break; case 'enable': @@ -62,6 +64,7 @@ set_config('auth', implode(',', $authsenabled)); } session_gc(); // remove stale sessions + plugin_manager::reset_caches(); break; case 'down': diff --git a/admin/block.php b/admin/block.php deleted file mode 100644 index 59938ec1fef94..0000000000000 --- a/admin/block.php +++ /dev/null @@ -1,76 +0,0 @@ -libdir.'/adminlib.php'); - - $blockid = required_param('block', PARAM_INT); - - if(!$blockrecord = blocks_get_record($blockid)) { - print_error('blockdoesnotexist', 'error'); - } - - admin_externalpage_setup('blocksetting'.$blockrecord->name); - - $block = block_instance($blockrecord->name); - if($block === false) { - print_error('blockcannotinistantiate', 'error'); - } - - // Define the data we're going to silently include in the instance config form here, - // so we can strip them from the submitted data BEFORE handling it. - $hiddendata = array( - 'block' => $blockid, - 'sesskey' => sesskey() - ); - - /// If data submitted, then process and store. - - if ($config = data_submitted()) { - - if (!confirm_sesskey()) { - print_error('confirmsesskeybad', 'error'); - } - if(!$block->has_config()) { - print_error('blockcannotconfig', 'error'); - } - $remove = array_keys($hiddendata); - foreach($remove as $item) { - unset($config->$item); - } - $block->config_save($config); - redirect("$CFG->wwwroot/$CFG->admin/blocks.php", get_string("changessaved"), 1); - exit; - } - - /// Otherwise print the form. - - $strmanageblocks = get_string('manageblocks'); - $strblockname = $block->get_title(); - - echo $OUTPUT->header(); - - echo $OUTPUT->heading($strblockname); - - echo $OUTPUT->notification('This block still uses an old-style config_global.html file. ' . - 'It must be updated by a developer to use a settings.php file.'); - - echo $OUTPUT->box(get_string('configwarning', 'admin'), 'generalbox boxwidthnormal boxaligncenter'); - echo '
'; - - echo '
'; - echo '

'; - foreach($hiddendata as $name => $val) { - echo ''; - } - echo '

'; - - echo $OUTPUT->box_start(); - include($CFG->dirroot.'/blocks/'. $block->name() .'/config_global.html'); - echo $OUTPUT->box_end(); - - echo '
'; - echo $OUTPUT->footer(); - - diff --git a/admin/blocks.php b/admin/blocks.php index 6a2d31efd6e03..1e37dd26d737a 100644 --- a/admin/blocks.php +++ b/admin/blocks.php @@ -5,6 +5,7 @@ require_once('../config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); + require_once($CFG->libdir.'/pluginlib.php'); admin_externalpage_setup('manageblocks'); @@ -29,9 +30,6 @@ $strprotect = get_string('blockprotect', 'admin'); $strunprotect = get_string('blockunprotect', 'admin'); - // Purge all caches related to blocks administration. - cache::make('core', 'plugininfo_block')->purge(); - /// If data submitted, then process and store. if (!empty($hide) && confirm_sesskey()) { @@ -39,6 +37,7 @@ print_error('blockdoesnotexist', 'error'); } $DB->set_field('block', 'visible', '0', array('id'=>$block->id)); // Hide block + plugin_manager::reset_caches(); admin_get_root(true, false); // settings not required - only pages } @@ -47,6 +46,7 @@ print_error('blockdoesnotexist', 'error'); } $DB->set_field('block', 'visible', '1', array('id'=>$block->id)); // Show block + plugin_manager::reset_caches(); admin_get_root(true, false); // settings not required - only pages } @@ -120,12 +120,13 @@ foreach ($blocknames as $blockid=>$strblockname) { $block = $blocks[$blockid]; $blockname = $block->name; + $dbversion = get_config('block_'.$block->name, 'version'); if (!file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) { $blockobject = false; $strblockname = ''.$strblockname.' ('.get_string('missingfromdisk').')'; $plugin = new stdClass(); - $plugin->version = $block->version; + $plugin->version = $dbversion; } else { $plugin = new stdClass(); @@ -186,10 +187,10 @@ $class = ' class="dimmed_text"'; // Leading space required! } - if ($block->version == $plugin->version) { - $version = $block->version; + if ($dbversion == $plugin->version) { + $version = $dbversion; } else { - $version = "$block->version ($plugin->version)"; + $version = "$dbversion ($plugin->version)"; } if (!$blockobject) { diff --git a/admin/courseformats.php b/admin/courseformats.php index 7ea15e86d165b..f86c4becc8cae 100644 --- a/admin/courseformats.php +++ b/admin/courseformats.php @@ -53,11 +53,13 @@ print_error('cannotdisableformat', 'error', $return); } set_config('disabled', 1, 'format_'. $formatname); + plugin_manager::reset_caches(); } break; case 'enable': if (!$formatplugins[$formatname]->is_enabled()) { unset_config('disabled', 'format_'. $formatname); + plugin_manager::reset_caches(); } break; case 'up': diff --git a/admin/editors.php b/admin/editors.php index da846faf2f846..50ee7a510864c 100644 --- a/admin/editors.php +++ b/admin/editors.php @@ -7,6 +7,7 @@ require_once('../config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); +require_once($CFG->libdir.'/pluginlib.php'); $action = required_param('action', PARAM_ALPHANUMEXT); $editor = required_param('editor', PARAM_PLUGIN); @@ -93,6 +94,7 @@ } set_config('texteditors', implode(',', $active_editors)); +plugin_manager::reset_caches(); if ($return) { redirect ($returnurl); diff --git a/admin/enrol.php b/admin/enrol.php index 59f77c53aee3c..14a7649f8992f 100644 --- a/admin/enrol.php +++ b/admin/enrol.php @@ -27,6 +27,7 @@ require_once('../config.php'); require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/pluginlib.php'); $action = required_param('action', PARAM_ALPHANUMEXT); $enrol = required_param('enrol', PARAM_PLUGIN); @@ -50,6 +51,7 @@ case 'disable': unset($enabled[$enrol]); set_config('enrol_plugins_enabled', implode(',', array_keys($enabled))); + plugin_manager::reset_caches(); $syscontext->mark_dirty(); // resets all enrol caches break; @@ -60,6 +62,7 @@ $enabled = array_keys($enabled); $enabled[] = $enrol; set_config('enrol_plugins_enabled', implode(',', $enabled)); + plugin_manager::reset_caches(); $syscontext->mark_dirty(); // resets all enrol caches break; diff --git a/admin/filters.php b/admin/filters.php index 6c585046ab9bc..334dc719fbf3d 100644 --- a/admin/filters.php +++ b/admin/filters.php @@ -33,6 +33,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->libdir . '/adminlib.php'); + require_once($CFG->libdir . '/pluginlib.php'); $action = optional_param('action', '', PARAM_ALPHANUMEXT); $filterpath = optional_param('filterpath', '', PARAM_SAFEDIR); @@ -44,9 +45,6 @@ $returnurl = "$CFG->wwwroot/$CFG->admin/filters.php"; admin_externalpage_setup('managefilters'); - // Purge all caches related to filter administration. - cache::make('core', 'plugininfo_filter')->purge(); - $filters = filter_get_global_states(); // In case any new filters have been installed, but not put in the table yet. @@ -59,7 +57,7 @@ /// Process actions ============================================================ if ($action) { - if (!isset($filters[$filterpath]) && !isset($newfilters[$filterpath])) { + if ($action !== 'delete' and !isset($filters[$filterpath]) and !isset($newfilters[$filterpath])) { throw new moodle_exception('filternotinstalled', 'error', $returnurl, $filterpath); } @@ -138,6 +136,7 @@ // Reset caches and return if ($action) { + plugin_manager::reset_caches(); reset_text_filters_cache(); redirect($returnurl); } diff --git a/admin/localplugins.php b/admin/localplugins.php index be09151a5a342..8286e96fee5fd 100644 --- a/admin/localplugins.php +++ b/admin/localplugins.php @@ -30,6 +30,7 @@ require_once(dirname(dirname(__FILE__)) . '/config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); +require_once($CFG->libdir.'/pluginlib.php'); admin_externalpage_setup('managelocalplugins'); diff --git a/admin/message.php b/admin/message.php index 88e8f856617ca..67a60b08b8812 100644 --- a/admin/message.php +++ b/admin/message.php @@ -24,6 +24,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->dirroot . '/message/lib.php'); require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/pluginlib.php'); // This is an admin page admin_externalpage_setup('managemessageoutputs'); @@ -44,6 +45,7 @@ print_error('outputdoesnotexist', 'message'); } $DB->set_field('message_processors', 'enabled', '0', array('id'=>$processor->id)); // Disable output + plugin_manager::reset_caches(); } if (!empty($enable) && confirm_sesskey()) { @@ -51,6 +53,7 @@ print_error('outputdoesnotexist', 'message'); } $DB->set_field('message_processors', 'enabled', '1', array('id'=>$processor->id)); // Enable output + plugin_manager::reset_caches(); } if (!empty($uninstall) && confirm_sesskey()) { diff --git a/admin/modules.php b/admin/modules.php index c8f69d45f3798..a964c73dfe4e4 100644 --- a/admin/modules.php +++ b/admin/modules.php @@ -5,6 +5,7 @@ require_once('../course/lib.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); + require_once($CFG->libdir.'/pluginlib.php'); // defines define('MODULE_TABLE','module_administration_table'); @@ -27,9 +28,6 @@ $stractivitymodule = get_string("activitymodule"); $strshowmodulecourse = get_string('showmodulecourse'); - // Purge all caches related to activity modules administration. - cache::make('core', 'plugininfo_mod')->purge(); - /// If data submitted, then process and store. if (!empty($hide) and confirm_sesskey()) { @@ -50,6 +48,7 @@ FROM {course_modules} WHERE visibleold=1 AND module=?)", array($module->id)); + plugin_manager::reset_caches(); admin_get_root(true, false); // settings not required - only pages } @@ -66,6 +65,7 @@ FROM {course_modules} WHERE visible=1 AND module=?)", array($module->id)); + plugin_manager::reset_caches(); admin_get_root(true, false); // settings not required - only pages } @@ -143,12 +143,12 @@ $visible = ""; $class = ""; } - + $version = get_config('mod_'.$module->name, 'version'); $table->add_data(array( ''.$strmodulename.'', $countlink, - ''.$module->version.'', + ''.$version.'', $visible, $uninstall, $settings diff --git a/admin/plagiarism.php b/admin/plagiarism.php index ace710fd7bde4..d5fc85ae12599 100644 --- a/admin/plagiarism.php +++ b/admin/plagiarism.php @@ -29,6 +29,8 @@ require_once(dirname(dirname(__FILE__)) . '/config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); +require_once($CFG->libdir.'/pluginlib.php'); + admin_externalpage_setup('manageplagiarismplugins'); diff --git a/admin/portfolio.php b/admin/portfolio.php index f5940536139f8..3b460fdce2e7f 100644 --- a/admin/portfolio.php +++ b/admin/portfolio.php @@ -4,6 +4,7 @@ require_once($CFG->libdir . '/portfoliolib.php'); require_once($CFG->libdir . '/portfolio/forms.php'); require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); $portfolio = optional_param('pf', '', PARAM_ALPHANUMEXT); $action = optional_param('action', '', PARAM_ALPHA); @@ -43,9 +44,6 @@ $return = true; // direct back to the main page -// Purge all caches related to portfolio administration. -cache::make('core', 'plugininfo_portfolio')->purge(); - /** * Helper function that generates a moodle_url object * relevant to the portfolio @@ -91,6 +89,7 @@ function portfolio_action_url($portfolio) { } else { portfolio_static_function($plugin, 'create_instance', $plugin, $fromform->name, $fromform); } + plugin_manager::reset_caches(); $savedstr = get_string('instancesaved', 'portfolio'); redirect($baseurl, $savedstr, 1); exit; @@ -119,6 +118,7 @@ function portfolio_action_url($portfolio) { $instance->set('visible', $visible); $instance->save(); + plugin_manager::reset_caches(); $return = true; } else if ($action == 'delete') { $instance = portfolio_instance($portfolio); diff --git a/admin/qbehaviours.php b/admin/qbehaviours.php index 85f0c8ef9053d..8e98af4237ead 100644 --- a/admin/qbehaviours.php +++ b/admin/qbehaviours.php @@ -92,6 +92,7 @@ $disabledbehaviours[] = $disable; set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question'); } + plugin_manager::reset_caches(); redirect($thispageurl); } @@ -109,6 +110,7 @@ unset($disabledbehaviours[$key]); set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question'); } + plugin_manager::reset_caches(); redirect($thispageurl); } diff --git a/admin/reports.php b/admin/reports.php index 0a73d226db9ba..879497c5222f6 100644 --- a/admin/reports.php +++ b/admin/reports.php @@ -30,6 +30,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/tablelib.php'); +require_once($CFG->libdir.'/pluginlib.php'); admin_externalpage_setup('managereports'); diff --git a/admin/repository.php b/admin/repository.php index 8f976cf32228c..05fc7557893ba 100644 --- a/admin/repository.php +++ b/admin/repository.php @@ -17,6 +17,7 @@ require_once(dirname(dirname(__FILE__)) . '/config.php'); require_once($CFG->dirroot . '/repository/lib.php'); require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); $repository = optional_param('repos', '', PARAM_ALPHANUMEXT); $action = optional_param('action', '', PARAM_ALPHANUMEXT); @@ -61,9 +62,6 @@ require_sesskey(); } -// Purge all caches related to repositories administration. -cache::make('core', 'plugininfo_repository')->purge(); - /** * Helper function that generates a moodle_url object * relevant to the repository @@ -151,6 +149,7 @@ function repository_action_url($repository) { } if ($success) { // configs saved + plugin_manager::reset_caches(); redirect($baseurl); } else { print_error('instancenotsaved', 'repository', $baseurl); @@ -191,6 +190,7 @@ function repository_action_url($repository) { print_error('invalidplugin', 'repository', '', $repository); } $repositorytype->update_visibility(true); + plugin_manager::reset_caches(); $return = true; } else if ($action == 'hide') { if (!confirm_sesskey()) { @@ -201,6 +201,7 @@ function repository_action_url($repository) { print_error('invalidplugin', 'repository', '', $repository); } $repositorytype->update_visibility(false); + plugin_manager::reset_caches(); $return = true; } else if ($action == 'delete') { $repositorytype = repository::get_type_by_typename($repository); @@ -211,6 +212,7 @@ function repository_action_url($repository) { } if ($repositorytype->delete($downloadcontents)) { + plugin_manager::reset_caches(); redirect($baseurl); } else { print_error('instancenotdeleted', 'repository', $baseurl); diff --git a/admin/repositoryinstance.php b/admin/repositoryinstance.php index b5eab73ffeaba..3def4f3ab5d77 100644 --- a/admin/repositoryinstance.php +++ b/admin/repositoryinstance.php @@ -17,6 +17,7 @@ require_once(dirname(dirname(__FILE__)) . '/config.php'); require_once($CFG->dirroot . '/repository/lib.php'); require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); require_sesskey(); @@ -102,6 +103,7 @@ $data = data_submitted(); } if ($success) { + plugin_manager::reset_caches(); redirect($parenturl); } else { print_error('instancenotsaved', 'repository', $parenturl); @@ -118,6 +120,7 @@ } else if (!empty($hide)) { $instance = repository::get_type_by_typename($hide); $instance->hide(); + plugin_manager::reset_caches(); $return = true; } else if (!empty($delete)) { $instance = repository::get_instance($delete); @@ -130,6 +133,7 @@ if ($sure) { if ($instance->delete($downloadcontents)) { $deletedstr = get_string('instancedeleted', 'repository'); + plugin_manager::reset_caches(); redirect($parenturl, $deletedstr, 3); } else { print_error('instancenotdeleted', 'repository', $parenturl); diff --git a/backup/converter/moodle1/handlerlib.php b/backup/converter/moodle1/handlerlib.php index c3df06bec7d59..3973a8bf3fc77 100644 --- a/backup/converter/moodle1/handlerlib.php +++ b/backup/converter/moodle1/handlerlib.php @@ -866,9 +866,11 @@ public function process_course_module($data, $raw) { // host... $versionfile = $CFG->dirroot.'/mod/'.$data['modulename'].'/version.php'; if (file_exists($versionfile)) { - $module = new stdClass(); + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; include($versionfile); - $data['version'] = $module->version; + $data['version'] = $plugin->version; } else { $data['version'] = null; } diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index e2f47f6498c15..ed53da7a5a7e2 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -307,6 +307,7 @@ protected function prepare_block_structure($blockstructure) { class backup_module_structure_step extends backup_structure_step { protected function define_structure() { + global $DB; // Define each element separated @@ -339,12 +340,14 @@ protected function define_structure() { $availinfo->add_child($availabilityfield); // Set the sources - $module->set_source_sql(' - SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber + $concat = $DB->sql_concat("'mod_'", 'm.name'); + $module->set_source_sql(" + SELECT cm.*, cp.value AS version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber FROM {course_modules} cm JOIN {modules} m ON m.id = cm.module + JOIN {config_plugins} cp ON cp.plugin = $concat AND cp.name = 'version' JOIN {course_sections} s ON s.id = cm.section - WHERE cm.id = ?', array(backup::VAR_MODID)); + WHERE cm.id = ?", array(backup::VAR_MODID)); $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID)); $availabilityfield->set_source_sql(' @@ -1363,7 +1366,7 @@ protected function define_structure() { } $blockrec->contextid = $this->task->get_contextid(); // Get the version of the block - $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname())); + $blockrec->version = get_config('block_'.$this->task->get_blockname(), 'version'); // Define sources diff --git a/blocks/upgrade.txt b/blocks/upgrade.txt index 23518050e1335..1a9ad4863dcf7 100644 --- a/blocks/upgrade.txt +++ b/blocks/upgrade.txt @@ -1,6 +1,10 @@ This files describes API changes in /blocks/* - activity modules, information provided here is intended especially for developers. +=== 2.6 === + +* Deprecated /admin/block.php was removed, make sure blocks are using settings.php instead. + === 2.4 === Created new capability 'blocks/xxx:myaddinstance' that determines whether a user can add diff --git a/cache/locks/file/version.php b/cache/locks/file/version.php new file mode 100644 index 0000000000000..b7955dab63ff6 --- /dev/null +++ b/cache/locks/file/version.php @@ -0,0 +1,30 @@ +. + +/** + * File locking for the Cache API + * + * @package cachelock_file + * @category cache + * @copyright 2012 Sam Hemelryk + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2013091300; // The current plugin version (Date: YYYYMMDDXX) +$plugin->requires = 2013091300; // Requires this Moodle version +$plugin->component = 'cachelock_file'; // Full name of the plugin (used for diagnostics) diff --git a/filter/manage.php b/filter/manage.php index 49d80ce1c2757..15b493cde35dd 100644 --- a/filter/manage.php +++ b/filter/manage.php @@ -25,6 +25,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); $contextid = required_param('contextid',PARAM_INT); $forfilter = optional_param('filter', '', PARAM_SAFEDIR); @@ -36,9 +37,6 @@ require_capability('moodle/filter:manage', $context); $PAGE->set_context($context); -// Purge all caches related to filter administration. -cache::make('core', 'plugininfo_filter')->purge(); - $args = array('contextid'=>$contextid); $baseurl = new moodle_url('/filter/manage.php', $args); if (!empty($forfilter)) { diff --git a/lang/en/cache.php b/lang/en/cache.php index 2ec0c97bc4c06..40021ed5c781b 100644 --- a/lang/en/cache.php +++ b/lang/en/cache.php @@ -51,12 +51,7 @@ $string['cachedef_langmenu'] = 'List of available languages'; $string['cachedef_locking'] = 'Locking'; $string['cachedef_observers'] = 'Event observers'; -$string['cachedef_plugininfo_base'] = 'Plugin info - base'; -$string['cachedef_plugininfo_block'] = 'Plugin info - blocks'; -$string['cachedef_plugininfo_filter'] = 'Plugin info - filters'; -$string['cachedef_plugininfo_mod'] = 'Plugin info - activity modules'; -$string['cachedef_plugininfo_portfolio'] = 'Plugin info - portfolios'; -$string['cachedef_plugininfo_repository'] = 'Plugin info - repositories'; +$string['cachedef_plugin_manager'] = 'Plugin info manager'; $string['cachedef_questiondata'] = 'Question definitions'; $string['cachedef_repositories'] = 'Repositories instances data'; $string['cachedef_string'] = 'Language string cache'; diff --git a/lib/adminlib.php b/lib/adminlib.php index efcf9a1be1382..7ca212768d93e 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -298,7 +298,10 @@ function uninstall_plugin($type, $name) { $DB->delete_records('log_display', array('component' => $component)); // delete the module configuration records - unset_all_config_for_plugin($pluginname); + unset_all_config_for_plugin($component); + if ($type === 'mod') { + unset_all_config_for_plugin($pluginname); + } // delete message provider message_provider_uninstall($component); @@ -375,9 +378,11 @@ function get_component_version($component, $source='installed') { if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) { return false; } else { - $module = new stdclass(); + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; include($mods[$name].'/version.php'); - return $module->version; + return $plugin->version; } } } diff --git a/lib/classes/component.php b/lib/classes/component.php index 0e21af390b48a..e193399b316ea 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -927,17 +927,11 @@ public static function get_all_versions_hash() { $plugs = self::fetch_plugins($type, $typedir); } foreach ($plugs as $plug => $fullplug) { - if ($type === 'mod') { - $module = new stdClass(); - $module->version = null; - include($fullplug.'/version.php'); - $versions[$type.'_'.$plug] = $module->version; - } else { - $plugin = new stdClass(); - $plugin->version = null; - @include($fullplug.'/version.php'); - $versions[$type.'_'.$plug] = $plugin->version; - } + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; + @include($fullplug.'/version.php'); + $versions[$type.'_'.$plug] = $plugin->version; } } diff --git a/lib/db/caches.php b/lib/db/caches.php index 305fdbca82529..0b1a99d45a415 100644 --- a/lib/db/caches.php +++ b/lib/db/caches.php @@ -141,58 +141,13 @@ 'persistentmaxsize' => 2, ), - // Cache used by the {@link plugininfo_base} class. - 'plugininfo_base' => array( + // Cache used by the {@link plugin_manager} class. + // NOTE: this must be a shared cache. + 'plugin_manager' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 2, - ), - - // Cache used by the {@link plugininfo_mod} class. - 'plugininfo_mod' => array( - 'mode' => cache_store::MODE_APPLICATION, - 'simplekeys' => true, - 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 1, - ), - - // Cache used by the {@link plugininfo_block} class. - 'plugininfo_block' => array( - 'mode' => cache_store::MODE_APPLICATION, - 'simplekeys' => true, - 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 1, - ), - - // Cache used by the {@link plugininfo_filter} class. - 'plugininfo_filter' => array( - 'mode' => cache_store::MODE_APPLICATION, - 'simplekeys' => true, - 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 1, - ), - - // Cache used by the {@link plugininfo_repository} class. - 'plugininfo_repository' => array( - 'mode' => cache_store::MODE_APPLICATION, - 'simplekeys' => true, - 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 1, - ), - - // Cache used by the {@link plugininfo_portfolio} class. - 'plugininfo_portfolio' => array( - 'mode' => cache_store::MODE_APPLICATION, - 'simplekeys' => true, - 'simpledata' => true, - 'persistent' => true, - 'persistentmaxsize' => 1, + 'persistent' => false, ), // Used to store the full tree of course categories diff --git a/lib/db/install.xml b/lib/db/install.xml index d79981f92ad3d..8fe1b98babc14 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -668,7 +668,6 @@ - @@ -2487,7 +2486,6 @@ - diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 543d1b3dd6867..4768e85a91aa3 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2450,5 +2450,52 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2013091300.01); } + if ($oldversion < 2013092001.01) { + // Force uninstall of deleted tool. + if (!file_exists("$CFG->dirroot/$CFG->admin/tool/bloglevelupgrade")) { + // Remove capabilities. + capabilities_cleanup('tool_bloglevelupgrade'); + // Remove all other associated config. + unset_all_config_for_plugin('tool_bloglevelupgrade'); + } + upgrade_main_savepoint(true, 2013092001.01); + } + + if ($oldversion < 2013092001.02) { + // Define field version to be dropped from modules. + $table = new xmldb_table('modules'); + $field = new xmldb_field('version'); + + // Conditionally launch drop field version. + if ($dbman->field_exists($table, $field)) { + // Migrate all plugin version info to config_plugins table. + $modules = $DB->get_records('modules'); + foreach ($modules as $module) { + set_config('version', $module->version, 'mod_'.$module->name); + } + unset($modules); + + $dbman->drop_field($table, $field); + } + + // Define field version to be dropped from block. + $table = new xmldb_table('block'); + $field = new xmldb_field('version'); + + // Conditionally launch drop field version. + if ($dbman->field_exists($table, $field)) { + $blocks = $DB->get_records('block'); + foreach ($blocks as $block) { + set_config('version', $block->version, 'block_'.$block->name); + } + unset($blocks); + + $dbman->drop_field($table, $field); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2013092001.02); + } + return true; } diff --git a/lib/editor/atto/lib.php b/lib/editor/atto/lib.php index f2ec963c1aaad..2b9b8a68c6f39 100644 --- a/lib/editor/atto/lib.php +++ b/lib/editor/atto/lib.php @@ -74,25 +74,20 @@ public function supports_repositories() { * @param null $fpoptions */ public function use_editor($elementid, array $options=null, $fpoptions=null) { - global $PAGE, $CFG; + global $PAGE; $PAGE->requires->yui_module('moodle-editor_atto-editor', 'M.editor_atto.init', array($this->get_init_params($elementid, $options, $fpoptions)), true); - require_once($CFG->libdir . '/pluginlib.php'); - - $pluginman = plugin_manager::instance(); - $plugins = $pluginman->get_subplugins_of_plugin('editor_atto'); - $sortedplugins = array(); + $plugins = core_component::get_plugin_list('atto'); - foreach ($plugins as $id => $plugin) { - $sortorder = component_callback($plugin->type . '_' . $plugin->name, 'sort_order', array($elementid)); - $sortedplugins[$sortorder] = $plugin; + foreach ($plugins as $name => $fulldir) { + $plugins[$name] = component_callback('atto_' . $name, 'sort_order', array($elementid)); } - ksort($sortedplugins); - foreach ($sortedplugins as $plugin) { - component_callback($plugin->type . '_' . $plugin->name, 'init_editor', array($elementid)); + asort($plugins); + foreach ($plugins as $name => $sort) { + component_callback('atto_' . $name, 'init_editor', array($elementid)); } } diff --git a/lib/editor/tinymce/adminlib.php b/lib/editor/tinymce/adminlib.php index d30f3bf56890a..2bc6130f06869 100644 --- a/lib/editor/tinymce/adminlib.php +++ b/lib/editor/tinymce/adminlib.php @@ -24,8 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -require_once("$CFG->libdir/pluginlib.php"); - /** * Editor subplugin info class. @@ -35,6 +33,34 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plugininfo_tinymce extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + $disabledsubplugins = array(); + $config = get_config('editor_tinymce', 'disabledsubplugins'); + if ($config) { + $config = explode(',', $config); + foreach ($config as $sp) { + $sp = trim($sp); + if ($sp !== '') { + $disabledsubplugins[$sp] = $sp; + } + } + } + + $enabled = array(); + $installed = core_component::get_plugin_list('tinymce'); + foreach ($installed as $plugin => $fulldir) { + if (isset($disabledsubplugins[$plugin])) { + continue; + } + $enabled[$plugin] = $plugin; + } + + return $enabled; + } public function is_uninstall_allowed() { return true; @@ -59,26 +85,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h $ADMIN->add($parentnodename, $settings); } } - - public function is_enabled() { - static $disabledsubplugins = null; // TODO: MDL-34344 remove this once get_config() is cached via MUC! - - if (is_null($disabledsubplugins)) { - $disabledsubplugins = array(); - $config = get_config('editor_tinymce', 'disabledsubplugins'); - if ($config) { - $config = explode(',', $config); - foreach ($config as $sp) { - $sp = trim($sp); - if ($sp !== '') { - $disabledsubplugins[$sp] = $sp; - } - } - } - } - - return !isset($disabledsubplugins[$this->name]); - } } diff --git a/lib/editor/tinymce/subplugins.php b/lib/editor/tinymce/subplugins.php index 29236e81fd34b..6eb01c416601d 100644 --- a/lib/editor/tinymce/subplugins.php +++ b/lib/editor/tinymce/subplugins.php @@ -24,6 +24,7 @@ require(__DIR__ . '/../../../config.php'); require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/pluginlib.php'); $disable = optional_param('disable', '', PARAM_PLUGIN); $enable = optional_param('enable', '', PARAM_PLUGIN); @@ -61,5 +62,6 @@ } set_config('disabledsubplugins', implode(',', $disabled), 'editor_tinymce'); +plugin_manager::reset_caches(); redirect($returnurl); diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 92091ea16dfc2..ad1e363caa497 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -1589,6 +1589,9 @@ function purge_all_caches() { theme_reset_all_caches(); get_string_manager()->reset_caches(); core_text::reset_caches(); + if (class_exists('plugin_manager')) { + plugin_manager::reset_caches(); + } // Bump up cacherev field for all courses. try { diff --git a/lib/pluginlib.php b/lib/pluginlib.php index cc8ae89267678..9172fc1cd4521 100644 --- a/lib/pluginlib.php +++ b/lib/pluginlib.php @@ -61,6 +61,12 @@ class plugin_manager { protected $pluginsinfo = null; /** @var array of raw subplugins information */ protected $subpluginsinfo = null; + /** @var array list of installed plugins $name=>$version */ + protected $installedplugins = null; + /** @var array list of all enabled plugins $name=>$name */ + protected $enabledplugins = null; + /** @var array list of all enabled plugins $name=>$diskversion */ + protected $presentplugins = null; /** * Direct initiation not allowed, use the factory method {@link self::instance()} @@ -87,94 +93,273 @@ public static function instance() { } /** - * Reset any caches + * Reset all caches. * @param bool $phpunitreset */ public static function reset_caches($phpunitreset = false) { if ($phpunitreset) { self::$singletoninstance = null; + } else { + if (self::$singletoninstance) { + self::$singletoninstance->pluginsinfo = null; + self::$singletoninstance->subpluginsinfo = null; + self::$singletoninstance->installedplugins = null; + self::$singletoninstance->enabledplugins = null; + self::$singletoninstance->presentplugins = null; + } } + $cache = cache::make('core', 'plugin_manager'); + $cache->purge(); } /** * Returns the result of {@link core_component::get_plugin_types()} ordered for humans * * @see self::reorder_plugin_types() - * @param bool $fullpaths false means relative paths from dirroot * @return array (string)name => (string)location */ - public function get_plugin_types($fullpaths = true) { - return $this->reorder_plugin_types(core_component::get_plugin_types($fullpaths)); + public function get_plugin_types() { + if (func_num_args() > 0) { + if (!func_get_arg(0)) { + throw coding_exception('plugin_manager->get_plugin_types() does not support relative paths.'); + } + } + return $this->reorder_plugin_types(core_component::get_plugin_types()); } /** - * Returns list of known plugins of the given type + * Load list of installed plugins, + * always call before using $this->installedplugins. * - * This method returns the subset of the tree returned by {@link self::get_plugins()}. - * If the given type is not known, empty array is returned. + * This method is caching results for all plugins. + */ + protected function load_installed_plugins() { + global $DB, $CFG; + + if ($this->installedplugins) { + return; + } + + if (empty($CFG->version)) { + // Nothing installed yet. + $this->installedplugins = array(); + return; + } + + $cache = cache::make('core', 'plugin_manager'); + $installed = $cache->get('installed'); + + if (is_array($installed)) { + $this->installedplugins = $installed; + return; + } + + $this->installedplugins = array(); + + if ($CFG->version < 2013092001.02) { + // We did not upgrade the database yet. + $modules = $DB->get_records('modules', array(), 'name ASC', 'id, name, version'); + foreach ($modules as $module) { + $this->installedplugins['mod'][$module->name] = $module->version; + } + $blocks = $DB->get_records('block', array(), 'name ASC', 'id, name, version'); + foreach ($blocks as $block) { + $this->installedplugins['block'][$block->name] = $block->version; + } + } + + $versions = $DB->get_records('config_plugins', array('name'=>'version')); + foreach ($versions as $version) { + $parts = explode('_', $version->plugin, 2); + if (!isset($parts[1])) { + // Invalid component, there must be at least one "_". + continue; + } + // Do not verify here if plugin type and name are valid. + $this->installedplugins[$parts[0]][$parts[1]] = $version->value; + } + + foreach ($this->installedplugins as $key => $value) { + ksort($this->installedplugins[$key]); + } + + $cache->set('installed', $this->installedplugins); + } + + /** + * Return list of installed plugins of given type. + * @param string $type + * @return array $name=>$version + */ + public function get_installed_plugins($type) { + $this->load_installed_plugins(); + if (isset($this->installedplugins[$type])) { + return $this->installedplugins[$type]; + } + return array(); + } + + /** + * Load list of all enabled plugins, + * call before using $this->enabledplugins. * - * @param string $type plugin type, e.g. 'mod' or 'workshopallocation' - * @param bool $disablecache force reload, cache can be used otherwise - * @return array (string)plugin name (e.g. 'workshop') => corresponding subclass of {@link plugininfo_base} + * This method is caching results from individual plugin info classes. */ - public function get_plugins_of_type($type, $disablecache=false) { + protected function load_enabled_plugins() { + global $CFG; - $plugins = $this->get_plugins($disablecache); + if ($this->enabledplugins) { + return; + } - if (!isset($plugins[$type])) { - return array(); + if (empty($CFG->version)) { + $this->enabledplugins = array(); + return; } - return $plugins[$type]; + $cache = cache::make('core', 'plugin_manager'); + $enabled = $cache->get('enabled'); + + if (is_array($enabled)) { + $this->enabledplugins = $enabled; + return; + } + + $this->enabledplugins = array(); + + require_once($CFG->libdir.'/adminlib.php'); + + $plugintypes = core_component::get_plugin_types(); + foreach ($plugintypes as $plugintype => $fulldir) { + // Hack: include mod and editor subplugin management classes first, + // the adminlib.php is supposed to contain extra admin settings too. + $plugininfoclass = 'plugininfo_' . $plugintype; + if (!class_exists($plugininfoclass) and file_exists("$fulldir/adminlib.php")) { + include_once("$fulldir/adminlib.php"); + } + if (class_exists($plugininfoclass)) { + $enabled = $plugininfoclass::get_enabled_plugins(); + if (!is_array($enabled)) { + continue; + } + $this->enabledplugins[$plugintype] = $enabled; + } + } + + $cache->set('enabled', $this->enabledplugins); + } + + /** + * Get list of enabled plugins of given type, + * the result may contain missing plugins. + * + * @param string $type + * @return array|null list of enabled plugins of this type, null if unknown + */ + public function get_enabled_plugins($type) { + $this->load_enabled_plugins(); + if (isset($this->enabledplugins[$type])) { + return $this->enabledplugins[$type]; + } + return null; + } + + /** + * Load list of all present plugins - call before using $this->presentplugins. + */ + protected function load_present_plugins() { + if ($this->presentplugins) { + return; + } + + $cache = cache::make('core', 'plugin_manager'); + $present = $cache->get('present'); + + if (is_array($present)) { + $this->presentplugins = $present; + return; + } + + $this->presentplugins = array(); + + $plugintypes = core_component::get_plugin_types(); + foreach ($plugintypes as $type => $typedir) { + $plugs = core_component::get_plugin_list($type); + foreach ($plugs as $plug => $fullplug) { + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; + @include($fullplug.'/version.php'); + $this->presentplugins[$type][$plug] = $plugin; + } + } + + $cache->set('present', $this->presentplugins); + } + + /** + * Get list of present plugins of given type. + * + * @param string $type + * @return array|null list of presnet plugins $name=>$diskversion, null if unknown + */ + public function get_present_plugins($type) { + $this->load_present_plugins(); + if (isset($this->presentplugins[$type])) { + return $this->presentplugins[$type]; + } + return null; } /** * Returns a tree of known plugins and information about them * - * @param bool $disablecache force reload, cache can be used otherwise * @return array 2D array. The first keys are plugin type names (e.g. qtype); * the second keys are the plugin local name (e.g. multichoice); and * the values are the corresponding objects extending {@link plugininfo_base} */ - public function get_plugins($disablecache=false) { + public function get_plugins() { global $CFG; - if ($disablecache or is_null($this->pluginsinfo)) { - // Hack: include mod and editor subplugin management classes first, - // the adminlib.php is supposed to contain extra admin settings too. - require_once($CFG->libdir.'/adminlib.php'); - foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { - foreach (core_component::get_plugin_list($type) as $dir) { - if (file_exists("$dir/adminlib.php")) { - include_once("$dir/adminlib.php"); - } + if (is_array($this->pluginsinfo)) { + return $this->pluginsinfo; + } + + $this->pluginsinfo = array(); + + // Hack: include mod and editor subplugin management classes first, + // the adminlib.php is supposed to contain extra admin settings too. + require_once($CFG->libdir.'/adminlib.php'); + foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { + foreach (core_component::get_plugin_list($type) as $dir) { + if (file_exists("$dir/adminlib.php")) { + include_once("$dir/adminlib.php"); } } - $this->pluginsinfo = array(); - $plugintypes = $this->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('plugininfo_' . $plugintype)) { - $plugintypeclass = 'plugininfo_' . $plugintype; - } else { - $plugintypeclass = 'plugininfo_general'; - } - if (!in_array('plugininfo_base', class_parents($plugintypeclass))) { - throw new coding_exception('Class ' . $plugintypeclass . ' must extend plugininfo_base'); - } - $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass); - $this->pluginsinfo[$plugintype] = $plugins; + } + $plugintypes = $this->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('plugininfo_' . $plugintype)) { + $plugintypeclass = 'plugininfo_' . $plugintype; + } else { + $plugintypeclass = 'plugininfo_general'; + } + if (!in_array('plugininfo_base', class_parents($plugintypeclass))) { + throw new coding_exception('Class ' . $plugintypeclass . ' must extend plugininfo_base'); } + $plugins = $plugintypeclass::get_plugins($plugintype, $plugintyperootdir, $plugintypeclass); + $this->pluginsinfo[$plugintype] = $plugins; + } - if (empty($CFG->disableupdatenotifications) and !during_initial_install()) { - // append the information about available updates provided by {@link available_update_checker()} - $provider = available_update_checker::instance(); - foreach ($this->pluginsinfo as $plugintype => $plugins) { - foreach ($plugins as $plugininfoholder) { - $plugininfoholder->check_available_updates($provider); - } + if (empty($CFG->disableupdatenotifications) and !during_initial_install()) { + // append the information about available updates provided by {@link available_update_checker()} + $provider = available_update_checker::instance(); + foreach ($this->pluginsinfo as $plugintype => $plugins) { + foreach ($plugins as $plugininfoholder) { + $plugininfoholder->check_available_updates($provider); } } } @@ -183,24 +368,43 @@ public function get_plugins($disablecache=false) { } /** - * Returns list of all known subplugins of the given plugin + * Returns list of known plugins of the given type. + * + * This method returns the subset of the tree returned by {@link self::get_plugins()}. + * If the given type is not known, empty array is returned. + * + * @param string $type plugin type, e.g. 'mod' or 'workshopallocation' + * @return array (string)plugin name (e.g. 'workshop') => corresponding subclass of {@link plugininfo_base} + */ + public function get_plugins_of_type($type) { + + $plugins = $this->get_plugins(); + + if (!isset($plugins[$type])) { + return array(); + } + + return $plugins[$type]; + } + + /** + * Returns list of all known subplugins of the given plugin. * * For plugins that do not provide subplugins (i.e. there is no support for it), * empty array is returned. * * @param string $component full component name, e.g. 'mod_workshop' - * @param bool $disablecache force reload, cache can be used otherwise * @return array (string) component name (e.g. 'workshopallocation_random') => subclass of {@link plugininfo_base} */ - public function get_subplugins_of_plugin($component, $disablecache=false) { + public function get_subplugins_of_plugin($component) { - $pluginfo = $this->get_plugin_info($component, $disablecache); + $pluginfo = $this->get_plugin_info($component); if (is_null($pluginfo)) { return array(); } - $subplugins = $this->get_subplugins($disablecache); + $subplugins = $this->get_subplugins(); if (!isset($subplugins[$pluginfo->component])) { return array(); @@ -221,28 +425,29 @@ public function get_subplugins_of_plugin($component, $disablecache=false) { * Returns list of plugins that define their subplugins and the information * about them from the db/subplugins.php file. * - * @param bool $disablecache force reload, cache can be used otherwise * @return array with keys like 'mod_quiz', and values the data from the * corresponding db/subplugins.php file. */ - public function get_subplugins($disablecache=false) { - - if ($disablecache or is_null($this->subpluginsinfo)) { - $this->subpluginsinfo = array(); - foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { - foreach (core_component::get_plugin_list($type) as $component => $ownerdir) { - $componentsubplugins = array(); - if (file_exists($ownerdir . '/db/subplugins.php')) { - $subplugins = array(); - include($ownerdir . '/db/subplugins.php'); - foreach ($subplugins as $subplugintype => $subplugintyperootdir) { - $subplugin = new stdClass(); - $subplugin->type = $subplugintype; - $subplugin->typerootdir = $subplugintyperootdir; - $componentsubplugins[$subplugintype] = $subplugin; - } - $this->subpluginsinfo[$type . '_' . $component] = $componentsubplugins; + public function get_subplugins() { + + if (is_array($this->subpluginsinfo)) { + return $this->subpluginsinfo; + } + + $this->subpluginsinfo = array(); + foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { + foreach (core_component::get_plugin_list($type) as $component => $ownerdir) { + $componentsubplugins = array(); + if (file_exists($ownerdir . '/db/subplugins.php')) { + $subplugins = array(); + include($ownerdir . '/db/subplugins.php'); + foreach ($subplugins as $subplugintype => $subplugintyperootdir) { + $subplugin = new stdClass(); + $subplugin->type = $subplugintype; + $subplugin->typerootdir = $subplugintyperootdir; + $componentsubplugins[$subplugintype] = $subplugin; } + $this->subpluginsinfo[$type . '_' . $component] = $componentsubplugins; } } } @@ -350,12 +555,11 @@ public function plugintype_name_plural($type) { * Returns information about the known plugin, or null * * @param string $component frankenstyle component name. - * @param bool $disablecache force reload, cache can be used otherwise * @return plugininfo_base|null the corresponding plugin information. */ - public function get_plugin_info($component, $disablecache=false) { - list($type, $name) = $this->normalize_component($component); - $plugins = $this->get_plugins($disablecache); + public function get_plugin_info($component) { + list($type, $name) = core_component::normalize_component($component); + $plugins = $this->get_plugins(); if (isset($plugins[$type][$name])) { return $plugins[$type][$name]; } else { @@ -637,6 +841,7 @@ public static function is_deleted_standard_plugin($type, $name) { $plugins = array( 'qformat' => array('blackboard'), 'enrol' => array('authorize'), + 'tool' => array('bloglevelupgrade'), ); if (!isset($plugins[$type])) { @@ -879,18 +1084,6 @@ public static function standard_plugins_list($type) { } } - /** - * Wrapper for the core function {@link core_component::normalize_component()}. - * - * This is here just to make it possible to mock it in unit tests. - * - * @param string $component - * @return array - */ - protected function normalize_component($component) { - return core_component::normalize_component($component); - } - /** * Reorders plugin types into a sequence to be displayed * @@ -2420,7 +2613,6 @@ public static function make($type, $typerootdir, $name, $namerootdir, $typeclass $plugin->init_display_name(); $plugin->load_disk_version(); $plugin->load_db_version(); - $plugin->load_required_main_version(); $plugin->init_is_standard(); return $plugin; @@ -2445,7 +2637,7 @@ abstract class plugininfo_base { 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 */ + /** @var string fullpath to the location of this plugin */ public $rootdir; /** @var int|string the version of the plugin's source code */ public $versiondisk; @@ -2463,7 +2655,16 @@ abstract class plugininfo_base { public $availableupdates; /** - * Gathers and returns the information about all plugins of the given type + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + return null; + } + + /** + * Gathers and returns the information about all plugins of the given type, + * either on disk or previously installed. * * @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 @@ -2471,15 +2672,35 @@ abstract class plugininfo_base { * @return array of plugintype classes, indexed by the plugin name */ public static function get_plugins($type, $typerootdir, $typeclass) { - - // get the information about plugins at the disk + // Get the information about plugins at the disk. $plugins = core_component::get_plugin_list($type); - $ondisk = array(); + $return = array(); foreach ($plugins as $pluginname => $pluginrootdir) { - $ondisk[$pluginname] = plugininfo_default_factory::make($type, $typerootdir, + $return[$pluginname] = plugininfo_default_factory::make($type, $typerootdir, $pluginname, $pluginrootdir, $typeclass); } - return $ondisk; + + // Fetch missing incorrectly uninstalled plugins. + $manager = plugin_manager::instance(); + $plugins = $manager->get_installed_plugins($type); + + foreach ($plugins as $name => $version) { + if (isset($return[$name])) { + continue; + } + $plugin = new $typeclass(); + $plugin->type = $type; + $plugin->typerootdir = $typerootdir; + $plugin->name = $name; + $plugin->rootdir = null; + $plugin->displayname = $name; + $plugin->versiondb = $version; + $plugin->init_is_standard(); + + $return[$name] = $plugin; + } + + return $return; } /** @@ -2524,34 +2745,6 @@ public function full_path($relativepath) { return $this->rootdir . '/' . $relativepath; } - /** - * Load the data from version.php. - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return stdClass the object called $plugin defined in version.php - */ - protected function load_version_php($disablecache=false) { - - $cache = cache::make('core', 'plugininfo_base'); - - $versionsphp = $cache->get('versions_php'); - - if (!$disablecache and $versionsphp !== false and isset($versionsphp[$this->component])) { - return $versionsphp[$this->component]; - } - - $versionfile = $this->full_path('version.php'); - - $plugin = new stdClass(); - if (is_readable($versionfile)) { - include($versionfile); - } - $versionsphp[$this->component] = $plugin; - $cache->set('versions_php', $versionsphp); - - return $plugin; - } - /** * Sets {@link $versiondisk} property to a numerical value representing the * version of the plugin's source code. @@ -2561,33 +2754,26 @@ protected function load_version_php($disablecache=false) { * data) or is missing from disk. */ public function load_disk_version() { - $plugin = $this->load_version_php(); + $versions = plugin_manager::instance()->get_present_plugins($this->type); + + $this->versiondisk = null; + $this->versionrequires = null; + $this->dependencies = array(); + + if (!isset($versions[$this->name])) { + return; + } + + $plugin = $versions[$this->name]; + if (isset($plugin->version)) { $this->versiondisk = $plugin->version; } - } - - /** - * Sets {@link $versionrequires} property to a numerical value representing - * the version of Moodle core that this plugin requires. - */ - public function load_required_main_version() { - $plugin = $this->load_version_php(); if (isset($plugin->requires)) { $this->versionrequires = $plugin->requires; } - } - - /** - * Initialise {@link $dependencies} to the list of other plugins (in any) - * that this one requires to be installed. - */ - protected function load_other_required_plugins() { - $plugin = $this->load_version_php(); - if (!empty($plugin->dependencies)) { + if (isset($plugin->dependencies)) { $this->dependencies = $plugin->dependencies; - } else { - $this->dependencies = array(); // By default, no dependencies. } } @@ -2599,7 +2785,7 @@ protected function load_other_required_plugins() { */ public function get_other_required_plugins() { if (is_null($this->dependencies)) { - $this->load_other_required_plugins(); + $this->load_disk_version(); } return $this->dependencies; } @@ -2631,8 +2817,12 @@ public function get_parent_plugin() { * data) or has not been installed yet. */ public function load_db_version() { - if ($ver = self::get_version_from_config_plugins($this->component)) { - $this->versiondb = $ver; + $versions = plugin_manager::instance()->get_installed_plugins($this->type); + + if (isset($versions[$this->name])) { + $this->versiondb = $versions[$this->name]; + } else { + $this->versiondb = null; } } @@ -2707,7 +2897,9 @@ public function get_status() { return plugin_manager::PLUGIN_STATUS_MISSING; } - } else if ((string)$this->versiondb === (string)$this->versiondisk) { + } else if ((float)$this->versiondb === (float)$this->versiondisk) { + // Note: the float comparison should work fine here + // because there are no arithmetic operations with the numbers. return plugin_manager::PLUGIN_STATUS_UPTODATE; } else if ($this->versiondb < $this->versiondisk) { @@ -2733,7 +2925,18 @@ public function get_status() { * @return null|bool */ public function is_enabled() { - return null; + if (!$this->rootdir) { + // Plugin missing. + return false; + } + + $enabled = plugin_manager::instance()->get_enabled_plugins($this->type); + + if (!is_array($enabled)) { + return null; + } + + return isset($enabled[$this->name]); } /** @@ -2842,7 +3045,7 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h * Note that even if true is returned, the core may still prohibit the uninstallation, * e.g. in case there are other plugins that depend on this one. * - * @return boolean + * @return bool */ public function is_uninstall_allowed() { @@ -2920,41 +3123,10 @@ protected final function get_default_uninstall_url() { )); } - /** - * Provides access to plugin versions from the {config_plugins} table - * - * @param string $plugin plugin name - * @param bool $disablecache do not attempt to obtain data from the cache - * @return int|bool the stored value or false if not found - */ - protected function get_version_from_config_plugins($plugin, $disablecache=false) { - global $DB; - - $cache = cache::make('core', 'plugininfo_base'); - - $pluginversions = $cache->get('versions_db'); - - if ($pluginversions === false or $disablecache) { - try { - $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value'); - } catch (dml_exception $e) { - // before install - $pluginversions = array(); - } - $cache->set('versions_db', $pluginversions); - } - - if (isset($pluginversions[$plugin])) { - return $pluginversions[$plugin]; - } else { - return false; - } - } - /** * Provides access to the plugin_manager singleton. * - * @return plugin_manmager + * @return plugin_manager */ protected function get_plugin_manager() { return plugin_manager::instance(); @@ -2973,31 +3145,14 @@ class plugininfo_general extends plugininfo_base { * Class for page side blocks */ class plugininfo_block extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB; - 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->init_is_standard(); - - $blocks[$blockname] = $plugin; - } - - return $blocks; + return $DB->get_records_menu('block', array('visible'=>1), 'name ASC', 'name, name AS val'); } /** @@ -3030,29 +3185,6 @@ public function init_display_name() { } } - public function load_db_version() { - global $DB; - - $blocksinfo = self::get_blocks_info(); - if (isset($blocksinfo[$this->name]->version)) { - $this->versiondb = $blocksinfo[$this->name]->version; - } - } - - 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(); - } - } - public function get_settings_section_name() { return 'blocksetting' . $this->name; } @@ -3061,6 +3193,12 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $block = $this; // also can be used inside settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); if (!$hassiteconfig || (($blockinstance = block_instance($this->name)) === false)) { @@ -3073,11 +3211,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h $settings = new admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false); include($this->full_path('settings.php')); // this may also set $settings to null - } else { - $blocksinfo = self::get_blocks_info(); - $settingsurl = new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id)); - $settings = new admin_externalpage($section, $this->displayname, - $settingsurl, 'moodle/site:config', $this->is_enabled() === false); } } if ($settings) { @@ -3103,33 +3236,7 @@ public function get_uninstall_extra_warning() { return '

'.get_string('uninstallextraconfirmblock', 'core_plugin', array('instances'=>$count)).'

'; } - - /** - * Provides access to the records in {block} table - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return array array of stdClasses - */ - protected static function get_blocks_info($disablecache=false) { - global $DB; - - $cache = cache::make('core', 'plugininfo_block'); - - $blocktypes = $cache->get('blocktypes'); - - if ($blocktypes === false or $disablecache) { - try { - $blocktypes = $DB->get_records('block', null, 'name', 'name,id,version,visible'); - } catch (dml_exception $e) { - // before install - $blocktypes = array(); - } - $cache->set('blocktypes', $blocktypes); - } - - return $blocktypes; - } -} +} /** @@ -3137,80 +3244,29 @@ protected static function get_blocks_info($disablecache=false) { */ class plugininfo_filter extends plugininfo_base { - public static function get_plugins($type, $typerootdir, $typeclass) { - global $CFG, $DB; - - $filters = array(); - - // get the list of filters in /filter location - $installed = filter_get_all_installed(); - - foreach ($installed as $name => $displayname) { - $plugin = new $typeclass(); - $plugin->type = $type; - $plugin->typerootdir = $typerootdir; - $plugin->name = $name; - $plugin->rootdir = "$CFG->dirroot/filter/$name"; - $plugin->displayname = $displayname; - - $plugin->load_disk_version(); - $plugin->load_db_version(); - $plugin->load_required_main_version(); - $plugin->init_is_standard(); - - $filters[$plugin->name] = $plugin; - } - - // Do not mess with filter registration here! - - $globalstates = self::get_global_states(); - - // 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/filter/$name"; - $plugin->displayname = $name; - - $plugin->load_db_version(); - - 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; - } - public function init_display_name() { - // do nothing, the name is set in self::get_plugins() + if (!get_string_manager()->string_exists('filtername', $this->component)) { + $this->displayname = '[filtername,' . $this->component . ']'; + } else { + $this->displayname = get_string('filtername', $this->component); + } } - public function is_enabled() { - - $globalstates = self::get_global_states(); + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB, $CFG; + require_once("$CFG->libdir/filterlib.php"); - foreach ($globalstates as $name => $info) { - if ($name === $this->name) { - if ($info->active == TEXTFILTER_DISABLED) { - return false; - } else { - // it may be 'On' or 'Off, but available' - return null; - } - } + $enabled = array(); + $filters = $DB->get_records_select('filter_active', "active <> :disabled", array('disabled'=>TEXTFILTER_DISABLED), 'filter ASC', 'id, filter'); + foreach ($filters as $filter) { + $enabled[$filter->filter] = $filter->filter; } - return null; + return $enabled; } public function get_settings_section_name() { @@ -3222,6 +3278,11 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h $ADMIN = $adminroot; // may be used in settings.php $filter = $this; // also can be used inside settings.php + if (!$this->rootdir) { + // Plugin missing. + return; + } + $settings = null; if ($hassiteconfig && file_exists($this->full_path('filtersettings.php'))) { $section = $this->get_settings_section_name(); @@ -3241,50 +3302,6 @@ public function is_uninstall_allowed() { public function get_uninstall_url() { return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $this->name, 'action' => 'delete')); } - - /** - * 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 do not attempt to obtain data from the cache - * @return array - */ - protected static function get_global_states($disablecache=false) { - global $DB; - - $cache = cache::make('core', 'plugininfo_filter'); - - $globalstates = $cache->get('globalstates'); - - if ($globalstates === false or $disablecache) { - - if (!$DB->get_manager()->table_exists('filter_active')) { - // Not installed yet. - $cache->set('globalstates', array()); - return array(); - } - - $globalstates = array(); - - foreach (filter_get_global_states() as $name => $info) { - if (strpos($name, '/') !== false) { - // Skip existing before upgrade to new names. - continue; - } - - $filterinfo = new stdClass(); - $filterinfo->active = $info->active; - $filterinfo->sortorder = $info->sortorder; - $globalstates[$name] = $filterinfo; - } - - $cache->set('globalstates', $globalstates); - } - - return $globalstates; - } } @@ -3292,31 +3309,13 @@ protected static function get_global_states($disablecache=false) { * Class for activity modules */ class plugininfo_mod extends plugininfo_base { - - 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->init_is_standard(); - - $modules[$modulename] = $plugin; - } - - return $modules; + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB; + return $DB->get_records_menu('modules', array('visible'=>1), 'name ASC', 'name, name AS val'); } /** @@ -3344,61 +3343,6 @@ public function init_display_name() { } } - /** - * Load the data from version.php. - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return object the data object defined in version.php. - */ - protected function load_version_php($disablecache=false) { - - $cache = cache::make('core', 'plugininfo_mod'); - - $versionsphp = $cache->get('versions_php'); - - if (!$disablecache and $versionsphp !== false and isset($versionsphp[$this->component])) { - return $versionsphp[$this->component]; - } - - $versionfile = $this->full_path('version.php'); - - $module = new stdClass(); - $plugin = new stdClass(); - if (is_readable($versionfile)) { - include($versionfile); - } - if (!isset($module->version) and isset($plugin->version)) { - $module = $plugin; - } - $versionsphp[$this->component] = $module; - $cache->set('versions_php', $versionsphp); - - return $module; - } - - public function load_db_version() { - global $DB; - - $modulesinfo = self::get_modules_info(); - if (isset($modulesinfo[$this->name]->version)) { - $this->versiondb = $modulesinfo[$this->name]->version; - } - } - - 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(); - } - } - public function get_settings_section_name() { return 'modsetting' . $this->name; } @@ -3407,11 +3351,16 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $module = $this; // also can be used inside settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); - $modulesinfo = self::get_modules_info(); $settings = null; - if ($hassiteconfig && isset($modulesinfo[$this->name]) && file_exists($this->full_path('settings.php'))) { + if ($hassiteconfig && file_exists($this->full_path('settings.php'))) { $settings = new admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false); include($this->full_path('settings.php')); // this may also set $settings to null @@ -3462,32 +3411,6 @@ public function get_uninstall_extra_warning() { return '

'.get_string('uninstallextraconfirmmod', 'core_plugin', array('instances'=>$count, 'courses'=>$courses)).'

'; } - - /** - * Provides access to the records in {modules} table - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return array array of stdClasses - */ - protected static function get_modules_info($disablecache=false) { - global $DB; - - $cache = cache::make('core', 'plugininfo_mod'); - - $modulesinfo = $cache->get('modulesinfo'); - - if ($modulesinfo === false or $disablecache) { - try { - $modulesinfo = $DB->get_records('modules', null, 'name', 'name,id,version,visible'); - } catch (dml_exception $e) { - // before install - $modulesinfo = array(); - } - $cache->set('modulesinfo', $modulesinfo); - } - - return $modulesinfo; - } } @@ -3529,6 +3452,11 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $qtype = $this; // also can be used inside settings.php + + if (!$this->rootdir) { + // Most probably somebody deleted dir without proper uninstall. + return; + } $section = $this->get_settings_section_name(); $settings = null; @@ -3550,18 +3478,20 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h * Class for authentication plugins */ class plugininfo_auth extends plugininfo_base { - - public function is_enabled() { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { global $CFG; - if (in_array($this->name, array('nologin', 'manual'))) { - // these two are always enabled and can't be disabled - return null; + // These two are always enabled and can't be disabled. + $enabled = array('nologin'=>'nologin', 'manual'=>'manual'); + foreach (explode(',', $CFG->auth) as $auth) { + $enabled[$auth] = $auth; } - $enabled = array_flip(explode(',', $CFG->auth)); - - return isset($enabled[$this->name]); + return $enabled; } public function get_settings_section_name() { @@ -3572,6 +3502,12 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $auth = $this; // also to be used inside settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); $settings = null; @@ -3598,18 +3534,19 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h * Class for enrolment plugins */ class plugininfo_enrol extends plugininfo_base { - - public function is_enabled() { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { global $CFG; - // 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 - - $enabled = array_flip(explode(',', $CFG->enrol_plugins_enabled)); + $enabled = array(); + foreach (explode(',', $CFG->enrol_plugins_enabled) as $enrol) { + $enabled[$enrol] = $enrol; + } - return isset($enabled[$this->name]); + return $enabled; } public function get_settings_section_name() { @@ -3623,6 +3560,11 @@ public function get_settings_section_name() { public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them + if (!$this->rootdir) { + // Plugin missing. + return; + } + if (!$hassiteconfig or !file_exists($this->full_path('settings.php'))) { return; } @@ -3681,6 +3623,14 @@ public function get_uninstall_extra_warning() { * Class for messaging processors */ class plugininfo_message extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB; + return $DB->get_records_menu('message_processors', array('enabled'=>1), 'name ASC', 'name, name AS val'); + } public function get_settings_section_name() { return 'messagesetting' . $this->name; @@ -3689,6 +3639,12 @@ public function get_settings_section_name() { public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + if (!$hassiteconfig) { return; } @@ -3709,18 +3665,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h } } - /** - * @see plugintype_interface::is_enabled() - */ - public function is_enabled() { - $processors = get_message_processors(); - if (isset($processors[$this->name])) { - return $processors[$this->name]->configured && $processors[$this->name]->enabled; - } else { - return parent::is_enabled(); - } - } - public function is_uninstall_allowed() { $processors = get_message_processors(); if (isset($processors[$this->name])) { @@ -3744,12 +3688,13 @@ public function get_uninstall_url() { * Class for repositories */ class plugininfo_repository extends plugininfo_base { - - public function is_enabled() { - - $enabled = self::get_enabled_repositories(); - - return isset($enabled[$this->name]); + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB; + return $DB->get_records_menu('repository', array('visible'=>1), 'type ASC', 'type, type AS val'); } public function get_settings_section_name() { @@ -3757,6 +3702,11 @@ public function get_settings_section_name() { } public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { + if (!$this->rootdir) { + // Plugin missing. + return; + } + if ($hassiteconfig && $this->is_enabled()) { // completely no access to repository setting when it is not enabled $sectionname = $this->get_settings_section_name(); @@ -3767,27 +3717,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h $adminroot->add($parentnodename, $settings); } } - - /** - * Provides access to the records in {repository} table - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return array array of stdClasses - */ - protected static function get_enabled_repositories($disablecache=false) { - global $DB; - - $cache = cache::make('core', 'plugininfo_repository'); - - $enabled = $cache->get('enabled'); - - if ($enabled === false or $disablecache) { - $enabled = $DB->get_records('repository', null, 'type', 'type,visible,sortorder'); - $cache->set('enabled', $enabled); - } - - return $enabled; - } } @@ -3795,44 +3724,17 @@ protected static function get_enabled_repositories($disablecache=false) { * Class for portfolios */ class plugininfo_portfolio extends plugininfo_base { - - public function is_enabled() { - - $enabled = self::get_enabled_portfolios(); - - return isset($enabled[$this->name]); - } - /** - * Returns list of enabled portfolio plugins - * - * Portfolio plugin is enabled if there is at least one record in the {portfolio_instance} - * table for it. - * - * @param bool $disablecache do not attempt to obtain data from the cache - * @return array array of stdClasses with properties plugin and visible indexed by plugin + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown */ - protected static function get_enabled_portfolios($disablecache=false) { + public static function get_enabled_plugins() { global $DB; - $cache = cache::make('core', 'plugininfo_portfolio'); - - $enabled = $cache->get('enabled'); - - if ($enabled === false or $disablecache) { - $enabled = array(); - $instances = $DB->get_recordset('portfolio_instance', null, '', 'plugin,visible'); - foreach ($instances as $instance) { - if (isset($enabled[$instance->plugin])) { - if ($instance->visible) { - $enabled[$instance->plugin]->visible = $instance->visible; - } - } else { - $enabled[$instance->plugin] = $instance; - } - } - $instances->close(); - $cache->set('enabled', $enabled); + $enabled = array(); + $rs = $DB->get_recordset('portfolio_instance', array('visible'=>1), 'plugin ASC', 'plugin'); + foreach ($rs as $repository) { + $enabled[$repository->plugin] = $repository->plugin; } return $enabled; @@ -3911,6 +3813,24 @@ public function is_uninstall_allowed() { * Class for HTML editors */ class plugininfo_editor extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $CFG; + + if (empty($CFG->texteditors)) { + return array('tinymce'=>'tinymce', 'textarea'=>'textarea'); + } + + $enabled = array(); + foreach (explode(',', $CFG->texteditors) as $editor) { + $enabled[$editor] = $editor; + } + + return $enabled; + } public function get_settings_section_name() { return 'editorsettings' . $this->name; @@ -3920,6 +3840,12 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $editor = $this; // also can be used inside settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); $settings = null; @@ -3943,27 +3869,6 @@ public function is_uninstall_allowed() { return true; } } - - /** - * 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() { - global $CFG; - if (empty($CFG->texteditors)) { - $CFG->texteditors = 'tinymce,textarea'; - } - if (in_array($this->name, explode(',', $CFG->texteditors))) { - return true; - } - return false; - } } /** @@ -3976,6 +3881,11 @@ public function get_settings_section_name() { } public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { + if (!$this->rootdir) { + // Plugin missing. + return; + } + // plagiarism plugin just redirect to settings.php in the plugins directory if ($hassiteconfig && file_exists($this->full_path('settings.php'))) { $section = $this->get_settings_section_name(); @@ -3995,6 +3905,24 @@ public function is_uninstall_allowed() { * Class for webservice protocols */ class plugininfo_webservice extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $CFG; + + if (empty($CFG->enablewebservices) or empty($CFG->webserviceprotocols)) { + return array(); + } + + $enabled = array(); + foreach (explode(',', $CFG->webserviceprotocols) as $protocol) { + $enabled[$protocol] = $protocol; + } + + return $enabled; + } public function get_settings_section_name() { return 'webservicesetting' . $this->name; @@ -4004,6 +3932,12 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // may be used in settings.php $webservice = $this; // also can be used inside settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); $settings = null; @@ -4017,18 +3951,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h } } - public function is_enabled() { - global $CFG; - if (empty($CFG->enablewebservices)) { - return false; - } - $active_webservices = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols); - if (in_array($this->name, $active_webservices)) { - return true; - } - return false; - } - public function is_uninstall_allowed() { return false; } @@ -4038,6 +3960,36 @@ public function is_uninstall_allowed() { * Class for course formats */ class plugininfo_format extends plugininfo_base { + /** + * Finds all enabled plugins, the result may include missing plugins. + * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown + */ + public static function get_enabled_plugins() { + global $DB; + + $plugins = plugin_manager::instance()->get_installed_plugins('format'); + $installed = array(); + foreach ($plugins as $plugin => $version) { + $installed[] = 'format_'.$plugin; + } + + list($installed, $params) = $DB->get_in_or_equal($installed, SQL_PARAMS_NAMED); + $disabled = $DB->get_recordset_select('config_plugins', "plugin $installed AND name = 'disabled'", $params, 'plugin ASC'); + foreach ($disabled as $conf) { + if (empty($conf->value)) { + continue; + } + list($type, $name) = explode('_', $conf->component, 2); + unset($plugins[$name]); + } + + $enabled = array(); + foreach ($plugins as $plugin => $version) { + $enabled[$plugin] = $plugin; + } + + return $enabled; + } /** * Gathers and returns the information about all plugins of the given type @@ -4066,6 +4018,12 @@ public function get_settings_section_name() { public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them $ADMIN = $adminroot; // also may be used in settings.php + + if (!$this->rootdir) { + // Plugin missing. + return; + } + $section = $this->get_settings_section_name(); $settings = null; @@ -4079,10 +4037,6 @@ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $h } } - public function is_enabled() { - return !get_config($this->component, 'disabled'); - } - public function is_uninstall_allowed() { if ($this->name !== get_config('moodlecourse', 'format') && $this->name !== 'site') { return true; diff --git a/lib/tests/available_update_checker_test.php b/lib/tests/available_update_checker_test.php new file mode 100644 index 0000000000000..b1d9f27bb29ea --- /dev/null +++ b/lib/tests/available_update_checker_test.php @@ -0,0 +1,290 @@ +. + +/** + * Unit tests for the update checker. + * + * @package core + * @category phpunit + * @copyright 2012 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/pluginlib.php'); +require_once(__DIR__.'/available_update_deployer_test.php'); + + +/** + * Tests of the basic API of the available update checker. + */ +class core_available_update_checker_testcase extends advanced_testcase { + + public function test_core_available_update() { + $provider = testable_available_update_checker::instance(); + $this->assertInstanceOf('available_update_checker', $provider); + + $provider->fake_current_environment(2012060102.00, '2.3.2 (Build: 20121012)', '2.3', array()); + $updates = $provider->get_update_info('core'); + $this->assertCount(2, $updates); + + $provider->fake_current_environment(2012060103.00, '2.3.3 (Build: 20121212)', '2.3', array()); + $updates = $provider->get_update_info('core'); + $this->assertCount(1, $updates); + + $provider->fake_current_environment(2012060103.00, '2.3.3 (Build: 20121212)', '2.3', array()); + $updates = $provider->get_update_info('core', array('minmaturity' => MATURITY_STABLE)); + $this->assertNull($updates); + } + + /** + * If there are no fetched data yet, the first cron should fetch them. + */ + public function test_cron_initial_fetch() { + $provider = testable_available_update_checker::instance(); + $provider->fakerecentfetch = null; + $provider->fakecurrenttimestamp = -1; + $this->setExpectedException('testable_available_update_checker_cron_executed'); + $provider->cron(); + } + + /** + * If there is a fresh fetch available, no cron execution is expected. + */ + public function test_cron_has_fresh_fetch() { + $provider = testable_available_update_checker::instance(); + $provider->fakerecentfetch = time() - 23 * HOURSECS; // Fetched 23 hours ago. + $provider->fakecurrenttimestamp = -1; + $provider->cron(); + $this->assertTrue(true); // We should get here with no exception thrown. + } + + /** + * If there is an outdated fetch, the cron execution is expected. + */ + public function test_cron_has_outdated_fetch() { + $provider = testable_available_update_checker::instance(); + $provider->fakerecentfetch = time() - 49 * HOURSECS; // Fetched 49 hours ago. + $provider->fakecurrenttimestamp = -1; + $this->setExpectedException('testable_available_update_checker_cron_executed'); + $provider->cron(); + } + + /** + * The first cron after 01:42 AM today should fetch the data. + * + * @see testable_available_update_checker::cron_execution_offset() + */ + public function test_cron_offset_execution_not_yet() { + $provider = testable_available_update_checker::instance(); + $provider->fakecurrenttimestamp = mktime(1, 40, 02); // 01:40:02 AM today + $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS; + $provider->cron(); + $this->assertTrue(true); // We should get here with no exception thrown. + } + + /** + * The first cron after 01:42 AM today should fetch the data and then + * it is supposed to wait next 24 hours. + * + * @see testable_available_update_checker::cron_execution_offset() + */ + public function test_cron_offset_execution() { + $provider = testable_available_update_checker::instance(); + + // The cron at 01:45 should fetch the data. + $provider->fakecurrenttimestamp = mktime(1, 45, 02); // 01:45:02 AM today + $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS - 1; + $executed = false; + try { + $provider->cron(); + } catch (testable_available_update_checker_cron_executed $e) { + $executed = true; + } + $this->assertTrue($executed, 'Cron should be executed at 01:45:02 but it was not.'); + + // Another cron at 06:45 should still consider data as fresh enough. + $provider->fakerecentfetch = $provider->fakecurrenttimestamp; + $provider->fakecurrenttimestamp = mktime(6, 45, 03); // 06:45:03 AM + $executed = false; + try { + $provider->cron(); + } catch (testable_available_update_checker_cron_executed $e) { + $executed = true; + } + $this->assertFalse($executed, 'Cron should not be executed at 06:45:03 but it was.'); + + // The next scheduled execution should happen the next day. + $provider->fakecurrenttimestamp = $provider->fakerecentfetch + 24 * HOURSECS + 1; + $executed = false; + try { + $provider->cron(); + } catch (testable_available_update_checker_cron_executed $e) { + $executed = true; + } + $this->assertTrue($executed, 'Cron should be executed the next night but it was not.'); + } + + public function test_compare_responses_both_empty() { + $provider = testable_available_update_checker::instance(); + $old = array(); + $new = array(); + $cmp = $provider->compare_responses($old, $new); + $this->assertInternalType('array', $cmp); + $this->assertEmpty($cmp); + } + + public function test_compare_responses_old_empty() { + $provider = testable_available_update_checker::instance(); + $old = array(); + $new = array( + 'updates' => array( + 'core' => array( + array( + 'version' => 2012060103 + ) + ) + ) + ); + $cmp = $provider->compare_responses($old, $new); + $this->assertInternalType('array', $cmp); + $this->assertNotEmpty($cmp); + $this->assertTrue(isset($cmp['core'][0]['version'])); + $this->assertEquals(2012060103, $cmp['core'][0]['version']); + } + + public function test_compare_responses_no_change() { + $provider = testable_available_update_checker::instance(); + $old = $new = array( + 'updates' => array( + 'core' => array( + array( + 'version' => 2012060104 + ), + array( + 'version' => 2012120100 + ) + ), + 'mod_foo' => array( + array( + 'version' => 2011010101 + ) + ) + ) + ); + $cmp = $provider->compare_responses($old, $new); + $this->assertInternalType('array', $cmp); + $this->assertEmpty($cmp); + } + + public function test_compare_responses_new_and_missing_update() { + $provider = testable_available_update_checker::instance(); + $old = array( + 'updates' => array( + 'core' => array( + array( + 'version' => 2012060104 + ) + ), + 'mod_foo' => array( + array( + 'version' => 2011010101 + ) + ) + ) + ); + $new = array( + 'updates' => array( + 'core' => array( + array( + 'version' => 2012060104 + ), + array( + 'version' => 2012120100 + ) + ) + ) + ); + $cmp = $provider->compare_responses($old, $new); + $this->assertInternalType('array', $cmp); + $this->assertNotEmpty($cmp); + $this->assertCount(1, $cmp); + $this->assertCount(1, $cmp['core']); + $this->assertEquals(2012120100, $cmp['core'][0]['version']); + } + + public function test_compare_responses_modified_update() { + $provider = testable_available_update_checker::instance(); + $old = array( + 'updates' => array( + 'mod_foo' => array( + array( + 'version' => 2011010101 + ) + ) + ) + ); + $new = array( + 'updates' => array( + 'mod_foo' => array( + array( + 'version' => 2011010102 + ) + ) + ) + ); + $cmp = $provider->compare_responses($old, $new); + $this->assertInternalType('array', $cmp); + $this->assertNotEmpty($cmp); + $this->assertCount(1, $cmp); + $this->assertCount(1, $cmp['mod_foo']); + $this->assertEquals(2011010102, $cmp['mod_foo'][0]['version']); + } + + public function test_compare_responses_invalid_format() { + $provider = testable_available_update_checker::instance(); + $broken = array( + 'status' => 'ERROR' // No 'updates' key here. + ); + $this->setExpectedException('available_update_checker_exception'); + $cmp = $provider->compare_responses($broken, $broken); + } + + public function test_is_same_release_explicit() { + $provider = testable_available_update_checker::instance(); + $this->assertTrue($provider->is_same_release('2.3dev (Build: 20120323)', '2.3dev (Build: 20120323)')); + $this->assertTrue($provider->is_same_release('2.3dev (Build: 20120323)', '2.3dev (Build: 20120330)')); + $this->assertFalse($provider->is_same_release('2.3dev (Build: 20120529)', '2.3 (Build: 20120601)')); + $this->assertFalse($provider->is_same_release('2.3dev', '2.3 dev')); + $this->assertFalse($provider->is_same_release('2.3.1', '2.3')); + $this->assertFalse($provider->is_same_release('2.3.1', '2.3.2')); + $this->assertTrue($provider->is_same_release('2.3.2+', '2.3.2')); // Yes, really! + $this->assertTrue($provider->is_same_release('2.3.2 (Build: 123456)', '2.3.2+ (Build: 123457)')); + $this->assertFalse($provider->is_same_release('3.0 Community Edition', '3.0 Enterprise Edition')); + $this->assertTrue($provider->is_same_release('3.0 Community Edition', '3.0 Community Edition (Build: 20290101)')); + } + + public function test_is_same_release_implicit() { + $provider = testable_available_update_checker::instance(); + $provider->fake_current_environment(2012060102.00, '2.3.2 (Build: 20121012)', '2.3', array()); + $this->assertTrue($provider->is_same_release('2.3.2')); + $this->assertTrue($provider->is_same_release('2.3.2+')); + $this->assertTrue($provider->is_same_release('2.3.2+ (Build: 20121013)')); + $this->assertFalse($provider->is_same_release('2.4dev (Build: 20121012)')); + } +} diff --git a/lib/tests/available_update_deployer_test.php b/lib/tests/available_update_deployer_test.php new file mode 100644 index 0000000000000..23e75c52d8ce5 --- /dev/null +++ b/lib/tests/available_update_deployer_test.php @@ -0,0 +1,209 @@ +. + +/** + * Unit tests for the update deployer. + * + * @package core + * @category phpunit + * @copyright 2012 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/pluginlib.php'); + + +/** + * Test cases for {@link available_update_deployer} class. + */ +class core_available_update_deployer_testcase extends advanced_testcase { + + public function test_magic_setters() { + $deployer = testable_available_update_deployer::instance(); + $value = new moodle_url('/'); + $deployer->set_returnurl($value); + $this->assertSame($deployer->get_returnurl(), $value); + } + + public function test_prepare_authorization() { + global $CFG; + + $deployer = testable_available_update_deployer::instance(); + list($passfile, $password) = $deployer->prepare_authorization(); + $filename = $CFG->phpunit_dataroot.'/mdeploy/auth/'.$passfile; + $this->assertFileExists($filename); + $stored = file($filename, FILE_IGNORE_NEW_LINES); + $this->assertCount(2, $stored); + $this->assertGreaterThan(23, strlen($stored[0])); + $this->assertSame($stored[0], $password); + $this->assertLessThan(60, time() - (int)$stored[1]); + } +} + + +/** + * Modified version of {@link available_update_checker} suitable for testing. + */ +class testable_available_update_checker extends available_update_checker { + + /** @var replaces the default DB table storage for the fetched response */ + protected $fakeresponsestorage; + /** @var int stores the fake recentfetch value */ + public $fakerecentfetch = -1; + /** @var int stores the fake value of time() */ + public $fakecurrenttimestamp = -1; + + /** + * Factory method for this class. + * + * @return testable_available_update_checker the singleton instance + */ + public static function instance() { + global $CFG; + + if (is_null(self::$singletoninstance)) { + self::$singletoninstance = new self(); + } + return self::$singletoninstance; + } + + protected function validate_response($response) { + } + + protected function store_response($response) { + $this->fakeresponsestorage = $response; + } + + protected function restore_response($forcereload = false) { + $this->recentfetch = time(); + $this->recentresponse = $this->decode_response($this->get_fake_response()); + } + + public function compare_responses(array $old, array $new) { + return parent::compare_responses($old, $new); + } + + public function is_same_release($remote, $local=null) { + return parent::is_same_release($remote, $local); + } + + protected function load_current_environment($forcereload=false) { + } + + public function fake_current_environment($version, $release, $branch, array $plugins) { + $this->currentversion = $version; + $this->currentrelease = $release; + $this->currentbranch = $branch; + $this->currentplugins = $plugins; + } + + public function get_last_timefetched() { + if ($this->fakerecentfetch == -1) { + return parent::get_last_timefetched(); + } else { + return $this->fakerecentfetch; + } + } + + private function get_fake_response() { + $fakeresponse = array( + 'status' => 'OK', + 'provider' => 'http://download.moodle.org/api/1.0/updates.php', + 'apiver' => '1.0', + 'timegenerated' => time(), + 'forversion' => '2012010100.00', + 'forbranch' => '2.3', + 'ticket' => sha1('No, I am not going to mention the word "frog" here. Oh crap. I just did.'), + 'updates' => array( + 'core' => array( + array( + 'version' => 2012060103.00, + 'release' => '2.3.3 (Build: 20121201)', + 'maturity' => 200, + 'url' => 'http://download.moodle.org/', + 'download' => 'http://download.moodle.org/download.php/MOODLE_23_STABLE/moodle-2.3.3-latest.zip', + ), + array( + 'version' => 2012120100.00, + 'release' => '2.4dev (Build: 20121201)', + 'maturity' => 50, + 'url' => 'http://download.moodle.org/', + 'download' => 'http://download.moodle.org/download.php/MOODLE_24_STABLE/moodle-2.4.0-latest.zip', + ), + ), + 'mod_foo' => array( + array( + 'version' => 2012030501, + 'requires' => 2012010100, + 'maturity' => 200, + 'release' => '1.1', + 'url' => 'http://moodle.org/plugins/blahblahblah/', + 'download' => 'http://moodle.org/plugins/download.php/blahblahblah', + ), + array( + 'version' => 2012030502, + 'requires' => 2012010100, + 'maturity' => 100, + 'release' => '1.2 beta', + 'url' => 'http://moodle.org/plugins/', + ), + ), + ), + ); + + return json_encode($fakeresponse); + } + + protected function cron_current_timestamp() { + if ($this->fakecurrenttimestamp == -1) { + return parent::cron_current_timestamp(); + } else { + return $this->fakecurrenttimestamp; + } + } + + protected function cron_mtrace($msg, $eol = PHP_EOL) { + } + + protected function cron_autocheck_enabled() { + return true; + } + + protected function cron_execution_offset() { + // Autofetch should run by the first cron after 01:42 AM. + return 42 * MINSECS; + } + + protected function cron_execute() { + throw new testable_available_update_checker_cron_executed('Cron executed!'); + } +} + + +/** + * Exception used to detect {@link available_update_checker::cron_execute()} calls. + */ +class testable_available_update_checker_cron_executed extends Exception { +} + +/** + * Modified {@link available_update_deployer} suitable for testing purposes. + */ +class testable_available_update_deployer extends available_update_deployer { +} diff --git a/lib/tests/fixtures/mockplugins/mod/bar/version.php b/lib/tests/fixtures/mockplugins/mod/bar/version.php deleted file mode 100644 index 47613f65a4c4a..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/bar/version.php +++ /dev/null @@ -1,4 +0,0 @@ -version = 2012030500; -$module->requires = 2012010100; diff --git a/lib/tests/fixtures/mockplugins/mod/baz/meg/one/version.php b/lib/tests/fixtures/mockplugins/mod/baz/meg/one/version.php deleted file mode 100644 index b099a779bbfde..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/baz/meg/one/version.php +++ /dev/null @@ -1,5 +0,0 @@ -version = 2013041103; -$plugin->requires = 2013010100; -$plugin->component = 'bazmeg_one'; diff --git a/lib/tests/fixtures/mockplugins/mod/baz/version.php b/lib/tests/fixtures/mockplugins/mod/baz/version.php deleted file mode 100644 index 47613f65a4c4a..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/baz/version.php +++ /dev/null @@ -1,4 +0,0 @@ -version = 2012030500; -$module->requires = 2012010100; diff --git a/lib/tests/fixtures/mockplugins/mod/foo/lish/frog/version.php b/lib/tests/fixtures/mockplugins/mod/foo/lish/frog/version.php deleted file mode 100644 index b5dd0eeba04ec..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/foo/lish/frog/version.php +++ /dev/null @@ -1,6 +0,0 @@ -version = 2013041103; -$plugin->requires = 2013010100; -$plugin->component = 'foolish_frog'; -$plugin->dependencies = array('mod_foo' => 2012030500); diff --git a/lib/tests/fixtures/mockplugins/mod/foo/lish/hippo/version.php b/lib/tests/fixtures/mockplugins/mod/foo/lish/hippo/version.php deleted file mode 100644 index f64b02d59ffd8..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/foo/lish/hippo/version.php +++ /dev/null @@ -1,6 +0,0 @@ -version = 2013041103; -$plugin->requires = 2012010100; -$plugin->component = 'foolish_hippo'; -$plugin->dependencies = array('foolish_frog' => ANY_VERSION); diff --git a/lib/tests/fixtures/mockplugins/mod/foo/version.php b/lib/tests/fixtures/mockplugins/mod/foo/version.php deleted file mode 100644 index a12a76a40dcb3..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/foo/version.php +++ /dev/null @@ -1,10 +0,0 @@ -version = 2012030500; -$module->requires = 2012010100; -$module->component = 'mod_foo'; -$module->dependencies = array( - 'mod_bar' => 2012030500, - 'mod_missing' => ANY_VERSION, - 'foolish_frog' => ANY_VERSION, -); diff --git a/lib/tests/fixtures/mockplugins/mod/new/version.php b/lib/tests/fixtures/mockplugins/mod/new/version.php deleted file mode 100644 index 976ddf1204b77..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/new/version.php +++ /dev/null @@ -1,5 +0,0 @@ -version = 2013041900; -$module->requires = 2012010100; -$module->component = 'mod_new'; diff --git a/lib/tests/fixtures/mockplugins/mod/qux/cat/one/version.php b/lib/tests/fixtures/mockplugins/mod/qux/cat/one/version.php deleted file mode 100644 index 4c9e9781270d7..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/qux/cat/one/version.php +++ /dev/null @@ -1,6 +0,0 @@ -version = 2013041103; -$plugin->requires = 2013010100; -$plugin->component = 'quxcat_one'; -$plugin->dependencies = array('bazmeg_one' => 2013010100); diff --git a/lib/tests/fixtures/mockplugins/mod/qux/version.php b/lib/tests/fixtures/mockplugins/mod/qux/version.php deleted file mode 100644 index 9c1c5f01a28b5..0000000000000 --- a/lib/tests/fixtures/mockplugins/mod/qux/version.php +++ /dev/null @@ -1,5 +0,0 @@ -version = 2013041103; -$plugin->requires = 2013010100; -$plugin->component = 'mod_qux'; diff --git a/lib/tests/plugin_manager_test.php b/lib/tests/plugin_manager_test.php new file mode 100644 index 0000000000000..4a8f3788354e5 --- /dev/null +++ b/lib/tests/plugin_manager_test.php @@ -0,0 +1,232 @@ +. + +/** + * Unit tests for plugin manager class. + * + * @package core + * @category phpunit + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/pluginlib.php'); + + +/** + * Tests of the basic API of the plugin manager. + */ +class core_plugin_manager_testcase extends advanced_testcase { + + public function test_instance() { + $pluginman = plugin_manager::instance(); + $this->assertInstanceOf('plugin_manager', $pluginman); + $pluginman2 = plugin_manager::instance(); + $this->assertSame($pluginman, $pluginman2); + } + + public function test_reset_caches() { + // Make sure there are no warnings or errors. + plugin_manager::reset_caches(); + } + + public function test_get_plugin_types() { + // Make sure there are no warnings or errors. + $types = plugin_manager::instance()->get_plugin_types(); + $this->assertInternalType('array', $types); + foreach ($types as $type => $fulldir) { + $this->assertFileExists($fulldir); + } + } + + public function test_get_installed_plugins() { + $types = plugin_manager::instance()->get_plugin_types(); + foreach ($types as $type => $fulldir) { + $installed = plugin_manager::instance()->get_installed_plugins($type); + foreach ($installed as $plugin => $version) { + $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $plugin); + $this->assertTrue(is_numeric($version), 'All plugins should have a version, plugin '.$type.'_'.$plugin.' does not have version info.'); + } + } + } + + public function test_get_enabled_plugins() { + $types = plugin_manager::instance()->get_plugin_types(); + foreach ($types as $type => $fulldir) { + $enabled = plugin_manager::instance()->get_enabled_plugins($type); + if (is_array($enabled)) { + foreach ($enabled as $key => $val) { + $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $key); + $this->assertSame($key, $val); + } + } else { + $this->assertNull($enabled); + } + } + } + + public function test_get_present_plugins() { + $types = plugin_manager::instance()->get_plugin_types(); + foreach ($types as $type => $fulldir) { + $present = plugin_manager::instance()->get_present_plugins($type); + if (is_array($present)) { + foreach ($present as $plugin => $version) { + $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $plugin, 'All plugins are supposed to have version.php file.'); + $this->assertInternalType('object', $version); + $this->assertTrue(is_numeric($version->version), 'All plugins should have a version, plugin '.$type.'_'.$plugin.' does not have version info.'); + } + } else { + // No plugins of this type exist. + $this->assertNull($present); + } + } + } + + public function test_get_plugins() { + $plugininfos = plugin_manager::instance()->get_plugins(); + foreach ($plugininfos as $type => $infos) { + foreach ($infos as $name => $info) { + $this->assertInstanceOf('plugininfo_base', $info); + } + } + } + + public function test_get_plugins_of_type() { + $plugininfos = plugin_manager::instance()->get_plugins(); + foreach ($plugininfos as $type => $infos) { + $this->assertSame($infos, plugin_manager::instance()->get_plugins_of_type($type)); + } + } + + public function test_get_subplugins_of_plugin() { + global $CFG; + + // Any standard plugin with subplugins is suitable. + $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.'); + + $subplugins = plugin_manager::instance()->get_subplugins_of_plugin('editor_tinymce'); + foreach ($subplugins as $component => $info) { + $this->assertInstanceOf('plugininfo_base', $info); + } + } + + public function test_get_subplugins() { + // Tested already indirectly from test_get_subplugins_of_plugin(). + $subplugins = plugin_manager::instance()->get_subplugins(); + $this->assertInternalType('array', $subplugins); + } + + public function test_get_parent_of_subplugin() { + global $CFG; + + // Any standard plugin with subplugins is suitable. + $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.'); + + $parent = plugin_manager::instance()->get_parent_of_subplugin('tinymce'); + $this->assertSame('editor_tinymce', $parent); + } + + public function test_plugin_name() { + global $CFG; + + // Any standard plugin is suitable. + $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.'); + + $name = plugin_manager::instance()->plugin_name('editor_tinymce'); + $this->assertSame(get_string('pluginname', 'editor_tinymce'), $name); + } + + public function test_plugintype_name() { + $name = plugin_manager::instance()->plugintype_name('editor'); + $this->assertSame(get_string('type_editor', 'core_plugin'), $name); + } + + public function test_plugintype_name_plural() { + $name = plugin_manager::instance()->plugintype_name_plural('editor'); + $this->assertSame(get_string('type_editor_plural', 'core_plugin'), $name); + } + + public function test_get_plugin_info() { + global $CFG; + + // Any standard plugin is suitable. + $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.'); + + $info = plugin_manager::instance()->get_plugin_info('editor_tinymce'); + $this->assertInstanceOf('plugininfo_editor', $info); + } + + public function test_can_uninstall_plugin() { + global $CFG; + + // Any standard plugin that is required by some other standard plugin is ok. + $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/assignmentupgrade", 'assign upgrade tool is not present'); + $this->assertFileExists("$CFG->dirroot/mod/assign", 'assign module is not present'); + + $this->assertFalse(plugin_manager::instance()->can_uninstall_plugin('mod_assign')); + $this->assertTrue(plugin_manager::instance()->can_uninstall_plugin('tool_assignmentupgrade')); + } + + public function test_plugin_states() { + global $CFG; + $this->resetAfterTest(); + + // Any standard plugin that is ok. + $this->assertFileExists("$CFG->dirroot/mod/assign", 'assign module is not present'); + $this->assertFileExists("$CFG->dirroot/mod/forum", 'forum module is not present'); + $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/phpunit", 'phpunit tool is not present'); + $this->assertFileNotExists("$CFG->dirroot/mod/xxxxxxx"); + $this->assertFileNotExists("$CFG->dirroot/enrol/autorize"); + + // Ready for upgrade. + $assignversion = get_config('mod_assign', 'version'); + set_config('version', $assignversion - 1, 'mod_assign'); + // Downgrade problem. + $forumversion = get_config('mod_forum', 'version'); + set_config('version', $forumversion + 1, 'mod_forum'); + // Not installed yet. + unset_config('version', 'tool_phpunit'); + // Missing already installed. + set_config('version', 2013091300, 'mod_xxxxxxx'); + // Deleted present. + set_config('version', 2013091300, 'enrol_authorize'); + + plugin_manager::reset_caches(); + + $plugininfos = plugin_manager::instance()->get_plugins(); + foreach ($plugininfos as $type => $infos) { + foreach ($infos as $name => $info) { + /** @var plugininfo_base $info */ + if ($info->component === 'mod_assign') { + $this->assertSame(plugin_manager::PLUGIN_STATUS_UPGRADE, $info->get_status(), 'Invalid '.$info->component.' state'); + } else if ($info->component === 'mod_forum') { + $this->assertSame(plugin_manager::PLUGIN_STATUS_DOWNGRADE, $info->get_status(), 'Invalid '.$info->component.' state'); + } else if ($info->component === 'tool_phpunit') { + $this->assertSame(plugin_manager::PLUGIN_STATUS_NEW, $info->get_status(), 'Invalid '.$info->component.' state'); + } else if ($info->component === 'mod_xxxxxxx') { + $this->assertSame(plugin_manager::PLUGIN_STATUS_MISSING, $info->get_status(), 'Invalid '.$info->component.' state'); + } else if ($info->component === 'enrol_authorize') { + $this->assertSame(plugin_manager::PLUGIN_STATUS_DELETE, $info->get_status(), 'Invalid '.$info->component.' state'); + } else { + $this->assertSame(plugin_manager::PLUGIN_STATUS_UPTODATE, $info->get_status(), 'Invalid '.$info->component.' state'); + } + } + } + } +} diff --git a/lib/tests/pluginlib_test.php b/lib/tests/pluginlib_test.php deleted file mode 100644 index d6f47d135fb50..0000000000000 --- a/lib/tests/pluginlib_test.php +++ /dev/null @@ -1,950 +0,0 @@ -. - -/** - * Unit tests for the lib/pluginlib.php library - * - * Execute the core_plugin group to run all tests in this file: - * - * $ phpunit --group core_plugin - * - * @package core - * @category phpunit - * @copyright 2012 David Mudrak - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -global $CFG; -require_once($CFG->libdir.'/pluginlib.php'); - - -/** - * Tests of the basic API of the plugin manager. - * - * @group core_plugin - */ -class core_plugin_manager_testcase extends advanced_testcase { - - public function setUp() { - $this->resetAfterTest(); - } - - public function test_plugin_manager_instance() { - $pluginman = testable_plugin_manager::instance(); - $this->assertInstanceOf('testable_plugin_manager', $pluginman); - } - - public function test_get_plugins_of_type() { - $pluginman = testable_plugin_manager::instance(); - $mods = $pluginman->get_plugins_of_type('mod'); - $this->assertInternalType('array', $mods); - $this->assertCount(5, $mods); - $this->assertInstanceOf('testable_plugininfo_mod', $mods['foo']); - $this->assertInstanceOf('testable_plugininfo_mod', $mods['bar']); - $this->assertInstanceOf('testable_plugininfo_mod', $mods['baz']); - $this->assertInstanceOf('testable_plugininfo_mod', $mods['qux']); - $this->assertInstanceOf('testable_plugininfo_mod', $mods['new']); - $foolishes = $pluginman->get_plugins_of_type('foolish'); - $this->assertCount(2, $foolishes); - $this->assertInstanceOf('testable_pluginfo_foolish', $foolishes['frog']); - $this->assertInstanceOf('testable_pluginfo_foolish', $foolishes['hippo']); - $bazmegs = $pluginman->get_plugins_of_type('bazmeg'); - $this->assertCount(1, $bazmegs); - $this->assertInstanceOf('testable_pluginfo_bazmeg', $bazmegs['one']); - $quxcats = $pluginman->get_plugins_of_type('quxcat'); - $this->assertCount(1, $quxcats); - $this->assertInstanceOf('testable_pluginfo_quxcat', $quxcats['one']); - $unknown = $pluginman->get_plugins_of_type('muhehe'); - $this->assertSame(array(), $unknown); - } - - public function test_get_plugins() { - $pluginman = testable_plugin_manager::instance(); - $plugins = $pluginman->get_plugins(); - $this->assertInternalType('array', $plugins); - $this->assertTrue(isset($plugins['mod']['foo'])); - $this->assertTrue(isset($plugins['mod']['bar'])); - $this->assertTrue(isset($plugins['mod']['baz'])); - $this->assertTrue(isset($plugins['mod']['new'])); - $this->assertTrue(isset($plugins['foolish']['frog'])); - $this->assertTrue(isset($plugins['foolish']['hippo'])); - $this->assertInstanceOf('testable_plugininfo_mod', $plugins['mod']['foo']); - $this->assertInstanceOf('testable_plugininfo_mod', $plugins['mod']['bar']); - $this->assertInstanceOf('testable_plugininfo_mod', $plugins['mod']['baz']); - $this->assertInstanceOf('testable_plugininfo_mod', $plugins['mod']['new']); - $this->assertInstanceOf('testable_pluginfo_foolish', $plugins['foolish']['frog']); - $this->assertInstanceOf('testable_pluginfo_foolish', $plugins['foolish']['hippo']); - $this->assertInstanceOf('testable_pluginfo_bazmeg', $plugins['bazmeg']['one']); - $this->assertInstanceOf('testable_pluginfo_quxcat', $plugins['quxcat']['one']); - } - - public function test_get_subplugins_of_plugin() { - $pluginman = testable_plugin_manager::instance(); - $this->assertSame(array(), $pluginman->get_subplugins_of_plugin('mod_missing')); - $this->assertSame(array(), $pluginman->get_subplugins_of_plugin('mod_bar')); - $foosubs = $pluginman->get_subplugins_of_plugin('mod_foo'); - $this->assertInternalType('array', $foosubs); - $this->assertCount(2, $foosubs); - $this->assertInstanceOf('testable_pluginfo_foolish', $foosubs['foolish_frog']); - $this->assertInstanceOf('testable_pluginfo_foolish', $foosubs['foolish_hippo']); - $bazsubs = $pluginman->get_subplugins_of_plugin('mod_baz'); - $this->assertInternalType('array', $bazsubs); - $this->assertCount(1, $bazsubs); - $this->assertInstanceOf('testable_pluginfo_bazmeg', $bazsubs['bazmeg_one']); - $quxsubs = $pluginman->get_subplugins_of_plugin('mod_qux'); - $this->assertInternalType('array', $quxsubs); - $this->assertCount(1, $quxsubs); - $this->assertInstanceOf('testable_pluginfo_quxcat', $quxsubs['quxcat_one']); - } - - public function test_get_subplugins() { - $pluginman = testable_plugin_manager::instance(); - $subplugins = $pluginman->get_subplugins(); - $this->assertTrue(isset($subplugins['mod_foo']['foolish'])); - $this->assertTrue(isset($subplugins['mod_baz']['bazmeg'])); - $this->assertTrue(isset($subplugins['mod_qux']['quxcat'])); - } - - public function test_get_parent_of_subplugin() { - $pluginman = testable_plugin_manager::instance(); - $this->assertSame('mod_foo', $pluginman->get_parent_of_subplugin('foolish')); - $this->assertSame('mod_baz', $pluginman->get_parent_of_subplugin('bazmeg')); - $this->assertSame('mod_qux', $pluginman->get_parent_of_subplugin('quxcat')); - $this->assertFalse($pluginman->get_parent_of_subplugin('mod')); - $this->assertFalse($pluginman->get_parent_of_subplugin('unknown')); - $plugins = $pluginman->get_plugins(); - $this->assertFalse($plugins['mod']['foo']->is_subplugin()); - $this->assertFalse($plugins['mod']['foo']->get_parent_plugin()); - $this->assertTrue($plugins['foolish']['frog']->is_subplugin()); - $this->assertSame('mod_foo', $plugins['foolish']['frog']->get_parent_plugin()); - } - - public function test_plugin_name() { - $pluginman = testable_plugin_manager::instance(); - $this->assertSame('Foo', $pluginman->plugin_name('mod_foo')); - $this->assertSame('Bar', $pluginman->plugin_name('mod_bar')); - $this->assertSame('Frog', $pluginman->plugin_name('foolish_frog')); - $this->assertSame('Hippo', $pluginman->plugin_name('foolish_hippo')); - $this->assertSame('One', $pluginman->plugin_name('bazmeg_one')); - $this->assertSame('One', $pluginman->plugin_name('quxcat_one')); - } - - public function test_get_plugin_info() { - $pluginman = testable_plugin_manager::instance(); - $this->assertInstanceOf('testable_plugininfo_mod', $pluginman->get_plugin_info('mod_foo')); - $this->assertInstanceOf('testable_pluginfo_foolish', $pluginman->get_plugin_info('foolish_frog')); - } - - public function test_other_plugins_that_require() { - $pluginman = testable_plugin_manager::instance(); - $this->assertEquals(array('foolish_frog'), $pluginman->other_plugins_that_require('mod_foo')); - $this->assertCount(2, $pluginman->other_plugins_that_require('foolish_frog')); - $this->assertContains('foolish_hippo', $pluginman->other_plugins_that_require('foolish_frog')); - $this->assertContains('mod_foo', $pluginman->other_plugins_that_require('foolish_frog')); - $this->assertEquals(array(), $pluginman->other_plugins_that_require('foolish_hippo')); - $this->assertEquals(array('mod_foo'), $pluginman->other_plugins_that_require('mod_bar')); - $this->assertEquals(array('mod_foo'), $pluginman->other_plugins_that_require('mod_missing')); - $this->assertEquals(array('quxcat_one'), $pluginman->other_plugins_that_require('bazmeg_one')); - } - - public function test_are_dependencies_satisfied() { - $pluginman = testable_plugin_manager::instance(); - $this->assertTrue($pluginman->are_dependencies_satisfied(array())); - $this->assertTrue($pluginman->are_dependencies_satisfied(array( - 'mod_bar' => 2012030500, - ))); - $this->assertTrue($pluginman->are_dependencies_satisfied(array( - 'mod_bar' => ANY_VERSION, - ))); - $this->assertFalse($pluginman->are_dependencies_satisfied(array( - 'mod_bar' => 2099010000, - ))); - $this->assertFalse($pluginman->are_dependencies_satisfied(array( - 'mod_bar' => 2012030500, - 'mod_missing' => ANY_VERSION, - ))); - } - - public function test_all_plugins_ok() { - $pluginman = testable_plugin_manager::instance(); - $failedplugins = array(); - $this->assertFalse($pluginman->all_plugins_ok(2013010100, $failedplugins)); - $this->assertContains('mod_foo', $failedplugins); // Requires mod_missing. - $this->assertNotContains('mod_bar', $failedplugins); - $this->assertNotContains('foolish_frog', $failedplugins); - $this->assertNotContains('foolish_hippo', $failedplugins); - - $failedplugins = array(); - $this->assertFalse($pluginman->all_plugins_ok(2012010100, $failedplugins)); - $this->assertContains('mod_foo', $failedplugins); // Requires mod_missing. - $this->assertNotContains('mod_bar', $failedplugins); - $this->assertContains('foolish_frog', $failedplugins); // Requires Moodle 2013010100. - $this->assertNotContains('foolish_hippo', $failedplugins); - - $failedplugins = array(); - $this->assertFalse($pluginman->all_plugins_ok(2011010100, $failedplugins)); - $this->assertContains('mod_foo', $failedplugins); // Requires mod_missing and Moodle 2012010100. - $this->assertContains('mod_bar', $failedplugins); // Requires Moodle 2012010100. - $this->assertContains('foolish_frog', $failedplugins); // Requires Moodle 2013010100. - $this->assertContains('foolish_hippo', $failedplugins); // Requires Moodle 2012010100. - } - - public function test_some_plugins_updatable() { - $pluginman = testable_plugin_manager::instance(); - $this->assertTrue($pluginman->some_plugins_updatable()); // We have available update for mod_foo. - } - - public function test_is_standard() { - $pluginman = testable_plugin_manager::instance(); - $this->assertTrue($pluginman->get_plugin_info('mod_bar')->is_standard()); - $this->assertFalse($pluginman->get_plugin_info('mod_foo')->is_standard()); - $this->assertFalse($pluginman->get_plugin_info('foolish_frog')->is_standard()); - } - - public function test_get_status() { - $pluginman = testable_plugin_manager::instance(); - $plugins = $pluginman->get_plugins(); - $this->assertEquals(plugin_manager::PLUGIN_STATUS_UPGRADE, $plugins['mod']['foo']->get_status()); - $this->assertEquals(plugin_manager::PLUGIN_STATUS_NEW, $plugins['mod']['new']->get_status()); - $this->assertEquals(plugin_manager::PLUGIN_STATUS_NEW, $plugins['bazmeg']['one']->get_status()); - $this->assertEquals(plugin_manager::PLUGIN_STATUS_UPTODATE, $plugins['quxcat']['one']->get_status()); - } - - public function test_available_update() { - $pluginman = testable_plugin_manager::instance(); - $plugins = $pluginman->get_plugins(); - $this->assertNull($plugins['mod']['bar']->available_updates()); - $this->assertInternalType('array', $plugins['mod']['foo']->available_updates()); - foreach ($plugins['mod']['foo']->available_updates() as $availableupdate) { - $this->assertInstanceOf('available_update_info', $availableupdate); - } - } - - public function test_can_uninstall_plugin() { - $pluginman = testable_plugin_manager::instance(); - $this->assertFalse($pluginman->can_uninstall_plugin('mod_missing')); - $this->assertTrue($pluginman->can_uninstall_plugin('mod_foo')); // Because mod_foo is required by foolish_frog only - // and foolish_frog is required by mod_foo and foolish_hippo only. - $this->assertFalse($pluginman->can_uninstall_plugin('mod_bar')); // Because mod_bar is required by mod_foo. - $this->assertFalse($pluginman->can_uninstall_plugin('mod_qux')); // Because even if no plugin (not even subplugins) declare - // dependency on it, but its subplugin can't be uninstalled. - $this->assertFalse($pluginman->can_uninstall_plugin('mod_baz')); // Because it's subplugin bazmeg_one is required by quxcat_one. - $this->assertFalse($pluginman->can_uninstall_plugin('mod_new')); // Because it is not installed. - $this->assertFalse($pluginman->can_uninstall_plugin('quxcat_one')); // Because of testable_pluginfo_quxcat::is_uninstall_allowed(). - $this->assertFalse($pluginman->can_uninstall_plugin('foolish_frog')); // Because foolish_hippo requires it. - } - - public function test_get_uninstall_url() { - $pluginman = testable_plugin_manager::instance(); - foreach ($pluginman->get_plugins() as $plugintype => $plugininfos) { - foreach ($plugininfos as $plugininfo) { - $this->assertInstanceOf('moodle_url', $plugininfo->get_uninstall_url()); - } - } - } -} - - -/** - * Tests of the basic API of the available update checker. - * - * @group core_plugin - */ -class core_available_update_checker_testcase extends advanced_testcase { - - public function test_core_available_update() { - $provider = testable_available_update_checker::instance(); - $this->assertInstanceOf('available_update_checker', $provider); - - $provider->fake_current_environment(2012060102.00, '2.3.2 (Build: 20121012)', '2.3', array()); - $updates = $provider->get_update_info('core'); - $this->assertCount(2, $updates); - - $provider->fake_current_environment(2012060103.00, '2.3.3 (Build: 20121212)', '2.3', array()); - $updates = $provider->get_update_info('core'); - $this->assertCount(1, $updates); - - $provider->fake_current_environment(2012060103.00, '2.3.3 (Build: 20121212)', '2.3', array()); - $updates = $provider->get_update_info('core', array('minmaturity' => MATURITY_STABLE)); - $this->assertNull($updates); - } - - /** - * If there are no fetched data yet, the first cron should fetch them. - */ - public function test_cron_initial_fetch() { - $provider = testable_available_update_checker::instance(); - $provider->fakerecentfetch = null; - $provider->fakecurrenttimestamp = -1; - $this->setExpectedException('testable_available_update_checker_cron_executed'); - $provider->cron(); - } - - /** - * If there is a fresh fetch available, no cron execution is expected. - */ - public function test_cron_has_fresh_fetch() { - $provider = testable_available_update_checker::instance(); - $provider->fakerecentfetch = time() - 23 * HOURSECS; // Fetched 23 hours ago. - $provider->fakecurrenttimestamp = -1; - $provider->cron(); - $this->assertTrue(true); // We should get here with no exception thrown. - } - - /** - * If there is an outdated fetch, the cron execution is expected. - */ - public function test_cron_has_outdated_fetch() { - $provider = testable_available_update_checker::instance(); - $provider->fakerecentfetch = time() - 49 * HOURSECS; // Fetched 49 hours ago. - $provider->fakecurrenttimestamp = -1; - $this->setExpectedException('testable_available_update_checker_cron_executed'); - $provider->cron(); - } - - /** - * The first cron after 01:42 AM today should fetch the data. - * - * @see testable_available_update_checker::cron_execution_offset() - */ - public function test_cron_offset_execution_not_yet() { - $provider = testable_available_update_checker::instance(); - $provider->fakecurrenttimestamp = mktime(1, 40, 02); // 01:40:02 AM today - $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS; - $provider->cron(); - $this->assertTrue(true); // We should get here with no exception thrown. - } - - /** - * The first cron after 01:42 AM today should fetch the data and then - * it is supposed to wait next 24 hours. - * - * @see testable_available_update_checker::cron_execution_offset() - */ - public function test_cron_offset_execution() { - $provider = testable_available_update_checker::instance(); - - // The cron at 01:45 should fetch the data. - $provider->fakecurrenttimestamp = mktime(1, 45, 02); // 01:45:02 AM today - $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS - 1; - $executed = false; - try { - $provider->cron(); - } catch (testable_available_update_checker_cron_executed $e) { - $executed = true; - } - $this->assertTrue($executed, 'Cron should be executed at 01:45:02 but it was not.'); - - // Another cron at 06:45 should still consider data as fresh enough. - $provider->fakerecentfetch = $provider->fakecurrenttimestamp; - $provider->fakecurrenttimestamp = mktime(6, 45, 03); // 06:45:03 AM - $executed = false; - try { - $provider->cron(); - } catch (testable_available_update_checker_cron_executed $e) { - $executed = true; - } - $this->assertFalse($executed, 'Cron should not be executed at 06:45:03 but it was.'); - - // The next scheduled execution should happen the next day. - $provider->fakecurrenttimestamp = $provider->fakerecentfetch + 24 * HOURSECS + 1; - $executed = false; - try { - $provider->cron(); - } catch (testable_available_update_checker_cron_executed $e) { - $executed = true; - } - $this->assertTrue($executed, 'Cron should be executed the next night but it was not.'); - } - - public function test_compare_responses_both_empty() { - $provider = testable_available_update_checker::instance(); - $old = array(); - $new = array(); - $cmp = $provider->compare_responses($old, $new); - $this->assertInternalType('array', $cmp); - $this->assertEmpty($cmp); - } - - public function test_compare_responses_old_empty() { - $provider = testable_available_update_checker::instance(); - $old = array(); - $new = array( - 'updates' => array( - 'core' => array( - array( - 'version' => 2012060103 - ) - ) - ) - ); - $cmp = $provider->compare_responses($old, $new); - $this->assertInternalType('array', $cmp); - $this->assertNotEmpty($cmp); - $this->assertTrue(isset($cmp['core'][0]['version'])); - $this->assertEquals(2012060103, $cmp['core'][0]['version']); - } - - public function test_compare_responses_no_change() { - $provider = testable_available_update_checker::instance(); - $old = $new = array( - 'updates' => array( - 'core' => array( - array( - 'version' => 2012060104 - ), - array( - 'version' => 2012120100 - ) - ), - 'mod_foo' => array( - array( - 'version' => 2011010101 - ) - ) - ) - ); - $cmp = $provider->compare_responses($old, $new); - $this->assertInternalType('array', $cmp); - $this->assertEmpty($cmp); - } - - public function test_compare_responses_new_and_missing_update() { - $provider = testable_available_update_checker::instance(); - $old = array( - 'updates' => array( - 'core' => array( - array( - 'version' => 2012060104 - ) - ), - 'mod_foo' => array( - array( - 'version' => 2011010101 - ) - ) - ) - ); - $new = array( - 'updates' => array( - 'core' => array( - array( - 'version' => 2012060104 - ), - array( - 'version' => 2012120100 - ) - ) - ) - ); - $cmp = $provider->compare_responses($old, $new); - $this->assertInternalType('array', $cmp); - $this->assertNotEmpty($cmp); - $this->assertCount(1, $cmp); - $this->assertCount(1, $cmp['core']); - $this->assertEquals(2012120100, $cmp['core'][0]['version']); - } - - public function test_compare_responses_modified_update() { - $provider = testable_available_update_checker::instance(); - $old = array( - 'updates' => array( - 'mod_foo' => array( - array( - 'version' => 2011010101 - ) - ) - ) - ); - $new = array( - 'updates' => array( - 'mod_foo' => array( - array( - 'version' => 2011010102 - ) - ) - ) - ); - $cmp = $provider->compare_responses($old, $new); - $this->assertInternalType('array', $cmp); - $this->assertNotEmpty($cmp); - $this->assertCount(1, $cmp); - $this->assertCount(1, $cmp['mod_foo']); - $this->assertEquals(2011010102, $cmp['mod_foo'][0]['version']); - } - - public function test_compare_responses_invalid_format() { - $provider = testable_available_update_checker::instance(); - $broken = array( - 'status' => 'ERROR' // No 'updates' key here. - ); - $this->setExpectedException('available_update_checker_exception'); - $cmp = $provider->compare_responses($broken, $broken); - } - - public function test_is_same_release_explicit() { - $provider = testable_available_update_checker::instance(); - $this->assertTrue($provider->is_same_release('2.3dev (Build: 20120323)', '2.3dev (Build: 20120323)')); - $this->assertTrue($provider->is_same_release('2.3dev (Build: 20120323)', '2.3dev (Build: 20120330)')); - $this->assertFalse($provider->is_same_release('2.3dev (Build: 20120529)', '2.3 (Build: 20120601)')); - $this->assertFalse($provider->is_same_release('2.3dev', '2.3 dev')); - $this->assertFalse($provider->is_same_release('2.3.1', '2.3')); - $this->assertFalse($provider->is_same_release('2.3.1', '2.3.2')); - $this->assertTrue($provider->is_same_release('2.3.2+', '2.3.2')); // Yes, really! - $this->assertTrue($provider->is_same_release('2.3.2 (Build: 123456)', '2.3.2+ (Build: 123457)')); - $this->assertFalse($provider->is_same_release('3.0 Community Edition', '3.0 Enterprise Edition')); - $this->assertTrue($provider->is_same_release('3.0 Community Edition', '3.0 Community Edition (Build: 20290101)')); - } - - public function test_is_same_release_implicit() { - $provider = testable_available_update_checker::instance(); - $provider->fake_current_environment(2012060102.00, '2.3.2 (Build: 20121012)', '2.3', array()); - $this->assertTrue($provider->is_same_release('2.3.2')); - $this->assertTrue($provider->is_same_release('2.3.2+')); - $this->assertTrue($provider->is_same_release('2.3.2+ (Build: 20121013)')); - $this->assertFalse($provider->is_same_release('2.4dev (Build: 20121012)')); - } -} - - -/** - * Base class for testable plugininfo classes. - */ -class testable_plugininfo_base extends plugininfo_base { - - protected function get_plugin_manager() { - return testable_plugin_manager::instance(); - } -} - - -/** - * Modified {@link plugininfo_mod} suitable for testing purposes. - */ -class testable_plugininfo_mod extends plugininfo_mod { - - public function init_display_name() { - $this->displayname = ucfirst($this->name); - } - - public function is_standard() { - if ($this->component === 'mod_foo') { - return false; - } else { - return true; - } - } - - public function load_db_version() { - if ($this->component !== 'mod_new') { - $this->versiondb = 2012022900; - } - } - - public function is_uninstall_allowed() { - return true; // Allow uninstall for standard plugins too. - } - - protected function get_plugin_manager() { - return testable_plugin_manager::instance(); - } -} - - -/** - * Testable class representing subplugins of testable mod_foo. - */ -class testable_pluginfo_foolish extends testable_plugininfo_base { - - public function init_display_name() { - $this->displayname = ucfirst($this->name); - } - - public function is_standard() { - return false; - } - - public function load_db_version() { - $this->versiondb = 2012022900; - } -} - - -/** - * Testable class representing subplugins of testable mod_baz. - */ -class testable_pluginfo_bazmeg extends testable_plugininfo_base { - - public function init_display_name() { - $this->displayname = ucfirst($this->name); - } - - public function is_standard() { - return false; - } - - public function load_db_version() { - $this->versiondb = null; - } -} - - -/** - * Testable class representing subplugins of testable mod_qux. - */ -class testable_pluginfo_quxcat extends testable_plugininfo_base { - - public function init_display_name() { - $this->displayname = ucfirst($this->name); - } - - public function is_standard() { - return false; - } - - public function load_db_version() { - $this->versiondb = 2013041103; - } - - public function is_uninstall_allowed() { - return false; - } -} - - -/** - * Modified {@link plugin_manager} suitable for testing purposes - */ -class testable_plugin_manager extends plugin_manager { - - /** - * 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; - } - - /** - * A version of {@link plugin_manager::get_plugins()} that prepares some faked - * testable instances. - * - * @param bool $disablecache ignored in this class - * @return array - */ - public function get_plugins($disablecache = false) { - - $dirroot = dirname(__FILE__).'/fixtures/mockplugins'; - - $this->pluginsinfo = array( - 'mod' => array( - 'foo' => plugininfo_default_factory::make('mod', $dirroot.'/mod', 'foo', - $dirroot.'/mod/foo', 'testable_plugininfo_mod'), - 'bar' => plugininfo_default_factory::make('mod', $dirroot.'/bar', 'bar', - $dirroot.'/mod/bar', 'testable_plugininfo_mod'), - 'baz' => plugininfo_default_factory::make('mod', $dirroot.'/baz', 'baz', - $dirroot.'/mod/baz', 'testable_plugininfo_mod'), - 'qux' => plugininfo_default_factory::make('mod', $dirroot.'/qux', 'qux', - $dirroot.'/mod/qux', 'testable_plugininfo_mod'), - 'new' => plugininfo_default_factory::make('mod', $dirroot.'/new', 'new', - $dirroot.'/mod/new', 'testable_plugininfo_mod'), - ), - 'foolish' => array( - 'frog' => plugininfo_default_factory::make('foolish', $dirroot.'/mod/foo/lish', 'frog', - $dirroot.'/mod/foo/lish/frog', 'testable_pluginfo_foolish'), - 'hippo' => plugininfo_default_factory::make('foolish', $dirroot.'/mod/foo/lish', 'hippo', - $dirroot.'/mod/foo/lish/hippo', 'testable_pluginfo_foolish'), - ), - 'bazmeg' => array( - 'one' => plugininfo_default_factory::make('bazmeg', $dirroot.'/mod/baz/meg', 'one', - $dirroot.'/mod/baz/meg/one', 'testable_pluginfo_bazmeg'), - ), - 'quxcat' => array( - 'one' => plugininfo_default_factory::make('quxcat', $dirroot.'/mod/qux/cat', 'one', - $dirroot.'/mod/qux/cat/one', 'testable_pluginfo_quxcat'), - ), - ); - - $checker = testable_available_update_checker::instance(); - $this->pluginsinfo['mod']['foo']->check_available_updates($checker); - $this->pluginsinfo['mod']['bar']->check_available_updates($checker); - $this->pluginsinfo['mod']['baz']->check_available_updates($checker); - $this->pluginsinfo['mod']['new']->check_available_updates($checker); - $this->pluginsinfo['bazmeg']['one']->check_available_updates($checker); - $this->pluginsinfo['quxcat']['one']->check_available_updates($checker); - - return $this->pluginsinfo; - } - - /** - * Testable version of {@link plugin_manager::get_subplugins()} that works with - * the simulated environment. - * - * In this case, the mod_foo fake module provides subplugins of type 'foolish', - * mod_baz provides subplugins of type 'bazmeg' and mod_qux has 'quxcat'. - * - * @param bool $disablecache ignored in this class - * @return array - */ - public function get_subplugins($disablecache = false) { - - $this->subpluginsinfo = array( - 'mod_foo' => array( - 'foolish' => (object)array( - 'type' => 'foolish', - 'typerootdir' => 'mod/foo/lish', - ), - ), - 'mod_baz' => array( - 'bazmeg' => (object)array( - 'type' => 'bazmeg', - 'typerootdir' => 'mod/baz/meg', - ), - ), - 'mod_qux' => array( - 'quxcat' => (object)array( - 'type' => 'quxcat', - 'typerootdir' => 'mod/qux/cat', - ), - ), - ); - - return $this->subpluginsinfo; - } - - /** - * Adds support for mock plugin types. - */ - protected function normalize_component($component) { - - // List of mock plugin types used in these unit tests. - $faketypes = array('foolish', 'bazmeg', 'quxcat'); - - foreach ($faketypes as $faketype) { - if (strpos($component, $faketype.'_') === 0) { - return explode('_', $component, 2); - } - } - - return parent::normalize_component($component); - } - - public function plugintype_name($type) { - return ucfirst($type); - } - - public function plugintype_name_plural($type) { - return ucfirst($type).'s'; // Simple, isn't it? ;-). - } - - public function plugin_external_source($component) { - if ($component === 'foolish_frog') { - return true; - } - return false; - } -} - - -/** - * Modified version of {@link available_update_checker} suitable for testing. - */ -class testable_available_update_checker extends available_update_checker { - - /** @var replaces the default DB table storage for the fetched response */ - protected $fakeresponsestorage; - /** @var int stores the fake recentfetch value */ - public $fakerecentfetch = -1; - /** @var int stores the fake value of time() */ - public $fakecurrenttimestamp = -1; - - /** - * Factory method for this class. - * - * @return testable_available_update_checker the singleton instance - */ - public static function instance() { - global $CFG; - - if (is_null(self::$singletoninstance)) { - self::$singletoninstance = new self(); - } - return self::$singletoninstance; - } - - protected function validate_response($response) { - } - - protected function store_response($response) { - $this->fakeresponsestorage = $response; - } - - protected function restore_response($forcereload = false) { - $this->recentfetch = time(); - $this->recentresponse = $this->decode_response($this->get_fake_response()); - } - - public function compare_responses(array $old, array $new) { - return parent::compare_responses($old, $new); - } - - public function is_same_release($remote, $local=null) { - return parent::is_same_release($remote, $local); - } - - protected function load_current_environment($forcereload=false) { - } - - public function fake_current_environment($version, $release, $branch, array $plugins) { - $this->currentversion = $version; - $this->currentrelease = $release; - $this->currentbranch = $branch; - $this->currentplugins = $plugins; - } - - public function get_last_timefetched() { - if ($this->fakerecentfetch == -1) { - return parent::get_last_timefetched(); - } else { - return $this->fakerecentfetch; - } - } - - private function get_fake_response() { - $fakeresponse = array( - 'status' => 'OK', - 'provider' => 'http://download.moodle.org/api/1.0/updates.php', - 'apiver' => '1.0', - 'timegenerated' => time(), - 'forversion' => '2012010100.00', - 'forbranch' => '2.3', - 'ticket' => sha1('No, I am not going to mention the word "frog" here. Oh crap. I just did.'), - 'updates' => array( - 'core' => array( - array( - 'version' => 2012060103.00, - 'release' => '2.3.3 (Build: 20121201)', - 'maturity' => 200, - 'url' => 'http://download.moodle.org/', - 'download' => 'http://download.moodle.org/download.php/MOODLE_23_STABLE/moodle-2.3.3-latest.zip', - ), - array( - 'version' => 2012120100.00, - 'release' => '2.4dev (Build: 20121201)', - 'maturity' => 50, - 'url' => 'http://download.moodle.org/', - 'download' => 'http://download.moodle.org/download.php/MOODLE_24_STABLE/moodle-2.4.0-latest.zip', - ), - ), - 'mod_foo' => array( - array( - 'version' => 2012030501, - 'requires' => 2012010100, - 'maturity' => 200, - 'release' => '1.1', - 'url' => 'http://moodle.org/plugins/blahblahblah/', - 'download' => 'http://moodle.org/plugins/download.php/blahblahblah', - ), - array( - 'version' => 2012030502, - 'requires' => 2012010100, - 'maturity' => 100, - 'release' => '1.2 beta', - 'url' => 'http://moodle.org/plugins/', - ), - ), - ), - ); - - return json_encode($fakeresponse); - } - - protected function cron_current_timestamp() { - if ($this->fakecurrenttimestamp == -1) { - return parent::cron_current_timestamp(); - } else { - return $this->fakecurrenttimestamp; - } - } - - protected function cron_mtrace($msg, $eol = PHP_EOL) { - } - - protected function cron_autocheck_enabled() { - return true; - } - - protected function cron_execution_offset() { - // Autofetch should run by the first cron after 01:42 AM. - return 42 * MINSECS; - } - - protected function cron_execute() { - throw new testable_available_update_checker_cron_executed('Cron executed!'); - } -} - - -/** - * Exception used to detect {@link available_update_checker::cron_execute()} calls. - */ -class testable_available_update_checker_cron_executed extends Exception { -} - - -/** - * Modified {@link available_update_deployer} suitable for testing purposes. - */ -class testable_available_update_deployer extends available_update_deployer { -} - - -/** - * Test cases for {@link available_update_deployer} class. - * - * @group core_plugin - */ -class core_available_update_deployer_testcase extends advanced_testcase { - - public function test_magic_setters() { - $deployer = testable_available_update_deployer::instance(); - $value = new moodle_url('/'); - $deployer->set_returnurl($value); - $this->assertSame($deployer->get_returnurl(), $value); - } - - public function test_prepare_authorization() { - global $CFG; - - $deployer = testable_available_update_deployer::instance(); - list($passfile, $password) = $deployer->prepare_authorization(); - $filename = $CFG->phpunit_dataroot.'/mdeploy/auth/'.$passfile; - $this->assertFileExists($filename); - $stored = file($filename, FILE_IGNORE_NEW_LINES); - $this->assertCount(2, $stored); - $this->assertGreaterThan(23, strlen($stored[0])); - $this->assertSame($stored[0], $password); - $this->assertLessThan(60, time() - (int)$stored[1]); - } -} diff --git a/lib/upgrade.txt b/lib/upgrade.txt index ae667267642fe..2473c306def6f 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -36,6 +36,9 @@ information provided here is intended especially for developers. Use core_user::get_noreply_user() and core_user::get_support_user() to get noreply and support user's respectively. Real users can be used as noreply/support users by setting $CFG->noreplyuserid and $CFG->supportuserid * New function readfile_allow_large() in filelib.php for use when very large files may need sending to user. +* Use plugin_manager::reset_caches() when changing visibility of plugins. +* Implement new method get_enabled_plugins() method in subplugin info classes. +* Each plugin should include version information in version.php. DEPRECATIONS: Various previously deprecated functions have now been altered to throw DEBUG_DEVELOPER debugging notices diff --git a/lib/upgradelib.php b/lib/upgradelib.php index 0ca4493b39ccd..12ed9b9a82569 100644 --- a/lib/upgradelib.php +++ b/lib/upgradelib.php @@ -222,21 +222,25 @@ function upgrade_main_savepoint($result, $version, $allowabort=true) { function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { global $DB; + $component = 'mod_'.$modname; + if (!$result) { - throw new upgrade_exception("mod_$modname", $version); + throw new upgrade_exception($component, $version); } + $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); + if (!$module = $DB->get_record('modules', array('name'=>$modname))) { print_error('modulenotexist', 'debug', '', $modname); } - if ($module->version >= $version) { + if ($dbversion >= $version) { // something really wrong is going on in upgrade script - throw new downgrade_exception("mod_$modname", $module->version, $version); + throw new downgrade_exception($component, $dbversion, $version); } - $module->version = $version; - $DB->update_record('modules', $module); - upgrade_log(UPGRADE_LOG_NORMAL, "mod_$modname", 'Upgrade savepoint reached'); + set_config('version', $version, $component); + + upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); // reset upgrade timeout to default upgrade_set_timeout(); @@ -262,21 +266,25 @@ function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) { global $DB; + $component = 'block_'.$blockname; + if (!$result) { - throw new upgrade_exception("block_$blockname", $version); + throw new upgrade_exception($component, $version); } + $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); + if (!$block = $DB->get_record('block', array('name'=>$blockname))) { print_error('blocknotexist', 'debug', '', $blockname); } - if ($block->version >= $version) { + if ($dbversion >= $version) { // something really wrong is going on in upgrade script - throw new downgrade_exception("block_$blockname", $block->version, $version); + throw new downgrade_exception($component, $dbversion, $version); } - $block->version = $version; - $DB->update_record('block', $block); - upgrade_log(UPGRADE_LOG_NORMAL, "block_$blockname", 'Upgrade savepoint reached'); + set_config('version', $version, $component); + + upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); // reset upgrade timeout to default upgrade_set_timeout(); @@ -301,16 +309,19 @@ function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true * @return void */ function upgrade_plugin_savepoint($result, $version, $type, $plugin, $allowabort=true) { + global $DB; + $component = $type.'_'.$plugin; if (!$result) { throw new upgrade_exception($component, $version); } - $installedversion = get_config($component, 'version'); - if ($installedversion >= $version) { + $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); + + if ($dbversion >= $version) { // Something really wrong is going on in the upgrade script - throw new downgrade_exception($component, $installedversion, $version); + throw new downgrade_exception($component, $dbversion, $version); } set_config('version', $version, $component); upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); @@ -338,6 +349,7 @@ function upgrade_stale_php_files_present() { $someexamplesofremovedfiles = array( // removed in 2.6dev + '/admin/block.php', '/admin/oacleanup.php', // removed in 2.5dev '/backup/lib.php', @@ -402,12 +414,10 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { } $plugin = new stdClass(); - $module = new stdClass(); // Prevent some notices when plugin placed in wrong directory. + $plugin->version = null; + $module = $plugin; // Prevent some notices when plugin placed in wrong directory. require($fullplug.'/version.php'); // defines $plugin with version etc - - if (!isset($plugin->version) and isset($module->version)) { - $plugin = $module; - } + unset($module); // if plugin tells us it's full name we may check the location if (isset($plugin->component)) { @@ -425,7 +435,6 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { $plugin->name = $plug; $plugin->fullname = $component; - if (!empty($plugin->requires)) { if ($plugin->requires > $CFG->version) { throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); @@ -457,7 +466,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { } } - $installedversion = get_config($plugin->fullname, 'version'); + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! if (empty($installedversion)) { // new installation $startcallback($component, true, $verbose); @@ -503,7 +512,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { $result = true; } - $installedversion = get_config($plugin->fullname, 'version'); + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! if ($installedversion < $plugin->version) { // store version if not already there upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false); @@ -516,6 +525,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { events_update_definition($component); message_update_providers($component); if ($type === 'message') { + // Ugly hack! message_update_processors($plug); } upgrade_plugin_mnet_functions($component); @@ -555,36 +565,34 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { throw new plugin_defective_exception($component, 'Missing version.php'); } - $module = new stdClass(); - $plugin = new stdClass(); // Prevent some notices when plugin placed in wrong directory. - require($fullmod .'/version.php'); // defines $module with version etc - - if (!isset($module->version) and isset($plugin->version)) { - $module = $plugin; - } + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; + require($fullmod .'/version.php'); // Defines $module/$plugin with version etc. + $plugin = clone($module); + unset($module->version); + unset($module->component); + unset($module->dependencies); + unset($module->release); // if plugin tells us it's full name we may check the location - if (isset($module->component)) { - if ($module->component !== $component) { + if (isset($plugin->component)) { + if ($plugin->component !== $component) { $current = str_replace($CFG->dirroot, '$CFG->dirroot', $fullmod); - $expected = str_replace($CFG->dirroot, '$CFG->dirroot', core_component::get_component_directory($module->component)); + $expected = str_replace($CFG->dirroot, '$CFG->dirroot', core_component::get_component_directory($plugin->component)); throw new plugin_misplaced_exception($component, $expected, $current); } } - if (empty($module->version)) { - if (isset($module->version)) { - // Version is empty but is set - it means its value is 0 or ''. Let us skip such module. - // This is intended for developers so they can work on the early stages of the module. - continue; - } + if (empty($plugin->version)) { + // Version must be always set now! throw new plugin_defective_exception($component, 'Missing version value in version.php'); } - if (!empty($module->requires)) { - if ($module->requires > $CFG->version) { - throw new upgrade_requires_exception($component, $module->version, $CFG->version, $module->requires); - } else if ($module->requires < 2010000000) { + if (!empty($plugin->requires)) { + if ($plugin->requires > $CFG->version) { + throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); + } else if ($plugin->requires < 2010000000) { throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); } } @@ -600,7 +608,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { $module->name = $mod; // The name MUST match the directory - $currmodule = $DB->get_record('modules', array('name'=>$module->name)); + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! if (file_exists($fullmod.'/db/install.php')) { if (get_config($module->name, 'installrunning')) { @@ -622,7 +630,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { } } - if (empty($currmodule->version)) { + if (empty($installedversion)) { $startcallback($component, true, $verbose); /// Execute install.xml (XMLDB) - must be present in all modules @@ -630,6 +638,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { /// Add record into modules table - may be needed in install.php already $module->id = $DB->insert_record('modules', $module); + upgrade_mod_savepoint(true, $plugin->version, $module->name, false); /// Post installation hook - optional if (file_exists("$fullmod/db/install.php")) { @@ -651,22 +660,23 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { $endcallback($component, true, $verbose); - } else if ($currmodule->version < $module->version) { + } else if ($installedversion < $plugin->version) { /// If versions say that we need to upgrade but no upgrade files are available, notify and continue $startcallback($component, false, $verbose); if (is_readable($fullmod.'/db/upgrade.php')) { require_once($fullmod.'/db/upgrade.php'); // defines new upgrading function $newupgrade_function = 'xmldb_'.$module->name.'_upgrade'; - $result = $newupgrade_function($currmodule->version, $module); + $result = $newupgrade_function($installedversion, $module); } else { $result = true; } + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! $currmodule = $DB->get_record('modules', array('name'=>$module->name)); - if ($currmodule->version < $module->version) { + if ($installedversion < $plugin->version) { // store version if not already there - upgrade_mod_savepoint($result, $module->version, $mod, false); + upgrade_mod_savepoint($result, $plugin->version, $mod, false); } // update cron flag if needed @@ -684,8 +694,8 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { $endcallback($component, false, $verbose); - } else if ($currmodule->version > $module->version) { - throw new downgrade_exception($component, $currmodule->version, $module->version); + } else if ($installedversion > $plugin->version) { + throw new downgrade_exception($component, $installedversion, $plugin->version); } } } @@ -731,24 +741,30 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { throw new plugin_defective_exception('block/'.$blockname, 'Missing version.php file.'); } $plugin = new stdClass(); - $module = new stdClass(); // Prevent some notices when module placed in wrong directory. - $plugin->version = NULL; + $plugin->version = null; $plugin->cron = 0; + $module = $plugin; // Prevent some notices when module placed in wrong directory. include($fullblock.'/version.php'); - if (!isset($plugin->version) and isset($module->version)) { - $plugin = $module; - } - $block = $plugin; + unset($module); + $block = clone($plugin); + unset($block->version); + unset($block->component); + unset($block->dependencies); + unset($block->release); // if plugin tells us it's full name we may check the location - if (isset($block->component)) { - if ($block->component !== $component) { + if (isset($plugin->component)) { + if ($plugin->component !== $component) { $current = str_replace($CFG->dirroot, '$CFG->dirroot', $fullblock); - $expected = str_replace($CFG->dirroot, '$CFG->dirroot', core_component::get_component_directory($block->component)); + $expected = str_replace($CFG->dirroot, '$CFG->dirroot', core_component::get_component_directory($plugin->component)); throw new plugin_misplaced_exception($component, $expected, $current); } } + if (empty($plugin->version)) { + throw new plugin_defective_exception($component, 'Missing block version.'); + } + if (!empty($plugin->requires)) { if ($plugin->requires > $CFG->version) { throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); @@ -778,11 +794,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { $block->name = $blockname; // The name MUST match the directory - if (empty($block->version)) { - throw new plugin_defective_exception($component, 'Missing block version.'); - } - - $currblock = $DB->get_record('block', array('name'=>$block->name)); + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! if (file_exists($fullblock.'/db/install.php')) { if (get_config('block_'.$blockname, 'installrunning')) { @@ -804,7 +816,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { } } - if (empty($currblock->version)) { // block not installed yet, so install it + if (empty($installedversion)) { // block not installed yet, so install it $conflictblock = array_search($blocktitle, $blocktitles); if ($conflictblock !== false) { // Duplicate block titles are not allowed, they confuse people @@ -817,6 +829,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml'); } $block->id = $DB->insert_record('block', $block); + upgrade_block_savepoint(true, $plugin->version, $block->name, false); if (file_exists($fullblock.'/db/install.php')) { require_once($fullblock.'/db/install.php'); @@ -839,21 +852,22 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { $endcallback($component, true, $verbose); - } else if ($currblock->version < $block->version) { + } else if ($installedversion < $plugin->version) { $startcallback($component, false, $verbose); if (is_readable($fullblock.'/db/upgrade.php')) { require_once($fullblock.'/db/upgrade.php'); // defines new upgrading function $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade'; - $result = $newupgrade_function($currblock->version, $block); + $result = $newupgrade_function($installedversion, $block); } else { $result = true; } + $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! $currblock = $DB->get_record('block', array('name'=>$block->name)); - if ($currblock->version < $block->version) { + if ($installedversion < $plugin->version) { // store version if not already there - upgrade_block_savepoint($result, $block->version, $block->name, false); + upgrade_block_savepoint($result, $plugin->version, $block->name, false); } if ($currblock->cron != $block->cron) { @@ -871,8 +885,8 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { $endcallback($component, false, $verbose); - } else if ($currblock->version > $block->version) { - throw new downgrade_exception($component, $currblock->version, $block->version); + } else if ($installedversion > $plugin->version) { + throw new downgrade_exception($component, $installedversion, $plugin->version); } } @@ -1178,35 +1192,6 @@ function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { include("$CFG->dirroot/version.php"); $targetversion = $version; - } else if ($plugintype === 'mod') { - try { - $currentversion = $DB->get_field('modules', 'version', array('name'=>$pluginname)); - $currentversion = ($currentversion === false) ? null : $currentversion; - } catch (Exception $ignored) { - } - $cd = core_component::get_component_directory($component); - if (file_exists("$cd/version.php")) { - $module = new stdClass(); - $module->version = null; - include("$cd/version.php"); - $targetversion = $module->version; - } - - } else if ($plugintype === 'block') { - try { - if ($block = $DB->get_record('block', array('name'=>$pluginname))) { - $currentversion = $block->version; - } - } catch (Exception $ignored) { - } - $cd = core_component::get_component_directory($component); - if (file_exists("$cd/version.php")) { - $plugin = new stdClass(); - $plugin->version = null; - include("$cd/version.php"); - $targetversion = $plugin->version; - } - } else { $pluginversion = get_config($component, 'version'); if (!empty($pluginversion)) { @@ -1216,6 +1201,7 @@ function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { if (file_exists("$cd/version.php")) { $plugin = new stdClass(); $plugin->version = null; + $module = $plugin; include("$cd/version.php"); $targetversion = $plugin->version; } diff --git a/mod/resource/backup/moodle1/lib.php b/mod/resource/backup/moodle1/lib.php index 8fb5af344da4e..30eaba7058e5c 100644 --- a/mod/resource/backup/moodle1/lib.php +++ b/mod/resource/backup/moodle1/lib.php @@ -113,9 +113,11 @@ public function process_resource(array $data, array $raw) { // use the version of the successor instead of the current mod/resource // beware - the version.php declares info via $module object, do not use // a variable of such name here - $module = new stdClass(); + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; include $CFG->dirroot.'/mod/'.$successor->get_modname().'/version.php'; - $cminfo['version'] = $module->version; + $cminfo['version'] = $plugin->version; // stash the new course module information for this successor $cminfo['modulename'] = $successor->get_modname(); diff --git a/user/portfolio.php b/user/portfolio.php index a84935ce45899..e8b0b39137188 100644 --- a/user/portfolio.php +++ b/user/portfolio.php @@ -29,6 +29,7 @@ print_error('disabled', 'portfolio'); } +require_once($CFG->libdir . '/pluginlib.php'); require_once($CFG->libdir . '/portfoliolib.php'); require_once($CFG->libdir . '/portfolio/forms.php'); @@ -57,9 +58,6 @@ require_login($course, false); -// Purge all caches related to portfolio administration. -cache::make('core', 'plugininfo_portfolio')->purge(); - $PAGE->set_url($url); $PAGE->set_context(context_user::instance($user->id)); $PAGE->set_title("$course->fullname: $fullname: $strportfolios"); @@ -84,6 +82,7 @@ $success = $instance->set_user_config($fromform, $USER->id); //$success = $success && $instance->save(); if ($success) { + plugin_manager::reset_caches(); redirect($baseurl, get_string('instancesaved', 'portfolio'), 3); } else { print_error('instancenotsaved', 'portfolio', $baseurl); @@ -100,6 +99,7 @@ } else if (!empty($hide)) { $instance = portfolio_instance($hide); $instance->set_user_config(array('visible' => !$instance->get_user_config('visible', $USER->id)), $USER->id); + plugin_manager::reset_caches(); } if ($display) { diff --git a/version.php b/version.php index 9474abd67b33c..8d77f2809d8ae 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2013092000.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2013092001.02; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.