MDL-49329 admin: Support plugins installation during upgrade
authorDavid Mudrák <david@moodle.com>
Wed, 7 Oct 2015 17:30:03 +0000 (19:30 +0200)
committerDavid Mudrák <david@moodle.com>
Fri, 9 Oct 2015 07:50:45 +0000 (09:50 +0200)
The plugins check screen (displayed during core upgrade and/or plugins
upgrade) now supports installation of remote plugins. This includes
installation of missing dependencies (both single and bulk mode) and
installation of available updates (both single and bulk mode).

All the HTTP query parameters supported by admin/index.php are now
explicitly enlisted. Previously, the \core\update\deployer used
its own additional parameters (and was source of some serious problems
in the past).

The implementation uses the plugin manager as the controller and
provides an unified interface for installing any remote plugin or
plugins (be it available update or missing dependency).

As a side effect, we now validate available updates which was not
happening before.

admin/index.php
lang/en/plugin.php
lib/upgradelib.php

index 8a765da..c5b562d 100644 (file)
@@ -98,18 +98,24 @@ core_component::get_core_subsystems();
 require_once($CFG->libdir.'/adminlib.php');    // various admin-only functions
 require_once($CFG->libdir.'/upgradelib.php');  // general upgrade/install related functions
 
-$confirmupgrade = optional_param('confirmupgrade', 0, PARAM_BOOL);
-$confirmrelease = optional_param('confirmrelease', 0, PARAM_BOOL);
-$confirmplugins = optional_param('confirmplugincheck', 0, PARAM_BOOL);
-$showallplugins = optional_param('showallplugins', 0, PARAM_BOOL);
-$agreelicense   = optional_param('agreelicense', 0, PARAM_BOOL);
-$fetchupdates   = optional_param('fetchupdates', 0, PARAM_BOOL);
-$newaddonreq    = optional_param('installaddonrequest', null, PARAM_RAW);
-$upgradekeyhash = optional_param('upgradekeyhash', null, PARAM_ALPHANUM);
-$installdep     = optional_param('installdep', null, PARAM_COMPONENT);
-$installdepx    = optional_param('installdepx', false, PARAM_BOOL);
-$abortinstall   = optional_param('abortinstall', null, PARAM_COMPONENT);
-$abortinstallx  = optional_param('abortinstallx', null, PARAM_BOOL);
+$confirmupgrade = optional_param('confirmupgrade', 0, PARAM_BOOL); // Core upgrade confirmed?
+$confirmrelease = optional_param('confirmrelease', 0, PARAM_BOOL); // Core release info and server checks confirmed?
+$confirmplugins = optional_param('confirmplugincheck', 0, PARAM_BOOL); // Plugins check page confirmed?
+$showallplugins = optional_param('showallplugins', 0, PARAM_BOOL); // Show all plugins on the plugins check page?
+$agreelicense = optional_param('agreelicense', 0, PARAM_BOOL); // GPL license confirmed for installation?
+$fetchupdates = optional_param('fetchremote', 0, PARAM_BOOL); // Should check for available updates?
+$newaddonreq = optional_param('installaddonrequest', null, PARAM_RAW); // Plugin installation requested at moodle.org/plugins.
+$upgradekeyhash = optional_param('upgradekeyhash', null, PARAM_ALPHANUM); // Hash of provided upgrade key.
+$installdep = optional_param('installdep', null, PARAM_COMPONENT); // Install given missing dependency (required plugin).
+$installdepx = optional_param('installdepx', false, PARAM_BOOL); // Install all missing dependencies.
+$confirminstalldep = optional_param('confirminstalldep', false, PARAM_BOOL); // Installing dependencies confirmed.
+$abortinstall = optional_param('abortinstall', null, PARAM_COMPONENT); // Cancel installation of the given new plugin.
+$abortinstallx = optional_param('abortinstallx', null, PARAM_BOOL); // Cancel installation of all new plugins.
+$confirmabortinstall = optional_param('confirmabortinstall', false, PARAM_BOOL); // Installation cancel confirmed.
+$installupdate = optional_param('installupdate', null, PARAM_COMPONENT); // Install given available update.
+$installupdateversion = optional_param('installupdateversion', null, PARAM_INT); // Version of the available update to install.
+$installupdatex = optional_param('installupdatex', false, PARAM_BOOL); // Install all available plugin updates.
+$confirminstallupdate = optional_param('confirminstallupdate', false, PARAM_BOOL); // Available update(s) install confirmed?
 
 // Set up PAGE.
 $url = new moodle_url('/admin/index.php');
