Skip to content

Commit

Permalink
MDL-48493 admin: Make plugin installer able to detect plugin component
Browse files Browse the repository at this point in the history
On contrary to deeper heuristic (read: guessing) we perform in the
Plugins directory (such as looking at the names of the language files),
here we simply rely on the plugin component being correctly defined in
the version.php file.

The validator class has more robust processing, to make sure the
component declaration is not provided in a commented area of the
version.php etc.  However, as it is fully acceptable that the
auto-detection fails if the version.php uses non-standard syntax, this
easier approach is valid here.
  • Loading branch information
mudrd8mz committed Jan 15, 2015
1 parent da0ef2e commit bbf3cd4
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
101 changes: 101 additions & 0 deletions admin/tool/installaddon/classes/installer.php
Expand Up @@ -459,6 +459,27 @@ public function move_directory($source, $target, $dirpermissions, $filepermissio
clearstatcache();
}

/**
* Detect the given plugin's component name
*
* Only plugins that declare valid $plugin->component value in the version.php
* are supported.
*
* @param string $zipfilepath full path to the saved ZIP file
* @param string $workdir full path to the directory we can use for extracting required bits from the archive
* @return string|bool declared component name or false if unable to detect
*/
public function detect_plugin_component($zipfilepath, $workdir) {

$versionphp = $this->extract_versionphp_file($zipfilepath, $workdir);

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

return $this->detect_plugin_component_from_versionphp(file_get_contents($workdir.'/'.$versionphp));
}

//// End of external API ///////////////////////////////////////////////////

/**
Expand Down Expand Up @@ -626,6 +647,86 @@ protected function decode_remote_request($request) {

return $data;
}

/**
* Extracts the version.php from the given plugin ZIP file into the target directory
*
* @param string $zipfilepath full path to the saved ZIP file
* @param string $targetdir full path to extract the file to
* @return string|bool path to the version.php within the $targetpath; false on error (e.g. not found)
*/
protected function extract_versionphp_file($zipfilepath, $targetdir) {
global $CFG;
require_once($CFG->libdir.'/filelib.php');

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

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

$rootdirname = null;
$found = null;

foreach ($files as $file) {
// Valid plugin ZIP package has just one root directory with all
// files in it.
$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;
}

// If we reached the valid version.php file, remember it.
if ($pathnameitems[1] === 'version.php' and !$file->is_directory and $file->size > 0) {
$found = $file->pathname;
}
}

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

$extracted = $fp->extract_to_pathname($zipfilepath, $targetdir, array($found));

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

// The following syntax uses function array dereferencing, added in PHP 5.4.0.
return array_keys($extracted)[0];
}

/**
* Return the plugin component declared in its version.php file
*
* @param string $code the contents of the version.php file
* @return string|bool declared plugin component or false if unable to detect
*/
protected function detect_plugin_component_from_versionphp($code) {

$result = preg_match_all('#^\s*\$plugin\->component\s*=\s*([\'"])(.+?_.+?)\1\s*;#m', $code, $matches);

// Return if and only if the single match was detected.
if ($result === 1 and !empty($matches[2][0])) {
return $matches[2][0];
}

return false;
}
}


Expand Down
Binary file added admin/tool/installaddon/tests/fixtures/zips/bar.zip
Binary file not shown.
19 changes: 19 additions & 0 deletions admin/tool/installaddon/tests/installer_test.php
Expand Up @@ -143,6 +143,21 @@ public function test_move_directory() {
$this->assertTrue(is_file($jobroot.'/moved/sub/folder/readme.txt'));
$this->assertSame('Hello world!', file_get_contents($jobroot.'/moved/sub/folder/readme.txt'));
}

public function test_detect_plugin_component() {
$jobid = md5(rand().uniqid('test_', true));
$workdir = make_temp_directory('tool_installaddon/'.$jobid.'/version');
$zipfile = __DIR__.'/fixtures/zips/bar.zip';
$installer = tool_installaddon_installer::instance();
$this->assertEquals('foo_bar', $installer->detect_plugin_component($zipfile, $workdir));
}

public function test_detect_plugin_component_from_versionphp() {
$installer = testable_tool_installaddon_installer::instance();
$this->assertEquals('bar_bar_conan', $installer->detect_plugin_component_from_versionphp('
$plugin->version = 2014121300;
$plugin->component= "bar_bar_conan" ; // Go Arnie go!'));
}
}


Expand Down Expand Up @@ -173,4 +188,8 @@ public function testable_decode_remote_request($request) {
protected function should_send_site_info() {
return true;
}

public function detect_plugin_component_from_versionphp($code) {
return parent::detect_plugin_component_from_versionphp($code);
}
}

0 comments on commit bbf3cd4

Please sign in to comment.