Skip to content

Commit

Permalink
MDL-49329 admin: Make plugin manager able to install from local zips too
Browse files Browse the repository at this point in the history
The plugin manager's method install_remote_plugins() has been changed to
install_plugins() and it is now able to install plugins from the
provided list of locally available ZIP files, too. This is used by the
Install plugins admin tool.
  • Loading branch information
mudrd8mz committed Oct 9, 2015
1 parent da54cf1 commit 2d00be6
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 38 deletions.
16 changes: 8 additions & 8 deletions admin/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@
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,
upgrade_install_plugins($installable, $confirminstalldep,
get_string('dependencyinstallhead', 'core_plugin'),
new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
);
Expand All @@ -380,7 +380,7 @@
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
if (!empty($installable[$installdep])) {
$installable = array($installable[$installdep]);
upgrade_install_remote_plugins($installable, $confirminstalldep,
upgrade_install_plugins($installable, $confirminstalldep,
get_string('dependencyinstallhead', 'core_plugin'),
new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
);
Expand All @@ -391,7 +391,7 @@
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,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
);
Expand All @@ -402,7 +402,7 @@
// 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,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdate' => $installupdate,
'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
Expand Down Expand Up @@ -493,7 +493,7 @@
if ($installdepx) {
require_sesskey();
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
upgrade_install_remote_plugins($installable, $confirminstalldep,
upgrade_install_plugins($installable, $confirminstalldep,
get_string('dependencyinstallhead', 'core_plugin'),
new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
);
Expand All @@ -505,7 +505,7 @@
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
if (!empty($installable[$installdep])) {
$installable = array($installable[$installdep]);
upgrade_install_remote_plugins($installable, $confirminstalldep,
upgrade_install_plugins($installable, $confirminstalldep,
get_string('dependencyinstallhead', 'core_plugin'),
new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
);
Expand All @@ -516,7 +516,7 @@
if ($installupdatex) {
require_sesskey();
$installable = $pluginman->filter_installable($pluginman->available_updates());
upgrade_install_remote_plugins($installable, $confirminstallupdate,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
);
Expand All @@ -527,7 +527,7 @@
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,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdate' => $installupdate,
'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
Expand Down
4 changes: 2 additions & 2 deletions admin/plugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
$PAGE->set_popup_notification_allowed(false);

$installable = $pluginman->filter_installable($pluginman->available_updates());
upgrade_install_remote_plugins($installable, $confirminstallupdate,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
);
Expand All @@ -201,7 +201,7 @@

if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
$installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
upgrade_install_remote_plugins($installable, $confirminstallupdate,
upgrade_install_plugins($installable, $confirminstallupdate,
get_string('updateavailableinstallallhead', 'core_admin'),
new moodle_url($PAGE->url, array('installupdate' => $installupdate,
'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
Expand Down
2 changes: 1 addition & 1 deletion admin/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ public function plugins_check_table(core_plugin_manager $pluginman, $version, ar
* @param moodle_url $cancel URL for the cancel link, defaults to the current page
* @return string HTML
*/
public function install_remote_plugins_buttons(moodle_url $continue=null, $label=null, moodle_url $cancel=null) {
public function install_plugins_buttons(moodle_url $continue=null, $label=null, moodle_url $cancel=null) {

$out = html_writer::start_div('install-remote-plugins-buttons');

Expand Down
6 changes: 3 additions & 3 deletions lang/en/plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
$string['overviewext'] = 'Additional plugins';
$string['overviewupdatable'] = 'Available updates';
$string['packagesdebug'] = 'Debugging output enabled';
$string['packagesdownloading'] = 'Downloading packages';
$string['packagesextracting'] = 'Extracting packages';
$string['packagesvalidating'] = 'Validating packages';
$string['packagesdownloading'] = 'Downloading {$a}';
$string['packagesextracting'] = 'Extracting {$a}';
$string['packagesvalidating'] = 'Validating {$a}';
$string['packagesvalidatingfailed'] = 'Installation aborted due to validation failure';
$string['packagesvalidatingok'] = 'Validation successful, installation can continue';
$string['plugincheckall'] = 'All plugins';
Expand Down
65 changes: 47 additions & 18 deletions lib/classes/plugin_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,20 @@ public function unzip_plugin_file($zipfilepath, $targetdir, $rootdir = '') {
return $this->get_code_manager()->unzip_plugin_file($zipfilepath, $targetdir, $rootdir);
}

/**
* Detects the plugin's name from its ZIP file.
*
* Plugin ZIP packages are expected to contain a single directory and the
* directory name would become the plugin name once extracted to the Moodle
* dirroot.
*
* @param string $zipfilepath full path to the ZIP files
* @return string|bool false on error
*/
public function get_plugin_zip_root_dir($zipfilepath) {
return $this->get_code_manager()->get_plugin_zip_root_dir($zipfilepath);
}

/**
* Return a list of missing dependencies.
*
Expand Down Expand Up @@ -1199,18 +1213,26 @@ public function can_uninstall_plugin($component) {
}

/**
* Perform the installation of plugins available in the plugins directory.
* Perform the installation of plugins.
*
* If used for installation of remote plugins from the Moodle Plugins
* directory, the $plugins must be list of {@link \core\update\remote_info}
* object that represent installable remote plugins. The caller can use
* {@link self::filter_installable()} to prepare the list.
*
* The list of plugins is supposed to be processed by
* {@link self::filter_installable()} to make sure all the plugins are
* valid.
* If used for installation of plugins from locally available ZIP files,
* the $plugins should be list of objects with properties ->component and
* ->zipfilepath.
*
* @param array $plugins list of installable remote plugins
* The method uses {@link mtrace()} to produce direct output and can be
* used in both web and cli interfaces.
*
* @param array $plugins list of plugins
* @param bool $confirmed should the files be really deployed into the dirroot?
* @param bool $silent perform without output
* @return bool true on success
*/
public function install_remote_plugins(array $plugins, $confirmed, $silent) {
public function install_plugins(array $plugins, $confirmed, $silent) {
global $CFG, $OUTPUT;

if (empty($plugins)) {
Expand All @@ -1223,24 +1245,32 @@ public function install_remote_plugins(array $plugins, $confirmed, $silent) {
$silent or $this->mtrace(get_string('packagesdebug', 'core_plugin'), PHP_EOL, DEBUG_NORMAL);

// Download all ZIP packages if we do not have them yet.
$silent or $this->mtrace(get_string('packagesdownloading', 'core_plugin'), ' ... ');
$zips = array();
foreach ($plugins as $plugin) {
$zips[$plugin->component] = $this->get_remote_plugin_zip($plugin->version->downloadurl, $plugin->version->downloadmd5);
$silent or $this->mtrace(PHP_EOL.$plugin->version->downloadurl, '', DEBUG_DEVELOPER);
$silent or $this->mtrace(PHP_EOL.' -> '.$zips[$plugin->component], ' ... ', DEBUG_DEVELOPER);
if (!$zips[$plugin->component]) {
$silent or $this->mtrace(get_string('error'));
return false;
if ($plugin instanceof \core\update\remote_info) {
$zips[$plugin->component] = $this->get_remote_plugin_zip($plugin->version->downloadurl,
$plugin->version->downloadmd5);
$silent or $this->mtrace(get_string('packagesdownloading', 'core_plugin', $plugin->component), ' ... ');
$silent or $this->mtrace(PHP_EOL.' <- '.$plugin->version->downloadurl, '', DEBUG_DEVELOPER);
$silent or $this->mtrace(PHP_EOL.' -> '.$zips[$plugin->component], ' ... ', DEBUG_DEVELOPER);
if (!$zips[$plugin->component]) {
$silent or $this->mtrace(get_string('error'));
return false;
}
$silent or $this->mtrace($ok);
} else {
if (empty($plugin->zipfilepath)) {
throw new coding_exception('Unexpected data structure provided');
}
$zips[$plugin->component] = $plugin->zipfilepath;
$silent or $this->mtrace('ZIP '.$plugin->zipfilepath, PHP_EOL, DEBUG_DEVELOPER);
}
}
$silent or $this->mtrace($ok);

// Validate all downloaded packages.
$silent or $this->mtrace(get_string('packagesvalidating', 'core_plugin'), ' ... '.PHP_EOL);
foreach ($plugins as $plugin) {
$zipfile = $zips[$plugin->component];
$silent or $this->mtrace('* '.s($plugin->name). ' ('.$plugin->component.')', ' ... ');
$silent or $this->mtrace(get_string('packagesvalidating', 'core_plugin', $plugin->component), ' ... ');
list($plugintype, $pluginname) = core_component::normalize_component($plugin->component);
$tmp = make_request_directory();
$zipcontents = $this->unzip_plugin_file($zipfile, $tmp, $pluginname);
Expand Down Expand Up @@ -1305,9 +1335,8 @@ public function install_remote_plugins(array $plugins, $confirmed, $silent) {
}

// Extract all ZIP packs do the dirroot.
$silent or $this->mtrace(get_string('packagesextracting', 'core_plugin'), ' ... '.PHP_EOL);
foreach ($plugins as $plugin) {
$silent or $this->mtrace('* '.s($plugin->name). ' ('.$plugin->component.')', ' ... ');
$silent or $this->mtrace(get_string('packagesextracting', 'core_plugin', $plugin->component), ' ... ');
$zipfile = $zips[$plugin->component];
list($plugintype, $pluginname) = core_component::normalize_component($plugin->component);
$target = $this->get_plugintype_root($plugintype);
Expand Down
40 changes: 40 additions & 0 deletions lib/classes/update/code_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,46 @@ public function unzip_plugin_file($zipfilepath, $targetdir, $rootdir = '') {
return $files;
}

/**
* Detects the plugin's name from its ZIP file.
*
* Plugin ZIP packages are expected to contain a single directory and the
* directory name would become the plugin name once extracted to the Moodle
* dirroot.
*
* @param string $zipfilepath full path to the ZIP files
* @return string|bool false on error
*/
public function get_plugin_zip_root_dir($zipfilepath) {

$fp = get_file_packer('application/zip');
$files = $fp->list_files($zipfilepath);

if (empty($files)) {
return false;
}

$rootdirname = null;
foreach ($files as $file) {
$pathnameitems = explode('/', $file->pathname);
if (empty($pathnameitems)) {
return false;
}
// Set the expected name of the root directory in the first
// iteration of the loop.
if ($rootdirname === null) {
$rootdirname = $pathnameitems[0];
}
// Require the same root directory for all files in the ZIP
// package.
if ($rootdirname !== $pathnameitems[0]) {
return false;
}
}

return $rootdirname;
}

// This is the end, my only friend, the end ... of external public API.

/**
Expand Down
11 changes: 11 additions & 0 deletions lib/tests/update_code_manager_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,15 @@ public function test_unzip_plugin_file() {
$zipfilepath = __DIR__.'/fixtures/update_validator/zips/bar.zip';
$files = $codeman->unzip_plugin_file($zipfilepath, $targetdir, 'bar');
}

public function test_get_plugin_zip_root_dir() {
$codeman = new \core\update\testable_code_manager();

$zipfilepath = __DIR__.'/fixtures/update_validator/zips/invalidroot.zip';
$this->assertEquals('invalid-root', $codeman->get_plugin_zip_root_dir($zipfilepath));

$zipfilepath = __DIR__.'/fixtures/update_validator/zips/bar.zip';
$this->assertEquals('bar', $codeman->get_plugin_zip_root_dir($zipfilepath));
}

}
12 changes: 6 additions & 6 deletions lib/upgradelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,7 @@ function check_upgrade_key($upgradekeyhash) {
* @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 cancelling at the validation screen
*/
function upgrade_install_remote_plugins(array $installable, $confirmed, $heading='', $continue=null, $return=null) {
function upgrade_install_plugins(array $installable, $confirmed, $heading='', $continue=null, $return=null) {
global $PAGE;

if (empty($return)) {
Expand All @@ -2398,8 +2398,8 @@ function upgrade_install_remote_plugins(array $installable, $confirmed, $heading

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);
if (!$pluginman->install_plugins($installable, true, true)) {
throw new moodle_exception('install_plugins_failed', 'core_plugin', $return);
}
// Always redirect to admin/index.php to perform the database upgrade.
redirect(new moodle_url('/admin/index.php?cache=0'));
Expand All @@ -2411,12 +2411,12 @@ function upgrade_install_remote_plugins(array $installable, $confirmed, $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);
$validated = $pluginman->install_plugins($installable, false, false);
echo html_writer::end_tag('pre');
if ($validated) {
echo $output->install_remote_plugins_buttons($continue, null, $return);
echo $output->install_plugins_buttons($continue, null, $return);
} else {
echo $output->install_remote_plugins_buttons(null, null, $return);
echo $output->install_plugins_buttons(null, null, $return);
}
echo $output->footer();
die();
Expand Down

0 comments on commit 2d00be6

Please sign in to comment.