@@ -337,8 +343,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         die();
 
     } else if (empty($confirmplugins)) {
-        // No sesskey support guaranteed here, because sessions might not work yet.
-
         $strplugincheck = get_string('plugincheck');
 
         $PAGE->navbar->add($strplugincheck);
@@ -349,17 +353,66 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $pluginman = core_plugin_manager::instance();
 
         if ($abortinstallx) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
             $pluginman->cancel_all_plugin_installations();
             redirect($PAGE->url);
         }
 
         if ($abortinstall) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
             $pluginman->cancel_plugin_installation($abortinstall);
             redirect($PAGE->url);
         }
 
+        // Install all available missing dependencies.
+        if ($installdepx) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
+            upgrade_install_remote_plugins($installable, $confirminstalldep,
+                get_string('dependencyinstallhead', 'core_plugin'),
+                new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
+            );
+        }
+
+        // Install single available missing dependency.
+        if ($installdep) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
+            if (!empty($installable[$installdep])) {
+                $installable = array($installable[$installdep]);
+                upgrade_install_remote_plugins($installable, $confirminstalldep,
+                    get_string('dependencyinstallhead', 'core_plugin'),
+                    new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
+                );
+            }
+        }
+
+        // Install all avilable updates.
+        if ($installupdatex) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $installable = $pluginman->filter_installable($pluginman->available_updates());
+            upgrade_install_remote_plugins($installable, $confirminstallupdate,
+                get_string('updateavailableinstallallhead', 'core_admin'),
+                new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
+            );
+        }
+
+        // Install single available update.
+        if ($installupdate and $installupdateversion) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
+                $installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
+                upgrade_install_remote_plugins($installable, $confirminstallupdate,
+                    get_string('updateavailableinstallallhead', 'core_admin'),
+                    new moodle_url($PAGE->url, array('installupdate' => $installupdate,
+                        'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
+                    )
+                );
+            }
+        }
 
         if ($fetchupdates) {
+            // No sesskey support guaranteed here, because sessions might not work yet.
             $updateschecker = \core\update\checker::instance();
             if ($updateschecker->enabled()) {
                 $updateschecker->fetch();
@@ -367,18 +420,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
             redirect($PAGE->url);
         }
 
-        $deployer = \core\update\deployer::instance();
-        if ($deployer->enabled()) {
-            $deployer->initialize($PAGE->url, $PAGE->url);
-
-            $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();
-            }
-        }
-
         echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                 $version, $showallplugins, $PAGE->url, new moodle_url($PAGE->url, array('confirmplugincheck' => 1)));
         die();
