MDL-49329 admin: Introduce new \core\update\validator class

This is not really new class. Originally, the validator was implemented
in the scope of the tool_installaddon plugin. But we need its features
in other parts at the core level (e.g. during the upgrade to validate
available missing dependencies or available updates). Looking back, it
was a mistake to have it implemented at the plugin level rather than the
core level.

So this patch moves the tool_installaddon_validator class to
\core\update\validator class, together with associated unit tests,
fixture files and strings. There were changes made in the
tool_installaddon to make use of this new class instead of the old one.

This is a great step towards the bright future, hopefully there won't be
many regressions.

AMOS BEGIN
 MOV [validationmsg_componentmatch,tool_installaddon],[validationmsg_componentmatch,core_plugin]
 MOV [validationmsg_componentmismatchname,tool_installaddon],[validationmsg_componentmismatchname,core_plugin]
 MOV [validationmsg_componentmismatchname_help,tool_installaddon],[validationmsg_componentmismatchname_help,core_plugin]
 MOV [validationmsg_componentmismatchname_info,tool_installaddon],[validationmsg_componentmismatchname_info,core_plugin]
 MOV [validationmsg_componentmismatchtype,tool_installaddon],[validationmsg_componentmismatchtype,core_plugin]
 MOV [validationmsg_componentmismatchtype_info,tool_installaddon],[validationmsg_componentmismatchtype_info,core_plugin]
 MOV [validationmsg_filenotexists,tool_installaddon],[validationmsg_filenotexists,core_plugin]
 MOV [validationmsg_filesnumber,tool_installaddon],[validationmsg_filesnumber,core_plugin]
 MOV [validationmsg_filestatus,tool_installaddon],[validationmsg_filestatus,core_plugin]
 MOV [validationmsg_filestatus_info,tool_installaddon],[validationmsg_filestatus_info,core_plugin]
 MOV [validationmsg_foundlangfile,tool_installaddon],[validationmsg_foundlangfile,core_plugin]
 MOV [validationmsg_maturity,tool_installaddon],[validationmsg_maturity,core_plugin]
 MOV [validationmsg_maturity_help,tool_installaddon],[validationmsg_maturity_help,core_plugin]
 MOV [validationmsg_missingcomponent,tool_installaddon],[validationmsg_missingcomponent,core_plugin]
 MOV [validationmsg_missingcomponent_help,tool_installaddon],[validationmsg_missingcomponent_help,core_plugin]
 MOV [validationmsg_missingcomponent_link,tool_installaddon],[validationmsg_missingcomponent_link,core_plugin]
 MOV [validationmsg_missingexpectedlangenfile,tool_installaddon],[validationmsg_missingexpectedlangenfile,core_plugin]
 MOV [validationmsg_missingexpectedlangenfile_info,tool_installaddon],[validationmsg_missingexpectedlangenfile_info,core_plugin]
 MOV [validationmsg_missinglangenfile,tool_installaddon],[validationmsg_missinglangenfile,core_plugin]
 MOV [validationmsg_missinglangenfolder,tool_installaddon],[validationmsg_missinglangenfolder,core_plugin]
 MOV [validationmsg_missingversion,tool_installaddon],[validationmsg_missingversion,core_plugin]
 MOV [validationmsg_missingversionphp,tool_installaddon],[validationmsg_missingversionphp,core_plugin]
 MOV [validationmsg_multiplelangenfiles,tool_installaddon],[validationmsg_multiplelangenfiles,core_plugin]
 MOV [validationmsg_onedir,tool_installaddon],[validationmsg_onedir,core_plugin]
 MOV [validationmsg_onedir_help,tool_installaddon],[validationmsg_onedir_help,core_plugin]
 MOV [validationmsg_pathwritable,tool_installaddon],[validationmsg_pathwritable,core_plugin]
 MOV [validationmsg_pluginversion,tool_installaddon],[validationmsg_pluginversion,core_plugin]
 MOV [validationmsg_release,tool_installaddon],[validationmsg_release,core_plugin]
 MOV [validationmsg_requiresmoodle,tool_installaddon],[validationmsg_requiresmoodle,core_plugin]
 MOV [validationmsg_rootdir,tool_installaddon],[validationmsg_rootdir,core_plugin]
 MOV [validationmsg_rootdir_help,tool_installaddon],[validationmsg_rootdir_help,core_plugin]
 MOV [validationmsg_rootdirinvalid,tool_installaddon],[validationmsg_rootdirinvalid,core_plugin]
 MOV [validationmsg_rootdirinvalid_help,tool_installaddon],[validationmsg_rootdirinvalid_help,core_plugin]
 MOV [validationmsg_targetexists,tool_installaddon],[validationmsg_targetexists,core_plugin]
 MOV [validationmsg_targetexists_help,tool_installaddon],[validationmsg_targetexists_help,core_plugin]
 MOV [validationmsg_unknowntype,tool_installaddon],[validationmsg_unknowntype,core_plugin]
 MOV [validationmsg_versionphpsyntax,tool_installaddon],[validationmsg_versionphpsyntax,core_plugin]
 MOV [validationmsglevel_debug,tool_installaddon],[validationmsglevel_debug,core_plugin]
 MOV [validationmsglevel_error,tool_installaddon],[validationmsglevel_error,core_plugin]
 MOV [validationmsglevel_info,tool_installaddon],[validationmsglevel_info,core_plugin]
 MOV [validationmsglevel_warning,tool_installaddon],[validationmsglevel_warning,core_plugin]
