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:
@@ -1,570 +0,0 @@
|
||||
<?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 validation class to check the plugin ZIP contents
|
||||
*
|
||||
* 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 <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if (!defined('T_ML_COMMENT')) {
|
||||
define('T_ML_COMMENT', T_COMMENT);
|
||||
} else {
|
||||
define('T_DOC_COMMENT', T_ML_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the contents of extracted plugin ZIP file
|
||||
*
|
||||
* @copyright 2013 David Mudrak <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_installaddon_validator {
|
||||
|
||||
/** Critical error message level, causes the validation fail. */
|
||||
const ERROR = 'error';
|
||||
|
||||
/** Warning message level, validation does not fail but the admin should be always informed. */
|
||||
const WARNING = 'warning';
|
||||
|
||||
/** Information message level that the admin should be aware of. */
|
||||
const INFO = 'info';
|
||||
|
||||
/** Debugging message level, should be displayed in debugging mode only. */
|
||||
const DEBUG = 'debug';
|
||||
|
||||
/** @var string full path to the extracted ZIP contents */
|
||||
protected $extractdir = null;
|
||||
|
||||
/** @var array as returned by {@link zip_packer::extract_to_pathname()} */
|
||||
protected $extractfiles = null;
|
||||
|
||||
/** @var bool overall result of validation */
|
||||
protected $result = null;
|
||||
|
||||
/** @var string the name of the plugin root directory */
|
||||
protected $rootdir = null;
|
||||
|
||||
/** @var array explicit list of expected/required characteristics of the ZIP */
|
||||
protected $assertions = null;
|
||||
|
||||
/** @var array of validation log messages */
|
||||
protected $messages = array();
|
||||
|
||||
/** @var array|null array of relevant data obtained from version.php */
|
||||
protected $versionphp = null;
|
||||
|
||||
/** @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
|
||||
*/
|
||||
public static function instance($zipcontentpath, array $zipcontentfiles) {
|
||||
return new static($zipcontentpath, $zipcontentfiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expected plugin type, fail the validation otherwise
|
||||
*
|
||||
* @param string $required plugin type
|
||||
*/
|
||||
public function assert_plugin_type($required) {
|
||||
$this->assertions['plugintype'] = $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expectation that the plugin can be installed into the given Moodle version
|
||||
*
|
||||
* @param string $required Moodle version we are about to install to
|
||||
*/
|
||||
public function assert_moodle_version($required) {
|
||||
$this->assertions['moodleversion'] = $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the validation process against all explicit and implicit requirements
|
||||
*
|
||||
* Returns true if the validation passes (all explicit and implicit requirements
|
||||
* pass) and the plugin can be installed. Returns false if the validation fails
|
||||
* (some explicit or implicit requirement fails) and the plugin must not be
|
||||
* installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function execute() {
|
||||
|
||||
$this->result = (
|
||||
$this->validate_files_layout()
|
||||
and $this->validate_version_php()
|
||||
and $this->validate_language_pack()
|
||||
and $this->validate_target_location()
|
||||
);
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns overall result of the validation.
|
||||
*
|
||||
* Null is returned if the validation has not been executed yet. Otherwise
|
||||
* this method returns true (the installation can continue) or false (it is not
|
||||
* safe to continue with the installation).
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function get_result() {
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of validation log messages
|
||||
*
|
||||
* Each validation message is a plain object with properties level, msgcode
|
||||
* and addinfo.
|
||||
*
|
||||
* @return array of (int)index => (stdClass) validation message
|
||||
*/
|
||||
public function get_messages() {
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
public function get_versionphp_info() {
|
||||
return $this->versionphp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the English language file without the .php extension
|
||||
*
|
||||
* This can be used as a suggestion for fixing the plugin root directory in the
|
||||
* ZIP file during the upload. If no file was found, or multiple PHP files are
|
||||
* located in lang/en/ folder, then null is returned.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_language_file_name() {
|
||||
return $this->langfilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rootdir of the extracted package (after eventual renaming)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_rootdir() {
|
||||
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 /////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @param string $zipcontentpath full path to the extracted ZIP contents
|
||||
* @param array $zipcontentfiles (string)filerelpath => (bool|string)true or error
|
||||
*/
|
||||
protected function __construct($zipcontentpath, array $zipcontentfiles) {
|
||||
$this->extractdir = $zipcontentpath;
|
||||
$this->extractfiles = $zipcontentfiles;
|
||||
}
|
||||
|
||||
// Validation methods //////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @return bool false if files in the ZIP do not have required layout
|
||||
*/
|
||||
protected function validate_files_layout() {
|
||||
|
||||
if (!is_array($this->extractfiles) or count($this->extractfiles) < 4) {
|
||||
// We need the English language pack with the name of the plugin at least
|
||||
$this->add_message(self::ERROR, 'filesnumber');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->extractfiles as $filerelname => $filestatus) {
|
||||
if ($filestatus !== true) {
|
||||
$this->add_message(self::ERROR, 'filestatus', array('file' => $filerelname, 'status' => $filestatus));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($this->extractfiles) as $filerelname) {
|
||||
if (!file_exists($this->extractdir.'/'.$filerelname)) {
|
||||
$this->add_message(self::ERROR, 'filenotexists', array('file' => $filerelname));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($this->extractfiles) as $filerelname) {
|
||||
$matches = array();
|
||||
if (!preg_match("#^([^/]+)/#", $filerelname, $matches) or (!is_null($this->rootdir) and $this->rootdir !== $matches[1])) {
|
||||
$this->add_message(self::ERROR, 'onedir');
|
||||
return false;
|
||||
}
|
||||
$this->rootdir = $matches[1];
|
||||
}
|
||||
|
||||
if ($this->rootdir !== clean_param($this->rootdir, PARAM_PLUGIN)) {
|
||||
$this->add_message(self::ERROR, 'rootdirinvalid', $this->rootdir);
|
||||
return false;
|
||||
} else {
|
||||
$this->add_message(self::INFO, 'rootdir', $this->rootdir);
|
||||
}
|
||||
|
||||
return is_dir($this->extractdir.'/'.$this->rootdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool false if the version.php file does not declare required information
|
||||
*/
|
||||
protected function validate_version_php() {
|
||||
|
||||
if (!isset($this->assertions['plugintype'])) {
|
||||
throw new coding_exception('Required plugin type must be set before calling this');
|
||||
}
|
||||
|
||||
if (!isset($this->assertions['moodleversion'])) {
|
||||
throw new coding_exception('Required Moodle version must be set before calling this');
|
||||
}
|
||||
|
||||
$fullpath = $this->extractdir.'/'.$this->rootdir.'/version.php';
|
||||
|
||||
if (!file_exists($fullpath)) {
|
||||
// This is tolerated for themes only.
|
||||
if ($this->assertions['plugintype'] === 'theme') {
|
||||
$this->add_message(self::DEBUG, 'missingversionphp');
|
||||
return true;
|
||||
} else {
|
||||
$this->add_message(self::ERROR, 'missingversionphp');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->versionphp = array();
|
||||
$info = $this->parse_version_php($fullpath);
|
||||
|
||||
if (isset($info['module->version'])) {
|
||||
$this->add_message(self::ERROR, 'versionphpsyntax', '$module');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($info['plugin->version'])) {
|
||||
$this->versionphp['version'] = $info['plugin->version'];
|
||||
$this->add_message(self::INFO, 'pluginversion', $this->versionphp['version']);
|
||||
} else {
|
||||
$this->add_message(self::ERROR, 'missingversion');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($info['plugin->requires'])) {
|
||||
$this->versionphp['requires'] = $info['plugin->requires'];
|
||||
if ($this->versionphp['requires'] > $this->assertions['moodleversion']) {
|
||||
$this->add_message(self::ERROR, 'requiresmoodle', $this->versionphp['requires']);
|
||||
return false;
|
||||
}
|
||||
$this->add_message(self::INFO, 'requiresmoodle', $this->versionphp['requires']);
|
||||
}
|
||||
|
||||
if (!isset($info['plugin->component'])) {
|
||||
$this->add_message(self::ERROR, 'missingcomponent');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->versionphp['component'] = $info['plugin->component'];
|
||||
list($reqtype, $reqname) = core_component::normalize_component($this->versionphp['component']);
|
||||
if ($reqtype !== $this->assertions['plugintype']) {
|
||||
$this->add_message(self::ERROR, 'componentmismatchtype', array(
|
||||
'expected' => $this->assertions['plugintype'],
|
||||
'found' => $reqtype));
|
||||
return false;
|
||||
}
|
||||
if ($reqname !== $this->rootdir) {
|
||||
$this->add_message(self::ERROR, 'componentmismatchname', $reqname);
|
||||
return false;
|
||||
}
|
||||
$this->add_message(self::INFO, 'componentmatch', $this->versionphp['component']);
|
||||
|
||||
if (isset($info['plugin->maturity'])) {
|
||||
$this->versionphp['maturity'] = $info['plugin->maturity'];
|
||||
if ($this->versionphp['maturity'] === 'MATURITY_STABLE') {
|
||||
$this->add_message(self::INFO, 'maturity', $this->versionphp['maturity']);
|
||||
} else {
|
||||
$this->add_message(self::WARNING, 'maturity', $this->versionphp['maturity']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($info['plugin->release'])) {
|
||||
$this->versionphp['release'] = $info['plugin->release'];
|
||||
$this->add_message(self::INFO, 'release', $this->versionphp['release']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool false if the English language pack is not provided correctly
|
||||
*/
|
||||
protected function validate_language_pack() {
|
||||
|
||||
if (!isset($this->assertions['plugintype'])) {
|
||||
throw new coding_exception('Required plugin type must be set before calling this');
|
||||
}
|
||||
|
||||
if (!isset($this->extractfiles[$this->rootdir.'/lang/en/'])
|
||||
or $this->extractfiles[$this->rootdir.'/lang/en/'] !== true
|
||||
or !is_dir($this->extractdir.'/'.$this->rootdir.'/lang/en')) {
|
||||
$this->add_message(self::ERROR, 'missinglangenfolder');
|
||||
return false;
|
||||
}
|
||||
|
||||
$langfiles = array();
|
||||
foreach (array_keys($this->extractfiles) as $extractfile) {
|
||||
$matches = array();
|
||||
if (preg_match('#^'.preg_quote($this->rootdir).'/lang/en/([^/]+).php?$#i', $extractfile, $matches)) {
|
||||
$langfiles[] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($langfiles)) {
|
||||
$this->add_message(self::ERROR, 'missinglangenfile');
|
||||
return false;
|
||||
} else if (count($langfiles) > 1) {
|
||||
$this->add_message(self::WARNING, 'multiplelangenfiles');
|
||||
} else {
|
||||
$this->langfilename = $langfiles[0];
|
||||
$this->add_message(self::DEBUG, 'foundlangfile', $this->langfilename);
|
||||
}
|
||||
|
||||
if ($this->assertions['plugintype'] === 'mod') {
|
||||
$expected = $this->rootdir.'.php';
|
||||
} else {
|
||||
$expected = $this->assertions['plugintype'].'_'.$this->rootdir.'.php';
|
||||
}
|
||||
|
||||
if (!isset($this->extractfiles[$this->rootdir.'/lang/en/'.$expected])
|
||||
or $this->extractfiles[$this->rootdir.'/lang/en/'.$expected] !== true
|
||||
or !is_file($this->extractdir.'/'.$this->rootdir.'/lang/en/'.$expected)) {
|
||||
$this->add_message(self::ERROR, 'missingexpectedlangenfile', $expected);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool false of the given add-on can't be installed into its location
|
||||
*/
|
||||
public function validate_target_location() {
|
||||
|
||||
if (!isset($this->assertions['plugintype'])) {
|
||||
throw new coding_exception('Required plugin type must be set before calling this');
|
||||
}
|
||||
|
||||
$plugintypepath = $this->get_plugintype_location($this->assertions['plugintype']);
|
||||
|
||||
if (is_null($plugintypepath)) {
|
||||
$this->add_message(self::ERROR, 'unknowntype', $this->assertions['plugintype']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_dir($plugintypepath)) {
|
||||
throw new coding_exception('Plugin type location does not exist!');
|
||||
}
|
||||
|
||||
$target = $plugintypepath.'/'.$this->rootdir;
|
||||
|
||||
if (file_exists($target)) {
|
||||
$this->add_message(self::ERROR, 'targetexists', $target);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_writable($plugintypepath)) {
|
||||
$this->add_message(self::INFO, 'pathwritable', $plugintypepath);
|
||||
} else {
|
||||
$this->add_message(self::ERROR, 'pathwritable', $plugintypepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper methods //////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get as much information from existing version.php as possible
|
||||
*
|
||||
* @param string full path to the version.php file
|
||||
* @return array of found meta-info declarations
|
||||
*/
|
||||
protected function parse_version_php($fullpath) {
|
||||
|
||||
$content = $this->get_stripped_file_contents($fullpath);
|
||||
|
||||
preg_match_all('#\$((plugin|module)\->(version|maturity|release|requires))=()(\d+(\.\d+)?);#m', $content, $matches1);
|
||||
preg_match_all('#\$((plugin|module)\->(maturity))=()(MATURITY_\w+);#m', $content, $matches2);
|
||||
preg_match_all('#\$((plugin|module)\->(release))=([\'"])(.*?)\4;#m', $content, $matches3);
|
||||
preg_match_all('#\$((plugin|module)\->(component))=([\'"])(.+?_.+?)\4;#m', $content, $matches4);
|
||||
|
||||
if (count($matches1[1]) + count($matches2[1]) + count($matches3[1]) + count($matches4[1])) {
|
||||
$info = array_combine(
|
||||
array_merge($matches1[1], $matches2[1], $matches3[1], $matches4[1]),
|
||||
array_merge($matches1[5], $matches2[5], $matches3[5], $matches4[5])
|
||||
);
|
||||
|
||||
} else {
|
||||
$info = array();
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given message to the messages log
|
||||
*
|
||||
* @param string $level e.g. self::ERROR
|
||||
* @param string $msgcode may form a string
|
||||
* @param string|array|object $a optional additional info suitable for {@link get_string()}
|
||||
*/
|
||||
protected function add_message($level, $msgcode, $a = null) {
|
||||
$msg = (object)array(
|
||||
'level' => $level,
|
||||
'msgcode' => $msgcode,
|
||||
'addinfo' => $a,
|
||||
);
|
||||
$this->messages[] = $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bare PHP code from the given file
|
||||
*
|
||||
* Returns contents without PHP opening and closing tags, text outside php code,
|
||||
* comments and extra whitespaces.
|
||||
*
|
||||
* @param string $fullpath full path to the file
|
||||
* @return string
|
||||
*/
|
||||
protected function get_stripped_file_contents($fullpath) {
|
||||
|
||||
$source = file_get_contents($fullpath);
|
||||
$tokens = token_get_all($source);
|
||||
$output = '';
|
||||
$doprocess = false;
|
||||
foreach ($tokens as $token) {
|
||||
if (is_string($token)) {
|
||||
// Simple one character token.
|
||||
$id = -1;
|
||||
$text = $token;
|
||||
} else {
|
||||
// Token array.
|
||||
list($id, $text) = $token;
|
||||
}
|
||||
switch ($id) {
|
||||
case T_WHITESPACE:
|
||||
case T_COMMENT:
|
||||
case T_ML_COMMENT:
|
||||
case T_DOC_COMMENT:
|
||||
// Ignore whitespaces, inline comments, multiline comments and docblocks.
|
||||
break;
|
||||
case T_OPEN_TAG:
|
||||
// Start processing.
|
||||
$doprocess = true;
|
||||
break;
|
||||
case T_CLOSE_TAG:
|
||||
// Stop processing.
|
||||
$doprocess = false;
|
||||
break;
|
||||
default:
|
||||
// Anything else is within PHP tags, return it as is.
|
||||
if ($doprocess) {
|
||||
$output .= $text;
|
||||
if ($text === 'function') {
|
||||
// Explicitly keep the whitespace that would be ignored.
|
||||
$output .= ' ';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the full path to the root directory of the given plugin type
|
||||
*
|
||||
* @param string $plugintype
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_plugintype_location($plugintype) {
|
||||
|
||||
$plugintypepath = null;
|
||||
|
||||
foreach (core_component::get_plugin_types() as $type => $fullpath) {
|
||||
if ($type === $plugintype) {
|
||||
$plugintypepath = $fullpath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $plugintypepath;
|
||||
}
|
||||
}
|
||||
@@ -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!';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Plugin must have more than one file.
|
||||
-1
@@ -1 +0,0 @@
|
||||
<?php
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->component = 'repository_mahara';
|
||||
$plugin->version = 2014010100;
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
echo 'One, my little hobbit, never installs malicisous add-ons';
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginname'] = 'Foo bar!';
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->version = 2013031900;
|
||||
$plugin->component = 'local_greenbar';
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->component = 'local_one';
|
||||
@@ -1 +0,0 @@
|
||||
Only one dir is allowed
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginname'] = 'This is a plugin with $plugin->component missing in its version.php';
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->version = 2015080600;
|
||||
$plugin->release = 'B.A.Z. Auth fake plugin';
|
||||
//$plugin->component is missing here so the validation must fail.
|
||||
@@ -1 +0,0 @@
|
||||
<?php
|
||||
@@ -1 +0,0 @@
|
||||
<?php
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginnname'] = 'Root directory mismatch';
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->version = 2014122455;
|
||||
$plugin->component = 'mod_bah';
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
echo 'Do not use hardcoded strings, provide the language pack';
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginversion'] = 'Activity module with no version.php';
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginversion'] = 'A theme with no version.php';
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
// index.php
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginname'] = 'Foo!';
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php // $Id$
|
||||
|
||||
// I don't miss CVS, do you?
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
echo 'One, my little hobbit, never installs malicisous add-ons';
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginname'] = 'Foo bar!';
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->version = 2013031900;
|
||||
$plugin->component = 'local_foobar';
|
||||
$plugin->requires = 2013031200;
|
||||
$plugin->maturity = MATURITY_ALPHA;
|
||||
//$plugin->release = 'And this is commented';
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['modulename'] = 'Legacy activity module with $module in version.php';
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Support for the $module has been dropped in Moodle 3.0.
|
||||
$module->version = 2013031900;
|
||||
@@ -1,52 +0,0 @@
|
||||
<h1>Example version.php file</h1>
|
||||
|
||||
<p>version.php is required for all plugins but themes.</p>
|
||||
|
||||
<h2>Example of values</h2>
|
||||
|
||||
<pre>
|
||||
$plugin->version = 2011051000;
|
||||
$plugin->requires = 2010112400;
|
||||
$plugin->cron = 0;
|
||||
$plugin->component = 'plugintype_pluginname';
|
||||
$plugin->maturity = MATURITY_STABLE;
|
||||
$plugin->release = '2.x (Build: 2011051000)';
|
||||
$plugin->dependencies = array('mod_forum' => ANY_VERSION, 'mod_data' => 2010020300);
|
||||
</pre>
|
||||
|
||||
Replace $plugin with $module for activity modules, as in
|
||||
|
||||
<pre>
|
||||
$module->version = 2012122400;
|
||||
</pre><?php // $Id$ $module->version = 1;
|
||||
|
||||
$plugin->component
|
||||
= 'old_foobar';//$plugin->component='commented';
|
||||
|
||||
$plugin->component =
|
||||
'block_foobar';
|
||||
|
||||
$plugin->version = 2013010100;
|
||||
////////$plugin->version = 0;
|
||||
/* for activity
|
||||
modules use:
|
||||
$module->version = 2014131300;
|
||||
|
||||
***/
|
||||
$plugin->version = "2010091855"; // Do not use quotes here.
|
||||
$plugin->version = '2010091856.9'; // Do not use quotes here.
|
||||
|
||||
|
||||
$plugin->requires = /* 2012010100 */ 2012122401 ;
|
||||
|
||||
$module->maturity = MATURITY_STABLE;
|
||||
$module->maturity = 50; // If both present, the constant wins (on contrary to what PHP would do)
|
||||
$module->maturity = 'MATURITY_BETA'; // Do not use quotes here.
|
||||
|
||||
$plugin->maturity = 10;
|
||||
$plugin->maturity = MATURITY_ALPHA;
|
||||
|
||||
|
||||
|
||||
$module->release = 2.3; $plugin->release = 'v2.4';
|
||||
$module->release = "v2.3"; $plugin->release = 2.4;
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$string['pluginname'] = 'This would be valid filename for module, not a block';
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
$plugin->version = 2014122455;
|
||||
$plugin->component = 'block_bah';
|
||||
Binary file not shown.
Binary file not shown.
@@ -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));
|
||||
}
|
||||
|
||||
@@ -1,354 +0,0 @@
|
||||
<?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 the unit tests class and some helper classes
|
||||
*
|
||||
* @package tool_installaddon
|
||||
* @category test
|
||||
* @copyright 2013 David Mudrak <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link tool_installaddon_installer} class
|
||||
*
|
||||
* @copyright 2013 David Mudrak <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_installaddon_validator_testcase extends basic_testcase {
|
||||
|
||||
public function test_validate_files_layout() {
|
||||
$fixtures = dirname(__FILE__).'/fixtures';
|
||||
|
||||
// Non-existing directory.
|
||||
$validator = testable_tool_installaddon_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->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(
|
||||
'foobar/' => true,
|
||||
'foobar/version.php' => true,
|
||||
'foobar/index.php' => true,
|
||||
'foobar/lang/' => true,
|
||||
'foobar/lang/en/' => true,
|
||||
'foobar/lang/en/local_foobar.php' => true,
|
||||
'foobar/NOTEXISTS.txt' => true));
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR,
|
||||
'filenotexists', array('file' => 'foobar/NOTEXISTS.txt')));
|
||||
|
||||
// Errors during ZIP extraction
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/multidir', array(
|
||||
'one/' => true,
|
||||
'one/version.php' => 'Can not write target file',
|
||||
'two/' => true,
|
||||
'two/README.txt' => true));
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'filestatus',
|
||||
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(
|
||||
'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(
|
||||
'version.php' => true,
|
||||
'index.php' => true,
|
||||
'lang/' => true,
|
||||
'lang/en/' => true,
|
||||
'lang/en/foo.php' => true));
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
|
||||
|
||||
// Multiple directories
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/multidir', array(
|
||||
'one/' => true,
|
||||
'one/version.php' => true,
|
||||
'two/' => true,
|
||||
'two/README.txt' => true));
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
|
||||
|
||||
// Invalid root directory name
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/github', array(
|
||||
'moodle-repository_mahara-master/' => true,
|
||||
'moodle-repository_mahara-master/lang/' => true,
|
||||
'moodle-repository_mahara-master/lang/en/' => true,
|
||||
'moodle-repository_mahara-master/lang/en/repository_mahara.php' => true,
|
||||
'moodle-repository_mahara-master/version.php' => true));
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'rootdirinvalid',
|
||||
'moodle-repository_mahara-master'));
|
||||
}
|
||||
|
||||
public function test_validate_version_php() {
|
||||
$fixtures = dirname(__FILE__).'/fixtures';
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/noversiontheme', array(
|
||||
'noversion/' => true,
|
||||
'noversion/lang/' => true,
|
||||
'noversion/lang/en/' => true,
|
||||
'noversion/lang/en/theme_noversion.php' => true));
|
||||
$validator->assert_plugin_type('theme');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertTrue($validator->execute());
|
||||
$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(
|
||||
'noversion/' => true,
|
||||
'noversion/lang/' => true,
|
||||
'noversion/lang/en/' => true,
|
||||
'noversion/lang/en/noversion.php' => true));
|
||||
$validator->assert_plugin_type('mod');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingversionphp'));
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
|
||||
'legacymod/' => true,
|
||||
'legacymod/version.php' => true,
|
||||
'legacymod/lang/' => true,
|
||||
'legacymod/lang/en/' => true,
|
||||
'legacymod/lang/en/legacymod.php' => true));
|
||||
$validator->assert_plugin_type('mod');
|
||||
$validator->assert_moodle_version(0);
|
||||
$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(
|
||||
'baz/' => true,
|
||||
'baz/version.php' => true,
|
||||
'baz/lang/' => true,
|
||||
'baz/lang/en/' => true,
|
||||
'baz/lang/en/auth_baz.php' => true));
|
||||
$validator->assert_plugin_type('auth');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingcomponent'));
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/plugindir', array(
|
||||
'foobar/' => true,
|
||||
'foobar/version.php' => true,
|
||||
'foobar/index.php' => true,
|
||||
'foobar/lang/' => true));
|
||||
$validator->assert_plugin_type('block');
|
||||
$validator->assert_moodle_version('2013031400.00');
|
||||
$this->assertFalse($validator->execute());
|
||||
$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(
|
||||
'foobar/' => true,
|
||||
'foobar/version.php' => true,
|
||||
'foobar/index.php' => true,
|
||||
'foobar/lang/' => true,
|
||||
'foobar/lang/en/' => true,
|
||||
'foobar/lang/en/local_foobar.php' => true));
|
||||
$validator->assert_plugin_type('local');
|
||||
$validator->assert_moodle_version('2013031400.00');
|
||||
$this->assertTrue($validator->execute());
|
||||
$this->assertTrue($validator->get_result());
|
||||
$this->assertEquals('foobar', $validator->get_rootdir());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'rootdir', 'foobar'));
|
||||
$versionphpinfo = $validator->get_versionphp_info();
|
||||
$this->assertInternalType('array', $versionphpinfo);
|
||||
$this->assertCount(4, $versionphpinfo);
|
||||
$this->assertEquals(2013031900, $versionphpinfo['version']);
|
||||
$this->assertEquals(2013031200, $versionphpinfo['requires']);
|
||||
$this->assertEquals('local_foobar', $versionphpinfo['component']);
|
||||
$this->assertEquals('MATURITY_ALPHA', $versionphpinfo['maturity']); // Note we get the constant name here.
|
||||
$this->assertEquals(MATURITY_ALPHA, constant($versionphpinfo['maturity'])); // This is how to get the real value.
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'maturity', 'MATURITY_ALPHA'));
|
||||
}
|
||||
|
||||
public function test_validate_language_pack() {
|
||||
$fixtures = dirname(__FILE__).'/fixtures';
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
|
||||
'bah/' => true,
|
||||
'bah/index.php' => true,
|
||||
'bah/view.php' => true,
|
||||
'bah/version.php' => true));
|
||||
$validator->assert_plugin_type('mod');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfolder'));
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
|
||||
'bah/' => true,
|
||||
'bah/version.php' => true,
|
||||
'bah/lang/' => true,
|
||||
'bah/lang/en/' => true));
|
||||
$validator->assert_plugin_type('mod');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertFalse($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfile'));
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/nolang', array(
|
||||
'bah/' => true,
|
||||
'bah/version.php' => true,
|
||||
'bah/lang/' => true,
|
||||
'bah/lang/en/' => true,
|
||||
'bah/lang/en/bleh.php' => true,
|
||||
'bah/lang/en/bah.php' => true));
|
||||
$validator->assert_plugin_type('mod');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertTrue($validator->execute());
|
||||
$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(
|
||||
'bah/' => true,
|
||||
'bah/version.php' => true,
|
||||
'bah/lang/' => true,
|
||||
'bah/lang/en/' => true,
|
||||
'bah/lang/en/bah.php' => true));
|
||||
$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->assertEquals('bah', $validator->get_language_file_name());
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/noversiontheme', array(
|
||||
'noversion/' => true,
|
||||
'noversion/lang/' => true,
|
||||
'noversion/lang/en/' => true,
|
||||
'noversion/lang/en/theme_noversion.php' => true));
|
||||
$validator->assert_plugin_type('theme');
|
||||
$validator->assert_moodle_version(0);
|
||||
$this->assertTrue($validator->execute());
|
||||
$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(
|
||||
'foobar/' => true,
|
||||
'foobar/version.php' => true,
|
||||
'foobar/index.php' => true,
|
||||
'foobar/lang/' => true,
|
||||
'foobar/lang/en/' => true,
|
||||
'foobar/lang/en/local_foobar.php' => true));
|
||||
$validator->assert_plugin_type('local');
|
||||
$validator->assert_moodle_version('2013031400.00');
|
||||
$this->assertTrue($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'foundlangfile', 'local_foobar'));
|
||||
$this->assertEquals('local_foobar', $validator->get_language_file_name());
|
||||
}
|
||||
|
||||
public function test_validate_target_location() {
|
||||
$fixtures = dirname(__FILE__).'/fixtures';
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures.'/installed', array(
|
||||
'greenbar/' => true,
|
||||
'greenbar/version.php' => true,
|
||||
'greenbar/index.php' => true,
|
||||
'greenbar/lang/' => true,
|
||||
'greenbar/lang/en/' => true,
|
||||
'greenbar/lang/en/local_greenbar.php' => true));
|
||||
$validator->assert_plugin_type('local');
|
||||
$validator->assert_moodle_version('2013031400.00');
|
||||
$this->assertFalse($validator->execute());
|
||||
$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(
|
||||
'foobar/' => true,
|
||||
'foobar/version.php' => true,
|
||||
'foobar/index.php' => true,
|
||||
'foobar/lang/' => true,
|
||||
'foobar/lang/en/' => true,
|
||||
'foobar/lang/en/local_foobar.php' => true));
|
||||
$validator->assert_plugin_type('local');
|
||||
$validator->assert_moodle_version('2013031400.00');
|
||||
$this->assertTrue($validator->execute());
|
||||
$this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'pathwritable',
|
||||
$validator->get_plugintype_location('local')));
|
||||
}
|
||||
|
||||
public function test_parse_version_php() {
|
||||
$fixtures = dirname(__FILE__).'/fixtures/versionphp';
|
||||
|
||||
$validator = testable_tool_installaddon_validator::instance($fixtures, array());
|
||||
$this->assertEquals('testable_tool_installaddon_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).
|
||||
}
|
||||
|
||||
// Helper methods //////////////////////////////////////////////////////////
|
||||
|
||||
protected function has_message(array $messages, $level, $msgcode, $addinfo = null) {
|
||||
foreach ($messages as $message) {
|
||||
if ($message->level === $level and $message->msgcode === $msgcode and $message->addinfo === $addinfo) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides access to protected methods we want to explicitly test
|
||||
*
|
||||
* @copyright 2013 David Mudrak <[email protected]>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user