@@ -417,9 +458,9 @@ if (!$cache and moodle_needs_upgrading()) {
 
     if (!$PAGE->headerprinted) {
         // means core upgrade or installation was not already done
+        $pluginman = core_plugin_manager::instance();
 
         if (!$confirmplugins) {
-            $pluginman = core_plugin_manager::instance();
             $strplugincheck = get_string('plugincheck');
 
             $PAGE->navbar->add($strplugincheck);
@@ -448,21 +489,56 @@ if (!$cache and moodle_needs_upgrading()) {
                 redirect($PAGE->url);
             }
 
-            /** @var core_admin_renderer $output */
-            $output = $PAGE->get_renderer('core', 'admin');
+            // Install all available missing dependencies.
+            if ($installdepx) {
+                require_sesskey();
+                $installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
+                upgrade_install_remote_plugins($installable, $confirminstalldep,
+                    get_string('dependencyinstallhead', 'core_plugin'),
+                    new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
+                );
+            }
+
+            // Install single available missing dependency.
+            if ($installdep) {
+                require_sesskey();
+                $installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
+                if (!empty($installable[$installdep])) {
+                    $installable = array($installable[$installdep]);
+                    upgrade_install_remote_plugins($installable, $confirminstalldep,
+                        get_string('dependencyinstallhead', 'core_plugin'),
+                        new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
+                    );
+                }
+            }
 
-            $deployer = \core\update\deployer::instance();
-            if ($deployer->enabled()) {
-                $deployer->initialize($PAGE->url, $PAGE->url);
+            // Install all available updates.
+            if ($installupdatex) {
+                require_sesskey();
+                $installable = $pluginman->filter_installable($pluginman->available_updates());
+                upgrade_install_remote_plugins($installable, $confirminstallupdate,
+                    get_string('updateavailableinstallallhead', 'core_admin'),
+                    new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
+                );
+            }
 
-                $deploydata = $deployer->submitted_data();
-                if (!empty($deploydata)) {
-                    require_sesskey();
-                    echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
-                    die();
+            // Install single available update.
+            if ($installupdate and $installupdateversion) {
+                require_sesskey();
+                if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
+                    $installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
+                    upgrade_install_remote_plugins($installable, $confirminstallupdate,
+                        get_string('updateavailableinstallallhead', 'core_admin'),
+                        new moodle_url($PAGE->url, array('installupdate' => $installupdate,
+                            'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
+                        )
+                    );
                 }
             }
 
+            /** @var core_admin_renderer $output */
+            $output = $PAGE->get_renderer('core', 'admin');
+
             // Show plugins info.
             echo $output->upgrade_plugin_check_page($pluginman, \core\update\checker::instance(),
                     $version, $showallplugins,
index 5a49264..9e873c2 100644 (file)
@@ -35,6 +35,7 @@ $string['detectedmisplacedplugin'] = 'Plugin "{$a->component}" is installed in i
 $string['dependencyavailable'] = 'Available';
 $string['dependencyfails'] = 'Fails';
 $string['dependencyinstall'] = 'Install';
+$string['dependencyinstallhead'] = 'Installing missing dependencies';
 $string['dependencyinstallmissing'] = 'Install missing dependencies ({$a})';
 $string['dependencymissing'] = 'Missing';
 $string['dependencyunavailable'] = 'Unavailable';
index f26e966..28bb38d 100644 (file)
@@ -2371,3 +2371,53 @@ function check_upgrade_key($upgradekeyhash) {
         }
     }
 }
+
+/**
+ * Helper procedure/macro for installing remote plugins at admin/index.php
+ *
+ * Does not return, always redirects or exits.
+ *
+ * @param array $installable list of \core\update\remote_info
+ * @param bool $confirmed false: display the validation screen, true: proceed installation
+ * @param string $heading validation screen heading
+ * @param moodle_url|string|null $continue URL to proceed with installation at the validation screen
+ * @param moodle_url|string|null $return URL to go back (on successful finish or cancel)
+ */
+function upgrade_install_remote_plugins(array $installable, $confirmed, $heading='', $continue=null, $return=null) {
+    global $PAGE;
+
+    if (empty($return)) {
+        $return = $PAGE->url;
+    }
+
+    if (empty($installable)) {
+        redirect($return);
+    }
+
+    $pluginman = core_plugin_manager::instance();
+
+    if ($confirmed) {
+        // Installation confirmed at the validation results page.
+        if (!$pluginman->install_remote_plugins($installable, true, true)) {
+            throw new moodle_exception('install_remote_plugins_failed', 'core_plugin', $return);
+        }
+        redirect($return);
+
+    } else {
+        $output = $PAGE->get_renderer('core', 'admin');
+        echo $output->header();
+        if ($heading) {
+            echo $output->heading($heading, 3);
+        }
+        echo html_writer::start_tag('pre', array('class' => 'plugin-install-console'));
+        $validated = $pluginman->install_remote_plugins($installable, false, false);
+        echo html_writer::end_tag('pre');
+        if ($validated) {
+            echo $output->install_remote_plugins_buttons($continue, null, $return);
+        } else {
+            echo $output->install_remote_plugins_buttons(null, null, $return);
+        }
+        echo $output->footer();
+        die();
+    }
+}