AMOS END
This commit is contained in:
David Mudrák
2015-10-08 23:32:03 +02:00
parent 0e442ee776
commit f2d8ed45e3
42 changed files with 296 additions and 227 deletions
@@ -58,47 +58,6 @@ $string['remoterequestpermcheck'] = 'There is a request to install plugin {$a->n
$string['remoterequestpluginfoexception'] = 'Oops... An error occurred while trying to obtain information about the plugin {$a->name} ({$a->component}) version {$a->version}. The plugin cannot be installed. Turn debugging mode on to see details of the error.';
$string['typedetectionfailed'] = 'Unable to detect the plugin type. Please choose the plugin type manually.';
$string['validation'] = 'Plugin package validation';
$string['validationmsg_componentmatch'] = 'Full component name';
$string['validationmsg_componentmismatchname'] = 'Plugin name mismatch';
$string['validationmsg_componentmismatchname_help'] = 'Some ZIP packages, such as those generated by Github, may contain an incorrect root directory name. You need to fix the name of the root directory to match the declared plugin name.';
$string['validationmsg_componentmismatchname_info'] = 'The plugin declares its name is \'{$a}\' but that does not match the name of the root directory.';
$string['validationmsg_componentmismatchtype'] = 'Plugin type mismatch';
$string['validationmsg_componentmismatchtype_info'] = 'Expected type \'{$a->expected}\' but the plugin declares its type is \'{$a->found}\'.';
$string['validationmsg_filenotexists'] = 'Extracted file not found';
$string['validationmsg_filesnumber'] = 'Not enough files found in the package';
$string['validationmsg_filestatus'] = 'Unable to extract all files';
$string['validationmsg_filestatus_info'] = 'Attempting to extract file {$a->file} resulted in error \'{$a->status}\'.';
$string['validationmsg_foundlangfile'] = 'Found language file';
$string['validationmsg_maturity'] = 'Declared maturity level';
$string['validationmsg_maturity_help'] = 'The plugin can declare its maturity level. If the maintainer considers the plugin stable, the declared maturity level will read MATURITY_STABLE. All other maturity levels (such as alpha or beta) should be considered unstable and a warning is raised.';
$string['validationmsg_missingcomponent'] = 'Plugin does not declare its component name';
$string['validationmsg_missingcomponent_help'] = 'All plugins must provide their full component name via the `$plugin->component` declaration in the version.php file.';
$string['validationmsg_missingcomponent_link'] = 'Development:version.php';
$string['validationmsg_missingexpectedlangenfile'] = 'English language file name mismatch';
$string['validationmsg_missingexpectedlangenfile_info'] = 'The given plugin type is missing the expected English language file {$a}.';
$string['validationmsg_missinglangenfile'] = 'No English language file found';
$string['validationmsg_missinglangenfolder'] = 'Missing English language folder';
$string['validationmsg_missingversion'] = 'Plugin does not declare its version';
$string['validationmsg_missingversionphp'] = 'File version.php not found';
$string['validationmsg_multiplelangenfiles'] = 'Multiple English language files found';
$string['validationmsg_onedir'] = 'Invalid structure of the ZIP package.';
$string['validationmsg_onedir_help'] = 'The ZIP package must contain just one root directory that holds the plugin code. The name of that root directory must match the name of the plugin.';
$string['validationmsg_pathwritable'] = 'Write access check';
$string['validationmsg_pluginversion'] = 'Plugin version';
$string['validationmsg_release'] = 'Plugin release';
$string['validationmsg_requiresmoodle'] = 'Required Moodle version';
$string['validationmsg_rootdir'] = 'Name of the plugin to be installed';
$string['validationmsg_rootdir_help'] = 'The name of the root directory in the ZIP package forms the name of the plugin to be installed. If the name is not correct, you may wish to rename the root directory in the ZIP prior to installing the plugin.';
$string['validationmsg_rootdirinvalid'] = 'Invalid plugin name';
$string['validationmsg_rootdirinvalid_help'] = 'The name of the root directory in the ZIP package violates formal syntax requirements. Some ZIP packages, such as those generated by Github, may contain an incorrect root directory name. You need to fix the name of the root directory to match the plugin name.';
$string['validationmsg_targetexists'] = 'Target location already exists';
$string['validationmsg_targetexists_help'] = 'The directory that the plugin is to be installed to must not yet exist.';
$string['validationmsg_unknowntype'] = 'Unknown plugin type';
$string['validationmsg_versionphpsyntax'] = 'Unsupported syntax detected in version.php file';
$string['validationmsglevel_debug'] = 'Debug';
$string['validationmsglevel_error'] = 'Error';
$string['validationmsglevel_info'] = 'OK';
$string['validationmsglevel_warning'] = 'Warning';
$string['validationresult0'] = 'Validation failed!';
$string['validationresult0_help'] = 'A serious problem was detected and so it is not safe to install the plugin. See the validation log messages for details.';
$string['validationresult1'] = 'Validation passed!';
+14 -57
View File
@@ -37,7 +37,7 @@ class tool_installaddon_renderer extends plugin_renderer_base {
/** @var tool_installaddon_installer */
protected $installer = null;
/** @var tool_installaddon_validator */
/** @var \core\update\validator */
protected $validator = null;
/**
@@ -55,12 +55,12 @@ class tool_installaddon_renderer extends plugin_renderer_base {
}
/**
* Sets the tool_installaddon_validator instance being used.
* Sets the \core\update\validator instance being used.
*
* @throws coding_exception if the validator has been already set
* @param tool_installaddon_validator $validator
* @param \core\update\validator $validator
*/
public function set_validator_instance(tool_installaddon_validator $validator) {
public function set_validator_instance(\core\update\validator $validator) {
if (is_null($this->validator)) {
$this->validator = $validator;
} else {
@@ -110,11 +110,8 @@ class tool_installaddon_renderer extends plugin_renderer_base {
throw new coding_exception('Validator instance has not been set.');
}
$out = $this->output->header();
$out .= $this->validation_page_heading();
$out = $this->validation_page_heading();
$out .= $this->validation_page_messages();
$out .= $this->validation_page_continue();
$out .= $this->output->footer();
return $out;
}
@@ -337,27 +334,17 @@ class tool_installaddon_renderer extends plugin_renderer_base {
continue;
}
$msgstatus = get_string('validationmsglevel_'.$message->level, 'tool_installaddon');
$msgtext = $msgtext = s($message->msgcode);
if (is_null($message->addinfo)) {
$msginfo = '';
} else {
$msgstatus = $validator->message_level_name($message->level);
$msgtext = $validator->message_code_name($message->msgcode);
$msginfo = $validator->message_code_info($message->msgcode, $message->addinfo);
if (empty($msginfo) and $message->addinfo !== null) {
$msginfo = html_writer::tag('pre', s(print_r($message->addinfo, true)));
}
$msghelp = '';
// Replace the message code with the string if it is defined.
if ($stringman->string_exists('validationmsg_'.$message->msgcode, 'tool_installaddon')) {
$msgtext = get_string('validationmsg_'.$message->msgcode, 'tool_installaddon');
// And check for the eventual help, too.
if ($stringman->string_exists('validationmsg_'.$message->msgcode.'_help', 'tool_installaddon')) {
$msghelp = $this->output->help_icon('validationmsg_'.$message->msgcode, 'tool_installaddon');
}
}
// Re-format the message info using a string if it is define.
if (!is_null($message->addinfo) and $stringman->string_exists('validationmsg_'.$message->msgcode.'_info', 'tool_installaddon')) {
$msginfo = get_string('validationmsg_'.$message->msgcode.'_info', 'tool_installaddon', $message->addinfo);
$msghelpicon = $validator->message_help_icon($message->msgcode);
if ($msghelpicon) {
$msghelp = $this->output->render($msghelpicon);
} else {
$msghelp = '';
}
$row = new html_table_row(array($msgstatus, $msgtext.$msghelp, $msginfo));
@@ -368,34 +355,4 @@ class tool_installaddon_renderer extends plugin_renderer_base {
return html_writer::table($table);
}
/**
* Renders widgets to continue from the validation results page
*
* @return string
*/
protected function validation_page_continue() {
$output = '';
$conturl = $this->validator->get_continue_url();
if (is_null($conturl)) {
$contbutton = '';
} else {
$contbutton = $this->output->single_button(
$conturl, get_string('installaddon', 'tool_installaddon'), 'post',
array('class' => 'singlebutton continuebutton'));
$output .= $this->output->heading(get_string('acknowledgement', 'tool_installaddon'), 3);
$output .= $this->output->container(get_string('acknowledgementtext', 'tool_installaddon'));
}
$cancelbutton = $this->output->single_button(
new moodle_url('/admin/tool/installaddon/index.php'), get_string('cancel', 'core'), 'get',
array('class' => 'singlebutton cancelbutton'));
$output .= $this->output->container($cancelbutton.$contbutton, 'postvalidationbuttons');
return $output;
}
}
@@ -50,10 +50,12 @@ class tool_installaddon_installer_testcase extends advanced_testcase {
}
public function test_extract_installfromzip_file() {
global $CFG;
$jobid = md5(rand().uniqid('test_', true));
$sourcedir = make_temp_directory('tool_installaddon/'.$jobid.'/source');
$contentsdir = make_temp_directory('tool_installaddon/'.$jobid.'/contents');
copy(dirname(__FILE__).'/fixtures/zips/invalidroot.zip', $sourcedir.'/testinvalidroot.zip');
copy($CFG->libdir.'/tests/fixtures/update_validator/zips/invalidroot.zip', $sourcedir.'/testinvalidroot.zip');
$installer = tool_installaddon_installer::instance();
$files = $installer->extract_installfromzip_file($sourcedir.'/testinvalidroot.zip', $contentsdir, 'fixed_root');
@@ -145,9 +147,11 @@ class tool_installaddon_installer_testcase extends advanced_testcase {
}
public function test_detect_plugin_component() {
global $CFG;
$jobid = md5(rand().uniqid('test_', true));
$workdir = make_temp_directory('tool_installaddon/'.$jobid.'/version');
$zipfile = __DIR__.'/fixtures/zips/bar.zip';
$zipfile = $CFG->libdir.'/tests/fixtures/update_validator/zips/bar.zip';
$installer = tool_installaddon_installer::instance();
$this->assertEquals('foo_bar', $installer->detect_plugin_component($zipfile, $workdir));
}
+26 -12
View File
@@ -55,24 +55,38 @@ $zipcontentpath = make_temp_directory('tool_installaddon/'.$jobid.'/contents');
$zipcontentfiles = $installer->extract_installfromzip_file($zipfilepath, $zipcontentpath, $rootdir);
// Validate the contents of the plugin ZIP file.
$validator = tool_installaddon_validator::instance($zipcontentpath, $zipcontentfiles);
$validator = \core\update\validator::instance($zipcontentpath, $zipcontentfiles);
$validator->assert_plugin_type($plugintype);
$validator->assert_moodle_version($CFG->version);
$result = $validator->execute();
if ($result) {
$validator->set_continue_url(new moodle_url('/admin/tool/installaddon/deploy.php', array(
'sesskey' => sesskey(),
'jobid' => $jobid,
'type' => $plugintype,
'name' => $validator->get_rootdir())));
} else {
fulldelete($CFG->tempdir.'/tool_installaddon/'.$jobid);
}
// Display the validation results.
$output = $PAGE->get_renderer('tool_installaddon');
$output->set_installer_instance($installer);
$output->set_validator_instance($validator);
echo $output->header();
echo $output->validation_page();
if ($result) {
$conturl = new moodle_url('/admin/tool/installaddon/deploy.php', array(
'sesskey' => sesskey(),
'jobid' => $jobid,
'type' => $plugintype,
'name' => $validator->get_rootdir())
);
$contbutton = $output->single_button($conturl, get_string('installaddon', 'tool_installaddon'), 'post',
array('class' => 'singlebutton continuebutton'));
echo $output->heading(get_string('acknowledgement', 'tool_installaddon'), 3);
echo $output->container(get_string('acknowledgementtext', 'tool_installaddon'));
} else {
$contbutton = '';
fulldelete($CFG->tempdir.'/tool_installaddon/'.$jobid);
}
$cancelbutton = $output->single_button(new moodle_url('/admin/tool/installaddon/index.php'), get_string('cancel', 'core'),
'get', array('class' => 'singlebutton cancelbutton'));
echo $output->container($cancelbutton.$contbutton, 'postvalidationbuttons');
echo $output->footer();
+41
View File
@@ -176,6 +176,47 @@ $string['uninstallextraconfirmblock'] = 'There are {$a->instances} instances of
$string['uninstallextraconfirmenrol'] = 'There are {$a->enrolments} user enrolments.';
$string['uninstallextraconfirmmod'] = 'There are {$a->instances} instances of this module in {$a->courses} courses.';
$string['uninstalling'] = 'Uninstalling {$a->name}';
$string['validationmsg_componentmatch'] = 'Full component name';
$string['validationmsg_componentmismatchname'] = 'Plugin name mismatch';
$string['validationmsg_componentmismatchname_help'] = 'Some ZIP packages, such as those generated by Github, may contain an incorrect root directory name. You need to fix the name of the root directory to match the declared plugin name.';
$string['validationmsg_componentmismatchname_info'] = 'The plugin declares its name is \'{$a}\' but that does not match the name of the root directory.';
$string['validationmsg_componentmismatchtype'] = 'Plugin type mismatch';
$string['validationmsg_componentmismatchtype_info'] = 'Expected type \'{$a->expected}\' but the plugin declares its type is \'{$a->found}\'.';
$string['validationmsg_filenotexists'] = 'Extracted file not found';
$string['validationmsg_filesnumber'] = 'Not enough files found in the package';
$string['validationmsg_filestatus'] = 'Unable to extract all files';
$string['validationmsg_filestatus_info'] = 'Attempting to extract file {$a->file} resulted in error \'{$a->status}\'.';
$string['validationmsg_foundlangfile'] = 'Found language file';
$string['validationmsg_maturity'] = 'Declared maturity level';
$string['validationmsg_maturity_help'] = 'The plugin can declare its maturity level. If the maintainer considers the plugin stable, the declared maturity level will read MATURITY_STABLE. All other maturity levels (such as alpha or beta) should be considered unstable and a warning is raised.';
$string['validationmsg_missingcomponent'] = 'Plugin does not declare its component name';
$string['validationmsg_missingcomponent_help'] = 'All plugins must provide their full component name via the `$plugin->component` declaration in the version.php file.';
$string['validationmsg_missingcomponent_link'] = 'Development:version.php';
$string['validationmsg_missingexpectedlangenfile'] = 'English language file name mismatch';
$string['validationmsg_missingexpectedlangenfile_info'] = 'The given plugin type is missing the expected English language file {$a}.';
$string['validationmsg_missinglangenfile'] = 'No English language file found';
$string['validationmsg_missinglangenfolder'] = 'Missing English language folder';
$string['validationmsg_missingversion'] = 'Plugin does not declare its version';
$string['validationmsg_missingversionphp'] = 'File version.php not found';
$string['validationmsg_multiplelangenfiles'] = 'Multiple English language files found';
$string['validationmsg_onedir'] = 'Invalid structure of the ZIP package.';
$string['validationmsg_onedir_help'] = 'The ZIP package must contain just one root directory that holds the plugin code. The name of that root directory must match the name of the plugin.';
$string['validationmsg_pathwritable'] = 'Write access check';
$string['validationmsg_pluginversion'] = 'Plugin version';
$string['validationmsg_release'] = 'Plugin release';
$string['validationmsg_requiresmoodle'] = 'Required Moodle version';
$string['validationmsg_rootdir'] = 'Name of the plugin to be installed';
$string['validationmsg_rootdir_help'] = 'The name of the root directory in the ZIP package forms the name of the plugin to be installed. If the name is not correct, you may wish to rename the root directory in the ZIP prior to installing the plugin.';
$string['validationmsg_rootdirinvalid'] = 'Invalid plugin name';
$string['validationmsg_rootdirinvalid_help'] = 'The name of the root directory in the ZIP package violates formal syntax requirements. Some ZIP packages, such as those generated by Github, may contain an incorrect root directory name. You need to fix the name of the root directory to match the plugin name.';
$string['validationmsg_targetexists'] = 'Target location already exists';
$string['validationmsg_targetexists_help'] = 'The directory that the plugin is to be installed to must not yet exist.';
$string['validationmsg_unknowntype'] = 'Unknown plugin type';
$string['validationmsg_versionphpsyntax'] = 'Unsupported syntax detected in version.php file';
$string['validationmsglevel_debug'] = 'Debug';
$string['validationmsglevel_error'] = 'Error';
$string['validationmsglevel_info'] = 'OK';
$string['validationmsglevel_warning'] = 'Warning';
$string['version'] = 'Version';
$string['versiondb'] = 'Current version';
$string['versiondisk'] = 'New version';
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -21,12 +20,18 @@
* Uses fragments of the local_plugins_archive_validator class copyrighted by
* Marina Glancy that is part of the local_plugins plugin.
*
* @package tool_installaddon
* @subpackage classes
* @copyright 2013 David Mudrak <david@moodle.com>
* @package core_plugin
* @subpackage validation
* @copyright 2013, 2015 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\update;
use core_component;
use help_icon;
use coding_exception;
defined('MOODLE_INTERNAL') || die();
if (!defined('T_ML_COMMENT')) {
@@ -38,10 +43,10 @@ if (!defined('T_ML_COMMENT')) {
/**
* Validates the contents of extracted plugin ZIP file
*
* @copyright 2013 David Mudrak <david@moodle.com>
* @copyright 2013, 2015 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_installaddon_validator {
class validator {
/** Critical error message level, causes the validation fail. */
const ERROR = 'error';
@@ -79,15 +84,12 @@ class tool_installaddon_validator {
/** @var string|null the name of found English language file without the .php extension */
protected $langfilename = null;
/** @var moodle_url|null URL to continue with the installation of validated add-on */
protected $continueurl = null;
/**
* Factory method returning instance of the validator
*
* @param string $zipcontentpath full path to the extracted ZIP contents
* @param array $zipcontentfiles (string)filerelpath => (bool|string)true or error
* @return tool_installaddon_validator
* @return \core\update\validator
*/
public static function instance($zipcontentpath, array $zipcontentfiles) {
return new static($zipcontentpath, $zipcontentfiles);
@@ -158,13 +160,76 @@ class tool_installaddon_validator {
return $this->messages;
}
/**
* Returns human readable localised name of the given log level.
*
* @param string $level e.g. self::INFO
* @return string
*/
public function message_level_name($level) {
return get_string('validationmsglevel_'.$level, 'core_plugin');
}
/**
* If defined, returns human readable validation code.
*
* Otherwise, it simply returns the code itself as a fallback.
*
* @param string $msgcode
* @return string
*/
public function message_code_name($msgcode) {
$stringman = get_string_manager();
if ($stringman->string_exists('validationmsg_'.$msgcode, 'core_plugin')) {
return get_string('validationmsg_'.$msgcode, 'core_plugin');
}
return $msgcode;
}
/**
* Returns help icon for the message code if defined.
*
* @param string $msgcode
* @return \help_icon|false
*/
public function message_help_icon($msgcode) {
$stringman = get_string_manager();
if ($stringman->string_exists('validationmsg_'.$msgcode.'_help', 'core_plugin')) {
return new help_icon('validationmsg_'.$msgcode, 'core_plugin');
}
return false;
}
/**
* Localizes the message additional info if it exists.
*
* @param string $msgcode
* @param array|string|null $addinfo value for the $a placeholder in the string
* @return string
*/
public function message_code_info($msgcode, $addinfo) {
$stringman = get_string_manager();
if ($addinfo !== null and $stringman->string_exists('validationmsg_'.$msgcode.'_info', 'core_plugin')) {
return get_string('validationmsg_'.$msgcode.'_info', 'core_plugin', $addinfo);
}
return '';
}
/**
* Return the information provided by the the plugin's version.php
*
* If version.php was not found in the plugin (which is tolerated for
* themes only at the moment), null is returned. Otherwise the array
* is returned. It may be empty if no information was parsed (which
* should not happen).
* If version.php was not found in the plugin, null is returned. Otherwise
* the array is returned. It may be empty if no information was parsed
* (which should not happen).
*
* @return null|array
*/
@@ -194,27 +259,7 @@ class tool_installaddon_validator {
return $this->rootdir;
}
/**
* Sets the URL to continue to after successful validation
*
* @param moodle_url $url
*/
public function set_continue_url(moodle_url $url) {
$this->continueurl = $url;
}
/**
* Get the URL to continue to after successful validation
*
* Null is returned if the URL has not been explicitly set by the caller.
*
* @return moodle_url|null
*/
public function get_continue_url() {
return $this->continueurl;
}
// End of external API /////////////////////////////////////////////////////
// End of external API.
/**
* @param string $zipcontentpath full path to the extracted ZIP contents
@@ -225,7 +270,7 @@ class tool_installaddon_validator {
$this->extractfiles = $zipcontentfiles;
}
// Validation methods //////////////////////////////////////////////////////
// Validation methods.
/**
* @return bool false if files in the ZIP do not have required layout
@@ -254,7 +299,8 @@ class tool_installaddon_validator {
foreach (array_keys($this->extractfiles) as $filerelname) {
$matches = array();
if (!preg_match("#^([^/]+)/#", $filerelname, $matches) or (!is_null($this->rootdir) and $this->rootdir !== $matches[1])) {
if (!preg_match("#^([^/]+)/#", $filerelname, $matches)
or (!is_null($this->rootdir) and $this->rootdir !== $matches[1])) {
$this->add_message(self::ERROR, 'onedir');
return false;
}
@@ -408,7 +454,6 @@ class tool_installaddon_validator {
return true;
}
/**
* @return bool false of the given add-on can't be installed into its location
*/
@@ -446,7 +491,7 @@ class tool_installaddon_validator {
return true;
}
// Helper methods //////////////////////////////////////////////////////////
// Helper methods.
/**
* Get as much information from existing version.php as possible
@@ -547,7 +592,6 @@ class tool_installaddon_validator {
return $output;
}
/**
* Returns the full path to the root directory of the given plugin type
*
BIN
View File
Binary file not shown.
Binary file not shown.
+54
View File
@@ -0,0 +1,54 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides testable_core_update_validator class.
*
* @package core_plugin
* @category test
* @copyright 2013, 2015 David Mudrak <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Provides access to protected methods we want to explicitly test
*
* @copyright 2013, 2015 David Mudrak <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class testable_core_update_validator extends \core\update\validator {
public function testable_parse_version_php($fullpath) {
return parent::parse_version_php($fullpath);
}
public function get_plugintype_location($plugintype) {
$testableroot = make_temp_directory('testable_core_update_validator/plugintypes');
if (!is_dir($testableroot.'/'.$plugintype)) {
make_temp_directory('testable_core_update_validator/plugintypes/'.$plugintype);
}
if ($plugintype === 'local') {
// We need the following for the test_validate_target_location() method
make_temp_directory('testable_core_update_validator/plugintypes/local/greenbar');
}
return $testableroot.'/'.$plugintype;
}
}
+2 -2
View File
@@ -106,7 +106,7 @@ class core_update_code_manager_testcase extends advanced_testcase {
public function test_unzip_plugin_file() {
$codeman = new \core\update\testable_code_manager();
$zipfilepath = __DIR__.'/fixtures/invalidroot.zip';
$zipfilepath = __DIR__.'/fixtures/update_validator/zips/invalidroot.zip';
$targetdir = make_request_directory();
$files = $codeman->unzip_plugin_file($zipfilepath, $targetdir);
@@ -141,7 +141,7 @@ class core_update_code_manager_testcase extends advanced_testcase {
}
}
$zipfilepath = __DIR__.'/fixtures/bar.zip';
$zipfilepath = __DIR__.'/fixtures/update_validator/zips/bar.zip';
$files = $codeman->unzip_plugin_file($zipfilepath, $targetdir, 'bar');
}
}
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -18,39 +17,40 @@
/**
* Provides the unit tests class and some helper classes
*
* @package tool_installaddon
* @package core_plugin
* @category test
* @copyright 2013 David Mudrak <david@moodle.com>
* @copyright 2013, 2015 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/testable_update_validator.php');
/**
* Unit tests for the {@link tool_installaddon_installer} class
* Unit tests for the {@link \core\update\validator} class
*
* @copyright 2013 David Mudrak <david@moodle.com>
* @copyright 2013, 2015 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_installaddon_validator_testcase extends basic_testcase {
class core_update_validator_testcase extends advanced_testcase {
public function test_validate_files_layout() {
$fixtures = dirname(__FILE__).'/fixtures';
$fixtures = __DIR__.'/fixtures/update_validator';
// Non-existing directory.
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nulldir', array(
$validator = testable_core_update_validator::instance($fixtures.'/nulldir', array(
'null/' => true,
'null/lang/' => true,
'null/lang/en/' => true,
'null/lang/en/null.php' => true));
$this->assertEquals('testable_tool_installaddon_validator', get_class($validator));
$this->assertEquals('testable_core_update_validator', get_class($validator));
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR,
'filenotexists', array('file' => 'null/')));
// Missing expected file
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'foobar/' => true,
'foobar/version.php' => true,
'foobar/index.php' => true,
@@ -63,7 +63,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
'filenotexists', array('file' => 'foobar/NOTEXISTS.txt')));
// Errors during ZIP extraction
$validator = testable_tool_installaddon_validator::instance($fixtures.'/multidir', array(
$validator = testable_core_update_validator::instance($fixtures.'/multidir', array(
'one/' => true,
'one/version.php' => 'Can not write target file',
'two/' => true,
@@ -73,14 +73,14 @@ class tool_installaddon_validator_testcase extends basic_testcase {
array('file' => 'one/version.php', 'status' => 'Can not write target file')));
// Insufficient number of extracted files
$validator = testable_tool_installaddon_validator::instance($fixtures.'/emptydir', array(
$validator = testable_core_update_validator::instance($fixtures.'/emptydir', array(
'emptydir/' => true,
'emptydir/README.txt' => true));
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'filesnumber'));
// No wrapping directory
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nowrapdir', array(
$validator = testable_core_update_validator::instance($fixtures.'/nowrapdir', array(
'version.php' => true,
'index.php' => true,
'lang/' => true,
@@ -90,7 +90,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
// Multiple directories
$validator = testable_tool_installaddon_validator::instance($fixtures.'/multidir', array(
$validator = testable_core_update_validator::instance($fixtures.'/multidir', array(
'one/' => true,
'one/version.php' => true,
'two/' => true,
@@ -99,7 +99,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
// Invalid root directory name
$validator = testable_tool_installaddon_validator::instance($fixtures.'/github', array(
$validator = testable_core_update_validator::instance($fixtures.'/github', array(
'moodle-repository_mahara-master/' => true,
'moodle-repository_mahara-master/lang/' => true,
'moodle-repository_mahara-master/lang/en/' => true,
@@ -111,9 +111,9 @@ class tool_installaddon_validator_testcase extends basic_testcase {
}
public function test_validate_version_php() {
$fixtures = dirname(__FILE__).'/fixtures';
$fixtures = __DIR__.'/fixtures/update_validator';
$validator = testable_tool_installaddon_validator::instance($fixtures.'/noversiontheme', array(
$validator = testable_core_update_validator::instance($fixtures.'/noversiontheme', array(
'noversion/' => true,
'noversion/lang/' => true,
'noversion/lang/en/' => true,
@@ -124,7 +124,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'missingversionphp'));
$this->assertTrue(is_null($validator->get_versionphp_info()));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/noversionmod', array(
$validator = testable_core_update_validator::instance($fixtures.'/noversionmod', array(
'noversion/' => true,
'noversion/lang/' => true,
'noversion/lang/en/' => true,
@@ -134,7 +134,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingversionphp'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'legacymod/' => true,
'legacymod/version.php' => true,
'legacymod/lang/' => true,
@@ -145,7 +145,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'versionphpsyntax', '$module'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nocomponent', array(
$validator = testable_core_update_validator::instance($fixtures.'/nocomponent', array(
'baz/' => true,
'baz/version.php' => true,
'baz/lang/' => true,
@@ -156,7 +156,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingcomponent'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'foobar/' => true,
'foobar/version.php' => true,
'foobar/index.php' => true,
@@ -167,7 +167,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'componentmismatchtype',
array('expected' => 'block', 'found' => 'local')));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'foobar/' => true,
'foobar/version.php' => true,
'foobar/index.php' => true,
@@ -192,9 +192,9 @@ class tool_installaddon_validator_testcase extends basic_testcase {
}
public function test_validate_language_pack() {
$fixtures = dirname(__FILE__).'/fixtures';
$fixtures = __DIR__.'/fixtures/update_validator';
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
$validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
'bah/' => true,
'bah/index.php' => true,
'bah/view.php' => true,
@@ -204,7 +204,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfolder'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
$validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
'bah/' => true,
'bah/version.php' => true,
'bah/lang/' => true,
@@ -214,7 +214,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfile'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
$validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
'bah/' => true,
'bah/version.php' => true,
'bah/lang/' => true,
@@ -227,7 +227,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'multiplelangenfiles'));
$this->assertTrue(is_null($validator->get_language_file_name()));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/wronglang', array(
$validator = testable_core_update_validator::instance($fixtures.'/wronglang', array(
'bah/' => true,
'bah/version.php' => true,
'bah/lang/' => true,
@@ -236,10 +236,11 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$validator->assert_plugin_type('block');
$validator->assert_moodle_version(0);
$this->assertFalse($validator->execute());
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingexpectedlangenfile', 'block_bah.php'));
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingexpectedlangenfile',
'block_bah.php'));
$this->assertEquals('bah', $validator->get_language_file_name());
$validator = testable_tool_installaddon_validator::instance($fixtures.'/noversiontheme', array(
$validator = testable_core_update_validator::instance($fixtures.'/noversiontheme', array(
'noversion/' => true,
'noversion/lang/' => true,
'noversion/lang/en/' => true,
@@ -250,7 +251,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'foundlangfile', 'theme_noversion'));
$this->assertEquals('theme_noversion', $validator->get_language_file_name());
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'foobar/' => true,
'foobar/version.php' => true,
'foobar/index.php' => true,
@@ -265,9 +266,9 @@ class tool_installaddon_validator_testcase extends basic_testcase {
}
public function test_validate_target_location() {
$fixtures = dirname(__FILE__).'/fixtures';
$fixtures = __DIR__.'/fixtures/update_validator';
$validator = testable_tool_installaddon_validator::instance($fixtures.'/installed', array(
$validator = testable_core_update_validator::instance($fixtures.'/installed', array(
'greenbar/' => true,
'greenbar/version.php' => true,
'greenbar/index.php' => true,
@@ -280,7 +281,7 @@ class tool_installaddon_validator_testcase extends basic_testcase {
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'targetexists',
$validator->get_plugintype_location('local').'/greenbar'));
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
$validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
'foobar/' => true,
'foobar/version.php' => true,
'foobar/index.php' => true,
@@ -295,24 +296,48 @@ class tool_installaddon_validator_testcase extends basic_testcase {
}
public function test_parse_version_php() {
$fixtures = dirname(__FILE__).'/fixtures/versionphp';
$fixtures = __DIR__.'/fixtures/update_validator/versionphp';
$validator = testable_tool_installaddon_validator::instance($fixtures, array());
$this->assertEquals('testable_tool_installaddon_validator', get_class($validator));
$validator = testable_core_update_validator::instance($fixtures, array());
$this->assertEquals('testable_core_update_validator', get_class($validator));
$info = $validator->testable_parse_version_php($fixtures.'/version1.php');
$this->assertInternalType('array', $info);
$this->assertCount(7, $info);
$this->assertEquals('block_foobar', $info['plugin->component']); // Later in the file.
$this->assertEquals('2013010100', $info['plugin->version']); // Numeric wins over strings.
$this->assertEquals('2012122401', $info['plugin->requires']); // Commented.
$this->assertEquals('MATURITY_STABLE', $info['module->maturity']); // Constant wins regardless the order (non-PHP behaviour).
$this->assertEquals('MATURITY_ALPHA', $info['plugin->maturity']); // Constant wins regardless the order (non-PHP behaviour).
$this->assertEquals('v2.3', $info['module->release']); // String wins over numeric (non-PHP behaviour).
$this->assertEquals('v2.4', $info['plugin->release']); // String wins over numeric (non-PHP behaviour).
$this->assertEquals('block_foobar', $info['plugin->component']); // Later in the file.
$this->assertEquals('2013010100', $info['plugin->version']); // Numeric wins over strings.
$this->assertEquals('2012122401', $info['plugin->requires']); // Commented.
$this->assertEquals('MATURITY_STABLE', $info['module->maturity']);// Constant wins regardless the order (non-PHP behaviour).
$this->assertEquals('MATURITY_ALPHA', $info['plugin->maturity']); // Constant wins regardless the order (non-PHP behaviour).
$this->assertEquals('v2.3', $info['module->release']); // String wins over numeric (non-PHP behaviour).
$this->assertEquals('v2.4', $info['plugin->release']); // String wins over numeric (non-PHP behaviour).
}
// Helper methods //////////////////////////////////////////////////////////
public function test_messages_output() {
$fixtures = __DIR__.'/fixtures/update_validator';
$validator = testable_core_update_validator::instance($fixtures, array());
$this->assertDebuggingNotCalled();
$this->assertNotEmpty($validator->message_level_name($validator::ERROR));
$this->assertNotEmpty($validator->message_level_name($validator::WARNING));
$this->assertNotEmpty($validator->message_level_name($validator::INFO));
$this->assertNotEmpty($validator->message_level_name($validator::DEBUG));
$this->assertNotEmpty($validator->message_code_name('missingversion'));
$this->assertSame(
'some_really_crazy_message_code_that_is_not_localised',
$validator->message_code_name('some_really_crazy_message_code_that_is_not_localised')
);
$this->assertInstanceOf('help_icon', $validator->message_help_icon('onedir'));
$this->assertFalse($validator->message_help_icon('some_really_crazy_message_code_that_is_not_localised'));
$this->assertNotEmpty($validator->message_code_info('missingexpectedlangenfile', 'something'));
$this->assertSame('', $validator->message_code_info('missingexpectedlangenfile', null));
$this->assertSame('', $validator->message_code_info('some_really_crazy_message_code_that_is_not_localised', 'something'));
}
// Helper methods.
protected function has_message(array $messages, $level, $msgcode, $addinfo = null) {
foreach ($messages as $message) {
@@ -323,32 +348,3 @@ class tool_installaddon_validator_testcase extends basic_testcase {
return false;
}
}
/**
* Provides access to protected methods we want to explicitly test
*
* @copyright 2013 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class testable_tool_installaddon_validator extends tool_installaddon_validator {
public function testable_parse_version_php($fullpath) {
return parent::parse_version_php($fullpath);
}
public function get_plugintype_location($plugintype) {
$testableroot = make_temp_directory('testable_tool_installaddon_validator/plugintypes');
if (!is_dir($testableroot.'/'.$plugintype)) {
make_temp_directory('testable_tool_installaddon_validator/plugintypes/'.$plugintype);
}
if ($plugintype === 'local') {
// We need the following for the test_validate_target_location() method
make_temp_directory('testable_tool_installaddon_validator/plugintypes/local/greenbar');
}
return $testableroot.'/'.$plugintype;
}
}