diff --git a/admin/index.php b/admin/index.php index ea9f37dd294..a07104c561a 100644 --- a/admin/index.php +++ b/admin/index.php @@ -47,7 +47,9 @@ if (!function_exists('iconv')) { define('NO_OUTPUT_BUFFERING', true); -if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey'])) { +if ((isset($_GET['cache']) and $_GET['cache'] === '0') + or (isset($_POST['cache']) and $_POST['cache'] === '0') + or (!isset($_POST['cache']) and !isset($_GET['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey']))) { // Prevent caching at all cost when visiting this page directly, // we redirect to self once we known no upgrades are necessary. // Note: $_GET and $_POST are used here intentionally because our param cleaning is not loaded yet. @@ -90,9 +92,7 @@ $newaddonreq = optional_param('installaddonrequest', null, PARAM_RAW); // Set up PAGE. $url = new moodle_url('/admin/index.php'); -if ($cache) { - $url->param('cache', 1); -} +$url->param('cache', $cache); $PAGE->set_url($url); unset($url); @@ -267,12 +267,13 @@ if (!$cache and $version > $CFG->version) { // upgrade $PAGE->set_pagelayout('maintenance'); $PAGE->set_popup_notification_allowed(false); + /** @var core_admin_renderer $output */ + $output = $PAGE->get_renderer('core', 'admin'); + if (upgrade_stale_php_files_present()) { $PAGE->set_title($stradministration); $PAGE->set_cacheable(false); - /** @var core_admin_renderer $output */ - $output = $PAGE->get_renderer('core', 'admin'); echo $output->upgrade_stale_php_files_page(); die(); } @@ -287,8 +288,6 @@ if (!$cache and $version > $CFG->version) { // upgrade $PAGE->set_heading($strdatabasechecking); $PAGE->set_cacheable(false); - /** @var core_admin_renderer $output */ - $output = $PAGE->get_renderer('core', 'admin'); echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite); die(); @@ -302,8 +301,6 @@ if (!$cache and $version > $CFG->version) { // upgrade $PAGE->set_heading($strcurrentrelease); $PAGE->set_cacheable(false); - /** @var core_admin_renderer $output */ - $output = $PAGE->get_renderer('core', 'admin'); echo $output->upgrade_environment_page($release, $envstatus, $environment_results); die(); @@ -315,23 +312,13 @@ if (!$cache and $version > $CFG->version) { // upgrade $PAGE->set_heading($strplugincheck); $PAGE->set_cacheable(false); - $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1)); - - /** @var core_admin_renderer $output */ - $output = $PAGE->get_renderer('core', 'admin'); - - // check plugin dependencies first - $failed = array(); - if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) { - echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl); - die(); - } - unset($failed); + $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)); if ($fetchupdates) { - // no sesskey support guaranteed here - if (empty($CFG->disableupdatenotifications)) { - \core\update\checker::instance()->fetch(); + // No sesskey support guaranteed here, because sessions might not work yet. + $updateschecker = \core\update\checker::instance(); + if ($updateschecker->enabled()) { + $updateschecker->fetch(); } redirect($reloadurl); } @@ -342,6 +329,7 @@ if (!$cache and $version > $CFG->version) { // upgrade $deploydata = $deployer->submitted_data(); if (!empty($deploydata)) { + // No sesskey support guaranteed here, because sessions might not work yet. echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata); die(); } @@ -349,11 +337,22 @@ if (!$cache and $version > $CFG->version) { // upgrade echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(), $version, $showallplugins, $reloadurl, - new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1))); + new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1, 'cache'=>0))); die(); } else { - // Launch main upgrade + // Always verify plugin dependencies! + $failed = array(); + if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) { + $PAGE->set_pagelayout('maintenance'); + $PAGE->set_popup_notification_allowed(false); + $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)); + echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl); + die(); + } + unset($failed); + + // Launch main upgrade. upgrade_core($version, true); } } else if ($version < $CFG->version) { @@ -373,6 +372,10 @@ if (!$cache and $branch <> $CFG->branch) { // Update the branch if (!$cache and moodle_needs_upgrading()) { if (!$PAGE->headerprinted) { // means core upgrade or installation was not already done + + /** @var core_admin_renderer $output */ + $output = $PAGE->get_renderer('core', 'admin'); + if (!$confirmplugins) { $strplugincheck = get_string('plugincheck'); @@ -384,40 +387,46 @@ if (!$cache and moodle_needs_upgrading()) { $PAGE->set_cacheable(false); if ($fetchupdates) { - // no sesskey support guaranteed here - \core\update\checker::instance()->fetch(); + require_sesskey(); + $updateschecker = \core\update\checker::instance(); + if ($updateschecker->enabled()) { + $updateschecker->fetch(); + } redirect($PAGE->url); } - $output = $PAGE->get_renderer('core', 'admin'); - $deployer = \core\update\deployer::instance(); if ($deployer->enabled()) { $deployer->initialize($PAGE->url, $PAGE->url); $deploydata = $deployer->submitted_data(); if (!empty($deploydata)) { + require_sesskey(); echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata); die(); } } - // check plugin dependencies first - $failed = array(); - if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) { - echo $output->unsatisfied_dependencies_page($version, $failed, $PAGE->url); - die(); - } - unset($failed); - - // dependencies check passed, let's rock! + // Show plugins info. echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(), $version, $showallplugins, new moodle_url($PAGE->url), - new moodle_url('/admin/index.php', array('confirmplugincheck'=>1))); + new moodle_url('/admin/index.php', array('confirmplugincheck'=>1, 'cache'=>0))); die(); } + + // Make sure plugin dependencies are always checked. + $failed = array(); + if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) { + $PAGE->set_pagelayout('maintenance'); + $PAGE->set_popup_notification_allowed(false); + $reloadurl = new moodle_url('/admin/index.php', array('cache' => 0)); + echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl); + die(); + } + unset($failed); } + // install/upgrade all plugins and other parts upgrade_noncore(true); } @@ -477,6 +486,17 @@ if (during_initial_install()) { upgrade_finished('upgradesettings.php'); } +if (has_capability('moodle/site:config', context_system::instance())) { + if ($fetchupdates) { + require_sesskey(); + $updateschecker = \core\update\checker::instance(); + if ($updateschecker->enabled()) { + $updateschecker->fetch(); + } + redirect(new moodle_url('/admin/index.php', array('cache' => 0))); + } +} + // Now we can be sure everything was upgraded and caches work fine, // redirect if necessary to make sure caching is enabled. if (!$cache) { @@ -564,12 +584,6 @@ $registered = $DB->count_records('registration_hubs', array('huburl' => HUB_MOOD admin_externalpage_setup('adminnotifications'); -if ($fetchupdates) { - require_sesskey(); - $updateschecker->fetch(); - redirect(new moodle_url('/admin/index.php')); -} - $output = $PAGE->get_renderer('core', 'admin'); echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed, $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb, diff --git a/admin/renderer.php b/admin/renderer.php index f86097be0b4..3ba44bb360b 100644 --- a/admin/renderer.php +++ b/admin/renderer.php @@ -140,7 +140,7 @@ class core_admin_renderer extends plugin_renderer_base { public function upgrade_confirm_page($strnewversion, $maturity, $testsite) { $output = ''; - $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1)); + $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'cache' => 0)); $continue = new single_button($continueurl, get_string('continue'), 'get'); $cancelurl = new moodle_url('/admin/index.php'); @@ -170,7 +170,7 @@ class core_admin_renderer extends plugin_renderer_base { $output .= $this->environment_check_table($envstatus, $environment_results); if (!$envstatus) { - $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1)); + $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1, 'cache' => 0)); } else { $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess'); @@ -179,7 +179,7 @@ class core_admin_renderer extends plugin_renderer_base { $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice'); } - $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1))); + $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0))); } $output .= $this->footer(); @@ -711,7 +711,7 @@ class core_admin_renderer extends plugin_renderer_base { } $updateinfo .= $this->container_start('checkforupdates'); - $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 1)); + $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0)); $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin')); if ($fetch) { $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin', @@ -962,7 +962,7 @@ class core_admin_renderer extends plugin_renderer_base { $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin')); if (empty($options['full'])) { $out .= html_writer::link(new moodle_url('/admin/index.php', - array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)), + array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)), get_string('nonehighlightedinfo', 'core_plugin')); } $out .= $this->output->container_end(); @@ -972,11 +972,11 @@ class core_admin_renderer extends plugin_renderer_base { $out .= $this->output->heading(get_string('somehighlighted', 'core_plugin', $sumofhighlighted)); if (empty($options['full'])) { $out .= html_writer::link(new moodle_url('/admin/index.php', - array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)), + array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)), get_string('somehighlightedinfo', 'core_plugin')); } else { $out .= html_writer::link(new moodle_url('/admin/index.php', - array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0)), + array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0, 'cache' => 0)), get_string('somehighlightedonly', 'core_plugin')); } $out .= $this->output->container_end(); diff --git a/lib/classes/update/checker.php b/lib/classes/update/checker.php index f3d358239a5..22776270e30 100644 --- a/lib/classes/update/checker.php +++ b/lib/classes/update/checker.php @@ -81,6 +81,18 @@ class checker { } } + /** + * Is automatic deployment enabled? + * + * @return bool + */ + public function enabled() { + global $CFG; + + // The feature can be prohibited via config.php. + return empty($CFG->disableupdateautodeploy); + } + /** * Returns the timestamp of the last execution of {@link fetch()} * diff --git a/lib/classes/update/deployer.php b/lib/classes/update/deployer.php index 89c47d68bdf..0eccb8e9e7d 100644 --- a/lib/classes/update/deployer.php +++ b/lib/classes/update/deployer.php @@ -32,9 +32,6 @@ defined('MOODLE_INTERNAL') || die(); */ class deployer { - const HTTP_PARAM_PREFIX = 'updteautodpldata_'; // Hey, even Google has not heard of such a prefix! So it MUST be safe :-p. - const HTTP_PARAM_CHECKER = 'datapackagesize'; // Name of the parameter that holds the number of items in the received data items. - /** @var \core\update\deployer holds the singleton instance */ protected static $singletoninstance; /** @var moodle_url URL of a page that includes the deployer UI */ @@ -207,9 +204,19 @@ class deployer { throw new coding_exception('Illegal method call - deployer not initialized.'); } - $params = $this->data_to_params(array( - 'updateinfo' => (array)$info, // See http://www.php.net/manual/en/language.types.array.php#language.types.array.casting . - )); + $params = array( + 'updateaddon' => $info->component, + 'version' =>$info->version, + 'sesskey' => sesskey(), + ); + + // Append some our own data. + if (!empty($this->callerurl)) { + $params['callerurl'] = $this->callerurl->out(false); + } + if (!empty($this->returnurl)) { + $params['returnurl'] = $this->returnurl->out(false); + } $widget = new \single_button( new moodle_url($this->callerurl, $params), @@ -301,25 +308,46 @@ class deployer { * @return array */ public function submitted_data() { - - $data = $this->params_to_data($_POST); - - if (empty($data) or empty($data[self::HTTP_PARAM_CHECKER])) { + $component = optional_param('updateaddon', '', PARAM_COMPONENT); + $version = optional_param('version', '', PARAM_RAW); + if (!$component or !$version) { return false; } - if (!empty($data['updateinfo']) and is_object($data['updateinfo'])) { - $updateinfo = $data['updateinfo']; - if (!empty($updateinfo->component) and !empty($updateinfo->version)) { - $data['updateinfo'] = new info($updateinfo->component, (array)$updateinfo); + $plugininfo = \core_plugin_manager::instance()->get_plugin_info($component); + if (!$plugininfo) { + return false; + } + + if ($plugininfo->is_standard()) { + return false; + } + + if (!$updates = $plugininfo->available_updates()) { + return false; + } + + $info = null; + foreach ($updates as $update) { + if ($update->version == $version) { + $info = $update; + break; } } - - if (!empty($data['callerurl'])) { - $data['callerurl'] = new moodle_url($data['callerurl']); + if (!$info) { + return false; } - if (!empty($data['returnurl'])) { + $data = array( + 'updateaddon' => $component, + 'updateinfo' => $info, + 'callerurl' => optional_param('callerurl', null, PARAM_URL), + 'returnurl' => optional_param('returnurl', null, PARAM_URL), + ); + if ($data['callerurl']) { + $data['callerurl'] = new moodle_url($data['callerurl']); + } + if ($data['callerurl']) { $data['returnurl'] = new moodle_url($data['returnurl']); } @@ -421,60 +449,6 @@ class deployer { /* === End of external API === */ - /** - * Prepares an array of HTTP parameters that can be passed to another page. - * - * @param array|object $data associative array or an object holding the data, data JSON-able - * @return array suitable as a param for moodle_url - */ - protected function data_to_params($data) { - - // Append some our own data. - if (!empty($this->callerurl)) { - $data['callerurl'] = $this->callerurl->out(false); - } - if (!empty($this->returnurl)) { - $data['returnurl'] = $this->returnurl->out(false); - } - - // Finally append the count of items in the package. - $data[self::HTTP_PARAM_CHECKER] = count($data); - - // Generate params. - $params = array(); - foreach ($data as $name => $value) { - $transname = self::HTTP_PARAM_PREFIX.$name; - $transvalue = json_encode($value); - $params[$transname] = $transvalue; - } - - return $params; - } - - /** - * Converts HTTP parameters passed to the script into native PHP data - * - * @param array $params such as $_REQUEST or $_POST - * @return array data passed for this class - */ - protected function params_to_data(array $params) { - - if (empty($params)) { - return array(); - } - - $data = array(); - foreach ($params as $name => $value) { - if (strpos($name, self::HTTP_PARAM_PREFIX) === 0) { - $realname = substr($name, strlen(self::HTTP_PARAM_PREFIX)); - $realvalue = json_decode($value); - $data[$realname] = $realvalue; - } - } - - return $data; - } - /** * Returns a random string to be used as a filename of the password storage. * @@ -511,7 +485,13 @@ class deployer { $directory = core_component::get_plugin_directory($plugintype, $pluginname); if (is_null($directory)) { - throw new coding_exception('Unknown component location', $component); + // Plugin unknown, most probably deleted or missing during upgrade, + // look at the parent directory instead because they might want to install it. + $plugintypes = core_component::get_plugin_types(); + if (!isset($plugintypes[$plugintype])) { + throw new coding_exception('Unknown component location', $component); + } + $directory = $plugintypes[$plugintype]; } return $this->directory_writable($directory); diff --git a/mdeploy.php b/mdeploy.php index 620053e8046..ad42d99ed19 100644 --- a/mdeploy.php +++ b/mdeploy.php @@ -780,17 +780,25 @@ class worker extends singleton_pattern { $this->log('Current plugin code location: '.$sourcelocation); $this->log('Moving the current code into archive: '.$backuplocation); - // We don't want to touch files unless we are pretty sure it would be all ok. - if (!$this->move_directory_source_precheck($sourcelocation)) { - throw new backup_folder_exception('Unable to backup the current version of the plugin (source precheck failed)'); - } - if (!$this->move_directory_target_precheck($backuplocation)) { - throw new backup_folder_exception('Unable to backup the current version of the plugin (backup precheck failed)'); - } + if (file_exists($sourcelocation)) { + // We don't want to touch files unless we are pretty sure it would be all ok. + if (!$this->move_directory_source_precheck($sourcelocation)) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (source precheck failed)'); + } + if (!$this->move_directory_target_precheck($backuplocation)) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (backup precheck failed)'); + } - // Looking good, let's try it. - if (!$this->move_directory($sourcelocation, $backuplocation, true)) { - throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)'); + // Looking good, let's try it. + if (!$this->move_directory($sourcelocation, $backuplocation, true)) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)'); + } + + } else { + // Upgrading missing plugin - this happens often during upgrades. + if (!$this->create_directory_precheck($sourcelocation)) { + throw new filesystem_exception('Unable to prepare the plugin location (cannot create new directory)'); + } } // Unzip the plugin package file into the target location.