diff options
| author | brettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-12-03 03:11:49 +0000 | 
|---|---|---|
| committer | brettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-12-03 03:11:49 +0000 | 
| commit | 4b10b550a2449827c643c896424eb0277c43b049 (patch) | |
| tree | a0b563666d98f3e32f2b502c2d6474afa3d17d86 /engine | |
| parent | 4ad5937e6077a60ecc7ee7828fb8975af7491862 (diff) | |
| download | elgg-4b10b550a2449827c643c896424eb0277c43b049.tar.gz elgg-4b10b550a2449827c643c896424eb0277c43b049.tar.bz2 | |
Refs #1986 #2170 #2225. Added semantic manifest.xml support and unit tests.  Also added plugin dependencies system. See engine/tests/test_files/plugin_18/manifest.xml for examples.  Not closing tickets pending discussion.
git-svn-id: http://code.elgg.org/elgg/trunk@7512 36083f99-b078-4883-b0ff-0f9b5a30f544
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/classes/ElggPluginManifest.php | 299 | ||||
| -rw-r--r-- | engine/classes/ElggPluginManifestParser.php | 27 | ||||
| -rw-r--r-- | engine/classes/ElggPluginManifestParser17.php | 39 | ||||
| -rw-r--r-- | engine/classes/ElggPluginManifestParser18.php | 43 | ||||
| -rw-r--r-- | engine/classes/ElggPluginPackage.php | 763 | ||||
| -rw-r--r-- | engine/lib/plugins.php | 81 | ||||
| -rw-r--r-- | engine/tests/api/plugins.php | 250 | ||||
| -rw-r--r-- | engine/tests/test_files/plugin_17/manifest.xml | 10 | ||||
| -rw-r--r-- | engine/tests/test_files/plugin_17/start.php | 0 | ||||
| -rw-r--r-- | engine/tests/test_files/plugin_18/manifest.xml | 97 | ||||
| -rw-r--r-- | engine/tests/test_files/plugin_18/start.php | 0 | 
11 files changed, 1406 insertions, 203 deletions
| diff --git a/engine/classes/ElggPluginManifest.php b/engine/classes/ElggPluginManifest.php index 395208c95..ccd0d984a 100644 --- a/engine/classes/ElggPluginManifest.php +++ b/engine/classes/ElggPluginManifest.php @@ -2,11 +2,18 @@  /**   * Parses Elgg manifest.xml files.   * + * Normalizes the values from the ElggManifestParser object. + *   * This requires an ElggPluginManifestParser class implementation   * as $this->parser.   * + * To add new parser versions, name them ElggPluginManifestParserXX + * where XX is the version specified in the top-level <plugin-manifest> + * tag. + *   * @package    Elgg.Core   * @subpackage Plugins + * @since      1.8   */  class ElggPluginManifest { @@ -16,6 +23,72 @@ class ElggPluginManifest {  	protected $parser;  	/** +	 * The expected structure of a requires element +	 */ +	private $_depsRequiresStructPlugin = array( +		'type' => '', +		'name' => '', +		'version' => '', +		'comparison' => 'ge' +	); + +	/* +	 * The expected structure of elgg and elgg_release requires element +	 */ +	private $_depsRequiresStructElgg = array( +		'type' => '', +		'version' => '', +		'comparison' => 'ge' +	); + +	/** +	 * The expected structure of a requires php_ini dependency element +	 */ +	private $_depsRequiresStructPhpIni = array( +		'type' => '', +		'name' => '', +		'value' => '', +		'comparison' => '=' +	); + +	/** +	 * The expected structure of a requires php_extension dependency element +	 */ +	private $_depsRequiresStructPhpExtension = array( +		'type' => '', +		'name' => '', +		'version' => '', +		'comparison' => '=' +	); + +	/** +	 * The expected structure of a conflicts depedency element +	 */ +	private $_depsConflictsStruct = array( +		'type' => '', +		'name' => '', +		'version' => '', +		'comparison' => '=' +	); + +	/** +	 * The expected structure of a provides dependency element. +	 */ +	private $_depsProvidesStruct = array( +		'type' => '', +		'name' => '', +		'version' => '' +	); + +	/** +	 * The expected structure of a screenshot element +	 */ +	private $_screenshotStruct = array( +		'description' => '', +		'path' => '' +	); + +	/**  	 * The API version of the manifest.  	 *  	 * @var int @@ -48,7 +121,7 @@ class ElggPluginManifest {  			if (substr(trim($manifest), 0, 1) == '<') {  				// this is a string  				$raw_xml = $manifest; -			} elseif (is_readable($manifest)) { +			} elseif (is_file($manifest)) {  				// this is a file  				$raw_xml = file_get_contents($manifest);  			} @@ -57,7 +130,8 @@ class ElggPluginManifest {  		}  		if (!$manifest_obj) { -			throw new PluginException(elgg_echo('PluginException:InvalidManifest', array($this->getPluginID()))); +			throw new PluginException(elgg_echo('PluginException:InvalidManifest', +						array($this->getPluginID())));  		}  		// set manifest api version @@ -67,19 +141,20 @@ class ElggPluginManifest {  			$this->apiVersion = 1.7;  		} -		switch ($this->apiVersion) { -			case 1.8: -				$this->parser = new ElggPluginManifestParser18($manifest_obj, $this); -				break; +		$parser_class_name = 'ElggPluginManifestParser' . str_replace('.', '', $this->apiVersion); -			case 1.7: -				$this->parser = new ElggPluginManifestParser17($manifest_obj, $this); -				break; +		// @todo currently the autoloader freaks out if a class doesn't exist. +		try { +			$class_exists = class_exists($parser_class_name); +		} catch (Exception $e) { +			$class_exists = false; +		} -			default: -				throw new PluginException(elgg_echo('PluginException:NoAvailableParser', +		if ($class_exists) { +			$this->parser = new $parser_class_name($manifest_obj, $this); +		} else { +			throw new PluginException(elgg_echo('PluginException:NoAvailableParser',  							array($this->apiVersion, $this->getPluginID()))); -				break;  		}  		if (!$this->parser->parse()) { @@ -124,35 +199,9 @@ class ElggPluginManifest {  		return $this->parser->getManifest();  	} -	/** -	 * Returns the dependencies listed. -	 * -	 * @return array -	 */ -	public function getDepends() { -		$deps = $this->parser->getAttribute('depends'); - -		if (!is_array($deps)) { -			$deps = array(); -		} - -		return $deps; -	} - -	/** -	 * Returns the conflicts listed -	 * -	 * @return array -	 */ -	public function getConflicts() { -		$conflicts = $this->parser->getAttribute('conflicts'); - -		if (!is_array($conflicts)) { -			$conflicts = array(); -		} - -		return $conflicts; -	} +	/*************************************** +	 * Parsed and Normalized Manifest Data * +	 ***************************************/  	/**  	 * Returns the plugin name @@ -163,19 +212,20 @@ class ElggPluginManifest {  		$name = $this->parser->getAttribute('name');  		if (!$name && $this->pluginID) { -			$name = ucwords(str_replace('_', ' ', $pluginID)); +			$name = ucwords(str_replace('_', ' ', $this->pluginID));  		}  		return $name;  	} +  	/**  	 * Return the description  	 *  	 * @return string  	 */  	public function getDescription() { -		return $this->parser->getAttribute('description'); +		return elgg_echo($this->parser->getAttribute('description'));  	}  	/** @@ -184,9 +234,7 @@ class ElggPluginManifest {  	 * @return string  	 */  	public function getBlurb() { -		$blurb = $this->parser->getAttribute('blurb'); - -		if (!$blurb) { +		if (!$blurb = elgg_echo($this->parser->getAttribute('blurb'))) {  			$blurb = elgg_get_excerpt($this->getDescription());  		} @@ -245,9 +293,7 @@ class ElggPluginManifest {  	 * @return array  	 */  	public function getCategories() { -		$cats = $this->parser->getAttribute('categories'); - -		if (!is_array($cats)) { +		if (!$cats = $this->parser->getAttribute('category')) {  			$cats = array();  		} @@ -260,13 +306,16 @@ class ElggPluginManifest {  	 * @return array  	 */  	public function getScreenshots() { -		$ss = $this->parser->getAttribute('screenshots'); - -		if (!is_array($ss)) { +		if (!$ss = $this->parser->getAttribute('screenshot')) {  			$ss = array();  		} -		return $ss; +		$normalized = array(); +		foreach ($ss as $s) { +			$normalized[] = $this->buildStruct($this->_screenshotStruct, $s); +		} + +		return $normalized;  	}  	/** @@ -275,13 +324,151 @@ class ElggPluginManifest {  	 * @return array  	 */  	public function getProvides() { -		$provides = $this->parser->getAttribute('provides'); +		if (!$provides = $this->parser->getAttribute('provides')) { +			$provides = array(); +		}  		// always provide ourself if we can  		if ($this->pluginID) { -			$provides[] = array('name' => $this->getPluginID(), 'version' => $this->getVersion); +			$provides[] = array( +				'type' => 'plugin', +				'name' => $this->getPluginID(), +				'version' => $this->getVersion() +			); +		} + +		$normalized = array(); +		foreach ($provides as $provide) { +			$normalized[] = $this->buildStruct($this->_depsProvidesStruct, $provide); +		} + +		return $normalized; +	} + +	/** +	 * Returns the dependencies listed. +	 * +	 * @return array +	 */ +	public function getRequires() { +		if (!$reqs = $this->parser->getAttribute('requires')) { +			$reqs = array(); +		} + +		$normalized = array(); +		foreach ($reqs as $req) { + +			switch ($req['type']) { +				case 'elgg': +				case 'elgg_release': +					$struct = $this->_depsRequiresStructElgg; +					break; + +				case 'plugin': +					$struct = $this->_depsRequiresStructPlugin; +					break; + +				case 'php_extension': +					$struct = $this->_depsRequiresStructPhpExtension; +					break; + +				case 'php_ini': +					$struct = $this->_depsRequiresStructPhpIni; + +					// also normalize boolean values +					if (isset($req['value'])) { +						switch (strtolower($normalized_req['value'])) { +							case 'yes': +							case 'true': +							case 'on': +							case 1: +								$normalized_req['value'] = 1; +								break; + +							case 'no': +							case 'false': +							case 'off': +							case 0: +							case '': +								$normalized_req['value'] = 0; +								break; +						} +					} + +					break; +			} + +			$normalized_req = $this->buildStruct($struct, $req); + +			// normalize comparison operators +			switch ($normalized_req['comparison']) { +				case '<': +					$normalized_req['comparison'] = 'lt'; +					break; + +				case '<=': +					$normalized_req['comparison'] = 'le'; +					break; + +				case '>': +					$normalized_req['comparison'] = 'gt'; +					break; + +				case '>=': +					$normalized_req['comparison'] = 'ge'; +					break; + +				case '==': +				case 'eq': +					$normalized_req['comparison'] = '='; +					break; + +				case '<>': +				case 'ne': +					$normalized_req['comparison'] = '!='; +					break; +			} + +			$normalized[] = $normalized_req; +		} + +		return $normalized; +	} + +	/** +	 * Returns the conflicts listed +	 * +	 * @return array +	 */ +	public function getConflicts() { +		if (!$conflicts = $this->parser->getAttribute('conflicts')) { +			$conflicts = array(); +		} + +		$normalized = array(); + +		foreach ($conflicts as $conflict) { +			$normalized[] = $this->buildStruct($this->_depsConflictsStruct, $conflict); +		} + +		return $normalized; +	} + +	/** +	 * Normalizes an array into the structure specified +	 * +	 * @param array $struct The struct to normalize $element to. +	 * @param array $array  The array +	 * +	 * @return array +	 */ +	protected function buildStruct(array $struct, array $array) { +		$return = array(); + +		foreach ($struct as $index => $default) { +			$return[$index] = elgg_get_array_value($index, $array, $default);  		} -		return $provides; +		return $return;  	}  }
\ No newline at end of file diff --git a/engine/classes/ElggPluginManifestParser.php b/engine/classes/ElggPluginManifestParser.php index 0ce3e3024..dce46cbb4 100644 --- a/engine/classes/ElggPluginManifestParser.php +++ b/engine/classes/ElggPluginManifestParser.php @@ -2,9 +2,24 @@  /**   * Parent class for manifest parsers.   * + * Converts manifest.xml files or strings to an array. + * + * This should be extended by a class that does the actual work + * to convert based on the manifest.xml version. + * + * This class only parses XML to an XmlEntity object and + * an array.  The array should be used primarily to extract + * information since it is quicker to parse once and store + * values from the XmlElement object than to parse the object + * each time. + * + * The array should be an exact representation of the manifest.xml + * file or string.  Any normalization needs to be done in the + * calling class / function. + *   * @package    Elgg.Core   * @subpackage Plugins - * + * @since      1.8   */  abstract class ElggPluginManifestParser {  	/** @@ -38,7 +53,7 @@ abstract class ElggPluginManifestParser {  	/**  	 * Loads the manifest XML to be parsed.  	 * -	 * @param XmlElement $xml    The Manifest XML to be parsed +	 * @param XmlElement $xml    The Manifest XML object to be parsed  	 * @param object     $caller The object calling this parser.  	 */  	public function __construct(XmlElement $xml, $caller) { @@ -71,12 +86,8 @@ abstract class ElggPluginManifestParser {  	 * @return mixed  	 */  	public function getAttribute($name) { -		if (array_key_exists($name, $this->validAttributes)) { -			if (isset($this->manifest[$name])) { -				return $this->manifest[$name]; -			} else { -				return $this->validAttributes[$name]; -			} +		if (in_array($name, $this->validAttributes) && isset($this->manifest[$name])) { +			return $this->manifest[$name];  		}  		return false; diff --git a/engine/classes/ElggPluginManifestParser17.php b/engine/classes/ElggPluginManifestParser17.php index 49b91ef52..f439b5af0 100644 --- a/engine/classes/ElggPluginManifestParser17.php +++ b/engine/classes/ElggPluginManifestParser17.php @@ -4,24 +4,18 @@   *   * @package    Elgg.Core   * @subpackage Plugins + * @since      1.8   */  class ElggPluginManifestParser17 extends ElggPluginManifestParser {  	/**  	 * The valid top level attributes and defaults for a 1.7 manifest  	 */  	protected $validAttributes = array( -		'author' => null, -		'version' => null, -		'description' => null, -		'website' => null, -		'copyright' => null, -		'license' => 'GNU Public License version 2', -		'elgg_version' => null, +		'author', 'version', 'description', 'website', +		'copyright', 'license', 'elgg_version',  		// were never really used and not enforced in code. -		'requires' => null, -		'recommends' => null, -		'conflicts' => null +		'requires', 'recommends', 'conflicts'  	);  	/** @@ -30,6 +24,10 @@ class ElggPluginManifestParser17 extends ElggPluginManifestParser {  	 * @return void  	 */  	public function parse() { +		if (!isset($this->manifestObject->children)) { +			return false; +		} +  		foreach ($this->manifestObject->children as $element) {  			$key = $element->attributes['key'];  			$value = $element->attributes['value']; @@ -47,8 +45,27 @@ class ElggPluginManifestParser17 extends ElggPluginManifestParser {  			}  		} -		$this->manifest = $elements; +		if (!$this->manifest = $elements) { +			return false; +		}  		return true;  	} + +	/** +	 * Return an attribute in the manifest. +	 * +	 * Overrides ElggPluginManifestParser::getAttribute() because before 1.8 +	 * there were no rules...weeeeeeeee! +	 * +	 * @param string $name Attribute name +	 * @return mixed +	 */ +	public function getAttribute($name) { +		if (isset($this->manifest[$name])) { +			return $this->manifest[$name]; +		} + +		return false; +	}  }
\ No newline at end of file diff --git a/engine/classes/ElggPluginManifestParser18.php b/engine/classes/ElggPluginManifestParser18.php index 1d4e9daed..54aa9603f 100644 --- a/engine/classes/ElggPluginManifestParser18.php +++ b/engine/classes/ElggPluginManifestParser18.php @@ -4,6 +4,7 @@   *   * @package    Elgg.Core   * @subpackage Plugins + * @since      1.8   */  class ElggPluginManifestParser18 extends ElggPluginManifestParser {  	/** @@ -12,23 +13,9 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  	 * @var array  	 */  	protected $validAttributes = array( -		'name' => null, -		'author' => null, -		'version' => null, -		'blurb' => null, -		'description' => null, -		'website' => null, -		'copyright' => null, -		'license' => 'GNU Public License version 2', -		'depends' => array(), -		'screenshots' => array(), -		'conflicts' => array(), -		'provides' => array(), -		'admin' => array( -			'on_enable' => null, -			'on_disable' => null, -			'interface_type' => 'advanced' -		) +		'name', 'author', 'version', 'blurb', 'description', +		'website', 'copyright', 'license', 'requires', 'screenshot', +		'category', 'conflicts', 'provides', 'admin'  	);  	/** @@ -37,7 +24,7 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  	 * @var array  	 */  	protected $requiredAttributes = array( -		'name', 'author', 'version', 'description', 'depends' +		'name', 'author', 'version', 'description', 'requires'  	);  	/** @@ -50,11 +37,8 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  		foreach ($this->manifestObject->children as $element) {  			switch ($element->name) {  				// single elements -				// translatable  				case 'blurb':  				case 'description': -					$element->content = elgg_echo($element->content); -  				case 'name':  				case 'author':  				case 'version': @@ -65,14 +49,8 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  					break;  				// arrays -				case 'screenshot': -					if (isset($element->attributes['description'])) { -						$description = elgg_echo($element->attributes['description']); -					} -					$parsed['screenshots'][] = array( -						'description' => $description, -						'path' => $element->content -					); +				case 'category': +					$parsed['category'][] = $element->content;  					break;  				case 'admin': @@ -87,9 +65,10 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  					break; +				case 'screenshot':  				case 'provides':  				case 'conflicts': -				case 'depends': +				case 'requires':  					if (!isset($element->children)) {  						return false;  					} @@ -112,7 +91,9 @@ class ElggPluginManifestParser18 extends ElggPluginManifestParser {  			}  		} -		$this->manifest = $parsed; +		if (!$this->manifest = $parsed) { +			return false; +		}  		return true;  	} diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php new file mode 100644 index 000000000..c00df7f8d --- /dev/null +++ b/engine/classes/ElggPluginPackage.php @@ -0,0 +1,763 @@ +<?php +/** + * Manages plugin packages under mod. + * + * @todo This should eventually be merged into ElggPlugin. + * Currently ElggPlugin objects are only used to get and save + * plugin settings and user settings, so not every plugin + * has an ElggPlugin object.  It's not implemented in ElggPlugin + * right now because of conflicts with at least the constructor, + * enable(), disable(), and private settings. + * + * Around 1.9 or so we should each plugin over to using + * ElggPlugin and merge ElggPluginPackage and ElggPlugin. + * + * @package    Elgg.Core + * @subpackage Plugins + * @since      1.8 + */ +class ElggPluginPackage { + +	/** +	 * The required files in the package +	 * +	 * @var array +	 */ +	private $_requiredFiles = array( +		'start.php', 'manifest.xml' +	); + +	/** +	 * Valid types for provides. +	 * +	 * @var array +	 */ +	private $_providesSupportedTypes = array( +		'plugin', 'php_extension' +	); + +	/** +	 * The type of requires/conflicts supported +	 * +	 * @var array +	 */ +	private $_depsSupportedTypes = array( +		'elgg', 'elgg_release', 'php_extension', 'php_ini', 'plugin' +	); + +	/** +	 * An invalid plugin error. +	 */ +	private $_invalidPluginError = ''; + +	/** +	 * Any dependencies messages +	 */ +	private $_depsMsgs = array(); + +	/** +	 * The plugin's manifest object +	 * +	 * @var ElggPluginManifest +	 */ +	protected $manifest; + +	/** +	 * The plugin's full path +	 * +	 * @var string +	 */ +	protected $path; + +	/** +	 * Is the plugin valid? +	 * +	 * @var mixed Bool after validation check, null before. +	 */ +	protected $valid = null; + +	/** +	 * The plugin ID (dir name) +	 * +	 * @var string +	 */ +	protected $id; + +	/** +	 * Load a plugin package from mod/$id or by full path. +	 * +	 * @param string $plugin   The ID (directory name) or full path of the plugin. +	 * @param bool   $validate Automatically run isValid()? +	 * +	 * @return true +	 * @throws PluginException +	 */ +	public function __construct($plugin, $validate = true) { +		if (substr($plugin, 0, 1) == '/') { +			// this is a path +			$plugin = sanitise_filepath($plugin); + +			if (!is_dir($plugin)) { +				throw new PluginException(elgg_echo('PluginException:InvalidPath', array($plugin))); +			} + +			// the id is the last element of the array +			$path_array = explode('/', trim($plugin, '/')); +			$this->id = array_pop($path_array); +			$this->path = $plugin; +		} else { +			// this is a plugin name + +			// strict plugin names +			if (preg_match('/[^a-z0-9\.\-_]/i', $id)) { +				throw new PluginException(elgg_echo('PluginException:InvalidID', array($plugin))); +			} + +			$this->id = $plugin; +			$this->path = get_config('pluginspath') . "$plugin/"; +		} + +		if ($validate && !$this->isValid()) { +			if ($this->_invalidPluginError) { +				throw new PluginException(elgg_echo('PluginException:InvalidPlugin:Details', +							array($plugin, $this->_invalidPluginError))); +			} else { +				throw new PluginException(elgg_echo('PluginException:InvalidPlugin', array($plugin))); +			} +		} + +		return true; +	} + +	/******************************** +	 * Validation and sanity checks * +	 ********************************/ + +	/** +	 * Checks if this is a valid Elgg plugin. +	 * +	 * Checks for requires files as defined at the start of this +	 * class.  Will check require manifest fields via ElggPluginManifest +	 * for Elgg 1.8 plugins. +	 * +	 * @note This doesn't check dependencies or conflicts. +	 * Use {@link ElggPluginPackage::canActivate()} or +	 * {@link ElggPluginPackage::checkDependencies()} for that. +	 * +	 * @return bool +	 */ +	public function isValid() { +		if (isset($this->valid)) { +			return $this->valid; +		} + +		$valid = true; + +		// check required files. +		$have_req_files = true; +		foreach ($this->_requiredFiles as $file) { +			if (!is_readable($this->path . $file)) { +				$have_req_files = false; +				$this->_invalidPluginError = +					elgg_echo('ElggPluginPackage:InvalidPlugin:MissingFile', array($file)); +				break; +			} +		} + +		// check required files +		if (!$have_req_files) { +			$valid = false; +		} + +		// check for valid manifest. +		if (!$this->_loadManifest()) { +			$valid = false; +		} + +		// can't require or conflict with yourself or something you provide. +		// make sure provides are all valid. +		if (!$this->_isSaneDeps()) { +			$valid = false; +		} + +		$this->valid = $valid; + +		return $valid; +	} + +	/** +	 * Check the plugin doesn't require or conflict with itself +	 * or something provides.  Also check that it only list +	 * valid provides.  Deps are checked in checkDependencies() +	 * +	 * @note Plugins always provide themselves. +	 * +	 * @todo Don't let them require and conflict the same thing +	 * +	 * @return bool +	 */ +	private function _isSaneDeps() { +		$conflicts = $this->getManifest()->getConflicts(); +		$requires = $this->getManifest()->getRequires(); +		$provides = $this->getManifest()->getProvides(); + +		foreach ($provides as $provide) { +			// only valid provide types +			if (!in_array($provide['type'], $this->_providesSupportedTypes)) { +				$this->_invalidPluginError = +					elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidProvides', array($provide['type'])); +				return false; +			} + +			// doesn't conflict or require any of its provides +			$name = $provide['name']; +			foreach (array('conflicts', 'requires') as $dep_type) { +				foreach (${$dep_type} as $dep) { +					if (!in_array($dep['type'], $this->_depsSupportedTypes)) { +						$this->_invalidPluginError = +							elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidDependency', array($dep['type'])); +						return false; +					} + +					// make sure nothing is providing something it conflicts or requires. +					if ($dep['name'] == $name) { +						$version_compare = version_compare($provide['version'], $dep['version'], $dep['comparison']); + +						if ($version_compare) { +							$this->_invalidPluginError = +								elgg_echo('ElggPluginPackage:InvalidPlugin:CircularDep', +									array($dep['type'], $dep['name'], $this->id)); + +							return false; +						} +					} +				} +			} +		} + +		return true; +	} + +	/** +	 * Checks if this plugin can be activated on the current +	 * Elgg installation. +	 * +	 * @return bool +	 */ +	public function canActivate() { +		return $this->checkDependencies(); +	} + + +	/************ +	 * Manifest * +	 ************/ + +	/** +	 * Returns a parsed manifest file. +	 * +	 * @return ElggPluginManifest +	 */ +	public function getManifest() { +		if (!$this->manifest) { +			$this->_loadManifest(); +		} + +		return $this->manifest; +	} + +	/** +	 * Loads the manifest into this->manifest as an +	 * ElggPluginManifest object. +	 * +	 * @return bool +	 */ +	private function _loadManifest() { +		$file = $this->path . 'manifest.xml'; +		if ($this->manifest = new ElggPluginManifest($file, $this->id)) { +			return true; +		} + +		return false; +	} + + +	/*********************** +	 * Dependencies system * +	 ***********************/ + +	/** +	 * Returns if the Elgg system meets the plugin's dependency +	 * requirements.  This includes both requires and conflicts. +	 * +	 * Full reports can be requested.  The results are returned +	 * as an array of arrays in the form array( +	 * 	'type' => requires|conflicts, +	 * 	'dep' => array( dependency array ), +	 * 	'status' => bool if depedency is met, +	 * 	'comment' => optional comment to display to the user. +	 * ) +	 * +	 * @param bool $full_report Return a full report. +	 * @return bool|array +	 */ +	public function checkDependencies($full_report = false) { +		$requires = $this->getManifest()->getRequires(); +		$conflicts = $this->getManifest()->getConflicts(); +		$enabled_plugins = get_installed_plugins('enabled'); +		$report = array(); + +		foreach (array('requires', 'conflicts') as $dep_type) { +			$inverse = ($dep_type == 'conflicts') ? true : false; + +			foreach (${$dep_type} as $dep) { +				switch ($dep['type']) { +					case 'elgg': +						$result = $this->_checkDepElgg($dep, get_version()); +						break; + +					case 'elgg_release': +						$result = $this->_checkDepElgg($dep, get_version(true)); +						break; + +					case 'plugin': +						$result = $this->_checkDepPlugin($dep, $enabled_plugins, $inverse); +						break; + +					case 'php_extension': +						$result = $this->_checkDepPhpExtension($dep); +						break; + +					case 'php_ini': +						$result = $this->_checkDepPhpIni($dep); +						break; +				} + +				// unless we're doing a full report, break as soon as we fail. +				if (!$full_report && !$result) { +					return $result; +				} else { +					// build report element and comment +					if ($dep_type == 'requires') { +						$comment = ''; +					} elseif ($dep_type == 'conflicts') { +						$comment = ''; +					} + +					$report[] = array( +						'type' => $dep_type, +						'dep' => $dep, +						'status' => $result, +						'comment' => $comment +					); +				} +			} +		} + +		if ($full_report) { +			return $report; +		} + +		return true; +	} + +	/** +	 * Checks if $plugins meets the requirement by $dep. +	 * +	 * @param array $dep     An Elgg manifest.xml deps array +	 * @param array $plugins A list of plugins as returned by get_installed_plugins(); +	 * @param bool  $inverse Inverse the results to use as a conflicts. +	 * @return bool +	 */ +	private function _checkDepPlugin(array $dep, array $plugins, $inverse = false) { +		$r = elgg_check_plugins_provides('plugin', $dep['name'], $dep['version'], $dep['comparison']); + +		if ($inverse) { +			$r = !$r; +		} + +		return $r; +	} + +	/** +	 * Checks if $elgg_version meets the requirement by $dep. +	 * +	 * @param array $dep          An Elgg manifest.xml deps array +	 * @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z) +	 * @param bool  $inverse      Inverse the result to use as a conflicts. +	 * @return bool +	 */ +	private function _checkDepElgg(array $dep, $elgg_version, $inverse = false) { +		$r = version_compare($elgg_version, $dep['version'], $dep['comparison']); + +		if ($inverse) { +			$r = !$r; +		} + +		return $r; +	} + +	/** +	 * Checks if the PHP extension in $dep is loaded. +	 * +	 * @todo Can this be merged with the plugin checker? +	 * +	 * @param array $dep An Elgg manifest.xml deps array +	 * @return bool +	 */ +	private function _checkDepPhpExtension(array $dep) { +		$name = $dep['name']; +		$version = $dep['version']; +		$comparison = $dep['comparison']; + +		// not enabled. +		$r = extension_loaded($name); + +		// enabled. check version. +		$ext_version = phpversion($name); + +		if ($version && !version_compare($ext_version, $version, $comparison)) { +			$r = false; +		} + +		// some php extensions can be emulated, so check provides. +		if ($r == false) { +			$r = elgg_check_plugins_provides('php_extension', $name, $version, $comparison); +		} + +		return $r; +	} + +	/** +	 * Check if the PHP ini setting satisfies $dep. +	 * +	 * @param array $dep An Elgg manifest.xml deps array +	 * @return bool +	 */ +	private function _checkDepPhpIni($dep) { +		$name = $dep['name']; +		$value = $dep['value']; +		$comparison = $dep['comparison']; + +		// ini_get() normalizes truthy values to 1 but falsey values to 0 or ''. +		// version_compare() considers '' < 0, so normalize '' to 0. +		// ElggPluginManifest normalizes all bool values and '' to 1 or 0. +		$setting = ini_get($name); + +		if ($setting === '') { +			$setting = 0; +		} + +		$r = version_compare($setting, $value, $comparison); + +		return $r; +	} + + +	/************************************** +	 * Detailed reports for requirements. * +	 **************************************/ + + +	/** +	 * Returns a report of the dependencies with human +	 * readable statuses. +	 * +	 * @return array +	 */ +	public function getDependenciesReport() { +		$requires = $this->getManifest()->getRequires(); +		$conflicts = $this->getManifest()->getConflicts(); +		$enabled_plugins = get_installed_plugins('enabled'); + +		$status = true; +		$messages = array(); + +		$return = array( +			array( +				'type' => 'requires', +				'dep' => $dep, +				'status' => 'bool', +				'comment' => '' +			) +		); + +		foreach ($requires as $require) { +			switch ($require['type']) { +				case 'elgg': +					$result = $this->_checkRequiresElgg($require, get_version()); +					break; + +				case 'elgg_release': +					$result = $this->_checkRequiresElgg($require, get_version(true)); +					break; + +				case 'plugin': +					$result = $this->_checkDepsPlugin($require, $enabled_plugins); +					break; + +				case 'php_extension': +					$result = $this->_checkRequiresPhpExtension($require); +					break; + +				case 'php_ini': +					$result = $this->_checkRequiresPhpIni($require); +					break; + +				default: +					$result = array( +						'status' => false, +						'message' => elgg_echo('ElggPluginPackage:UnknownDep', +										array($require['type'], $this->getManifest()->getPluginID())) +					); +					break; +			} + +			if (!$result['status']) { +				$status = false; +				$messages[] = $result['message']; +			} +		} + +		foreach ($conflicts as $conflict) { + +		} + +		$return = array( +			'status' => $status, +			'messages' => $messages +		); + +		return $return; +	} + +	/** +	 * Checks if $plugins meets the requirement by $require. +	 * +	 * Returns an array in the form array('status' => bool, 'message' => 'Any messages') +	 * +	 * @param array $require An Elgg manifest.xml requires array +	 * @param array $plugins A list of plugins as returned by get_installed_plugins(); +	 * @return array +	 */ +	private function _checkRequiresPlugin(array $require, array $plugins = array()) { +		$status = true; +		$message = ''; + +		$name = $require['name']; +		$version = $require['version']; +		$comparison = $require['comparison']; + +		// not enabled. +		if (!array_key_exists($name, $plugins)) { +			$status = false; + +			if ($version) { +				$message = elgg_echo("ElggPluginPackage:Requires:Plugin:NotEnabled:$comparison", +							array($this->getManifest()->getPluginID(), $name, $version)); +			} else { +				$message = elgg_echo('ElggPluginPackage:Requires:Plugin:NotEnabled:NoVersion', +							array($this->getManifest()->getPluginID(), $name)); +			} +		} + +		// enabled. check version. +		if ($status != false) { +			$requires_plugin_info = $plugins[$name]; + +			//@todo boot strapping until we can migrate everything over to ElggPluginPackage. +			$plugin_package = new ElggPluginPackage($name); +			$plugin_version = $plugin_package->getManifest()->getVersion(); + +			if ($version && !version_compare($plugin_version, $version, $comparison)) { +				$status = false; + +				$message = elgg_echo("ElggPluginPackage:Requires:Plugin:$comparison", +								array($this->getManifest()->getPluginID(), $name, $version, $plugin_version)); +			} +		} + +		// if all else fails check with the provides +		if ($status == false) { +			if (elgg_check_plugins_provides('plugin', $name)) { +				// it's provided. check version if asked. +				$status = true; +				$message = ''; + +				if ($version && !elgg_check_plugins_provides('plugin', $name, $version, $comparison)) { +						// change the message to something more meaningful +						$provide = elgg_get_plugins_provides('plugin', $name); +						$plugin_version = "{$provide['provided_by']}:$name={$provide['version']}"; + +						$status = false; +						$message = elgg_echo("ElggPluginPackage:Requires:Plugin:$comparison", +								array($this->getManifest()->getPluginID(), $name, $version, $plugin_version)); +				} +			} +		} + +		return array( +			'status' => $status, +			'message' => $message +		); +	} + +	/** +	 * Checks if $elgg_version meets the requirement by $require. +	 * +	 * Returns an array in the form array('status' => bool, 'message' => 'Any messages') +	 * +	 * @param array $require      An Elgg manifest.xml requires array +	 * @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z) +	 * @return array +	 */ +	private function _checkRequiresElgg(array $require, $elgg_version) { +		$status = true; +		$message = ''; +		$version = $require['version']; +		$comparison = $require['comparison']; + +		if (!version_compare($elgg_version, $version, $comparison)) { +			$status = false; +			$message = elgg_echo("ElggPluginPackage:Requires:Elgg:$comparison", +							array($this->getManifest()->getPluginID(), $version)); +		} + +		return array( +			'status' => $status, +			'message' => $message +		); +	} + +	/** +	 * Checks if the PHP extension in $require is loaded. +	 * +	 * @todo Can this be merged with the plugin checker? +	 * +	 * @param array $require An Elgg manifest.xml deps array +	 * @return array +	 */ +	private function _checkRequiresPhpExtension($require) { +		$status = true; +		$message = ''; + +		$name = $require['name']; +		$version = $require['version']; +		$comparison = $require['comparison']; + +		// not enabled. +		if (!extension_loaded($name)) { +			$status = false; +			if ($version) { +				$message = elgg_echo("ElggPluginPackage:Requires:PhpExtension:NotInstalled:$comparison", +							array($this->getManifest()->getPluginID(), $name, $version)); +			} else { +				$message = elgg_echo('ElggPluginPackage:Requires:PhpExtension:NotInstalled:NoVersion', +							array($this->getManifest()->getPluginID(), $name)); +			} +		} + +		// enabled. check version. +		if ($status != false) { +			$ext_version = phpversion($name); + +			if ($version && !version_compare($ext_version, $version, $comparison)) { +				$status = false; +				$message = elgg_echo("ElggPluginPackage:Requires:PhpExtension:$comparison", +								array($this->getManifest()->getPluginID(), $name, $version)); +			} +		} + +		// some php extensions can be emulated, so check provides. +		if ($status == false) { +			if (elgg_check_plugins_provides('php_extension', $name)) { +				// it's provided. check version if asked. +				$status = true; +				$message = ''; + +				if ($version && !elgg_check_plugins_provides('php_extension', $name, $version, $comparison)) { +						// change the message to something more meaningful +						$provide = elgg_get_plugins_provides('php_extension', $name); +						$plugin_version = "{$provide['provided_by']}:$name={$provide['version']}"; + +						$status = false; +						$message = elgg_echo("ElggPluginPackage:Requires:PhpExtension:$comparison", +								array($this->getManifest()->getPluginID(), $name, $version, $plugin_version)); +				} +			} +		} + +		return array( +			'status' => $status, +			'message' => $message +		); +	} + + +	/** +	 * Check if the PHP ini setting satisfies $require. +	 * +	 * @param array $require An Elgg manifest.xml requires array +	 * @return array +	 */ +	private function _checkRequiresPhpIni($require) { +		$status = true; +		$message = ''; + +		$name = $require['name']; +		$value = $require['value']; +		$comparison = $require['comparison']; + +		// ini_get() normalizes truthy values to 1 but falsey values to 0 or ''. +		// version_compare() considers '' < 0, so normalize '' to 0. +		// ElggPluginManifest normalizes all bool values and '' to 1 or 0. +		$setting = ini_get($name); + +		if ($setting === '') { +			$setting = 0; +		} + +		if (!version_compare($setting, $value, $comparison)) { +			$status = false; +			$message = elgg_echo("ElggPluginPackage:Requires:PhpIni:$comparison", +					array($this->getManifest()->getPluginID(), $name, $value, $setting)); +		} + +		return array( +			'status' => $status, +			'message' => $message +		); +	} + +	/** +	 * Activate the plugin. +	 * +	 * @note This method is activate() to avoid clashing with ElggEntity::enable() +	 * +	 * @return bool +	 */ +	public function activate() { +		return enable_plugin($this->getID()); +	} + +	/** +	 * Deactivate the plugin. +	 * +	 * @note This method is deactivate() to avoid clashing with ElggEntity::disable() +	 * +	 * @return bool +	 */ +	public function deactivate() { +		return disable_plugin($this->getID()); +	} + +	/** +	 * Returns the Plugin ID +	 * +	 * @return string +	 */ +	public function getID() { +		return $this->id; +	} + +}
\ No newline at end of file diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 7624b1768..1c91a7776 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -306,6 +306,87 @@ function check_plugin_compatibility($manifest_elgg_version_string) {  }  /** + * Returns an array of all provides from all active plugins. + * + * Array in the form array( + * 	'provide_type' => array( + * 		'provided_name' => array( + * 			'version' => '1.8', + * 			'provided_by' => 'provider_plugin_id' + *  	) + *  ) + * ) + * + * @param string $type The type of provides to return + * @param string $name A specific provided name to return. Requires $provide_type. + * + * @return array + */ +function elgg_get_plugins_provides($type = null, $name = null) { +	static $provides = null; +	$active_plugins = get_installed_plugins('enabled'); + +	if (!isset($provides)) { +		$provides = array(); + +		foreach ($active_plugins as $plugin_id => $plugin_info) { +			// @todo remove this when fully converted to ElggPluginPackage. +			$package = new ElggPluginPackage($plugin_id); + +			if ($plugin_provides = $package->getManifest()->getProvides()) { +				foreach ($plugin_provides as $provided) { +					$provides[$provided['type']][$provided['name']] = array( +						'version' => $provided['version'], +						'provided_by' => $plugin_id +					); +				} +			} +		} +	} + +	if ($type && $name) { +		if (isset($provides[$type][$name])) { +			return $provides[$type][$name]; +		} else { +			return false; +		} +	} elseif ($type) { +		if (isset($provides[$type])) { +			return $provides[$type]; +		} else { +			return false; +		} +	} + +	return $provides; +} + +/** + * Checks if a plugin is currently providing $type and $name, and optionally + * checking a version. + * + * @param string $type       The type of the provide + * @param string $name       The name of the provide + * @param string $version    A version to check against + * @param string $comparison The comparison operator to use in version_compare() + * + * @return bool + */ +function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') { +	if (!$provided = elgg_get_plugins_provides($type, $name)) { +		return false; +	} + +	if ($provided) { +		if ($version) { +			return version_compare($provided['version'], $version, $comparison); +		} else { +			return true; +		} +	} +} + +/**   * Shorthand function for finding the plugin settings.   *   * @param string $plugin_name Optional plugin name, if not specified diff --git a/engine/tests/api/plugins.php b/engine/tests/api/plugins.php index 66e87c91e..61e2cdde6 100644 --- a/engine/tests/api/plugins.php +++ b/engine/tests/api/plugins.php @@ -2,83 +2,30 @@  /**   * Elgg Plugins Test   * - * @package Elgg - * @subpackage Test + * @package Elgg.Core + * @subpackage Plugins.Test   */  class ElggCorePluginsAPITest extends ElggCoreUnitTest { -	var $manifest_file_18 = <<<___END -<?xml version="1.0" encoding="UTF-8"?> -<plugin_manifest version="1.8"> -	<name>Test Manifest</name> -	<author>Anyone</author> -	<version>1.0</version> -	<blurb>A concise description.</blurb> -	<description>A longer, more interesting description.</description> -	<website>http://www.elgg.org/</website> -	<copyright>(C) Elgg 2010</copyright> -	<license>GNU Public License version 2</license> -	<depends> -		<type>elgg</type> -		<value>2009030802</value> -	</depends> - -	<screenshot description="Fun things to do 1">graphics/plugin_ss1.png</screenshot> -	<screenshot description="Fun things to do 2">graphics/plugin_ss2.png</screenshot> - -	<admin> -	<on_enable>setup_function</on_enable> -		<on_disable>teardown_function</on_disable> -		<interface_type>simple</interface_type> -	</admin> - -	<depends> -		<type>php_extension</type> -		<value>gd</value> -	</depends> - -	<depends> -		<type>php_ini</type> -		<name>safe_mode</name> -		<value>off</value> -	</depends> - -	<conflicts> -		<type>plugin</type> -		<value>profile</value> -	</conflicts> - -	<provides> -		<name>profile_api</name> -		<version>1.3</version> -	</provides> - -</plugin_manifest> -___END; -  	// 1.8 manifest object  	var $manifest18; -	var $manifest_file_17 = <<<___END -<?xml version="1.0" encoding="UTF-8"?> -<plugin_manifest> -	<field key="author" value="Anyone" /> -	<field key="version" value="1.0" /> -	<field key="description" value="A 1.7-style manifest" /> -	<field key="website" value="http://www.elgg.org/" /> -	<field key="copyright" value="(C) Elgg2008-2009" /> -	<field key="license" value="GNU Public License version 2" /> -	<field key="elgg_version" value="2009030702" /> -</plugin_manifest> -___END; +	// 1.8 package at test_files/plugin_18/ +	var $package18;  	// 1.7 manifest object  	var $manifest17; +	// 1.7 package at test_files/plugin_17/ +	var $package17; +  	public function __construct() {  		parent::__construct(); -		$this->manifest18 = new ElggPluginManifest($this->manifest_file_18, 'unit_test'); -		$this->manifest17 = new ElggPluginManifest($this->manifest_file_17); +		$this->manifest18 = new ElggPluginManifest(get_config('path') . 'engine/tests/test_files/plugin_18/manifest.xml', 'plugin_test_18'); +		$this->manifest17 = new ElggPluginManifest(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml', 'plugin_test_17'); + +		$this->package18 = new ElggPluginPackage(get_config('path') . 'engine/tests/test_files/plugin_18'); +		$this->package17 = new ElggPluginPackage(get_config('path') . 'engine/tests/test_files/plugin_17');  	}  	/** @@ -89,38 +36,30 @@ ___END;  		$this->swallowErrors();  	} -  	// generic tests  	public function testElggPluginManifestFromString() { -		$manifest = new ElggPluginManifest($this->manifest_file_17); +		$manifest_file = file_get_contents(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml'); +		$manifest = new ElggPluginManifest($manifest_file);  		$this->assertIsA($manifest, 'ElggPluginManifest');  	}  	public function testElggPluginManifestFromFile() { -		$file = get_config('dataroot') . '/manifest_test.xml'; -		$fp = fopen($file, 'wb'); -		fputs($fp, $this->manifest_file_17); -		fclose($fp); - +		$file = get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml';  		$manifest = new ElggPluginManifest($file);  		$this->assertIsA($manifest, 'ElggPluginManifest'); - -		unlink($file);  	} -	public function testElggPluginManifestFromXML() { -		$xml = xml_to_object($this->manifest_file_17); +	public function testElggPluginManifestFromXMLEntity() { +		$xml = xml_to_object($manifest_file = file_get_contents(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml'));  		$manifest = new ElggPluginManifest($xml);  		$this->assertIsA($manifest, 'ElggPluginManifest');  	} - - +	// exact manifest values  	// 1.8 interface -  	public function testElggPluginManifest18() {  		$manifest_array = array(  			'name' => 'Test Manifest', @@ -132,23 +71,33 @@ ___END;  			'copyright' => '(C) Elgg 2010',  			'license' => 'GNU Public License version 2', -			'depends' => array( -				array('type' => 'elgg', 'value' => '2009030802'), -				array('type' => 'php_extension', 'value' => 'gd'), -				array('type' => 'php_ini', 'name' => 'safe_mode', 'value' => 'off'), +			'requires' => array( +				array('type' => 'elgg', 'version' => '3009030802', 'comparison' => 'lt'), +				array('type' => 'elgg_release', 'version' => '1.8-svn'), +				array('type' => 'php_extension', 'name' => 'gd'), +				array('type' => 'php_ini', 'name' => 'short_open_tag', 'value' => 'off'), +				array('type' => 'php_extension', 'name' => 'made_up', 'version' => '1.0'), +				array('type' => 'plugin', 'name' => 'fake_plugin', 'version' => '1.0'), +				array('type' => 'plugin', 'name' => 'profile', 'version' => '1.0'), +				array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3', 'comparison' => 'lt'),  			), -			'screenshots' => array( +			'screenshot' => array(  				array('description' => 'Fun things to do 1', 'path' => 'graphics/plugin_ss1.png'),  				array('description' => 'Fun things to do 2', 'path' => 'graphics/plugin_ss2.png'),  			), +			'category' => array( +				'Admin', 'ServiceAPI' +			), +  			'conflicts' => array( -				array('type' => 'plugin', 'value' => 'profile') +				array('type' => 'plugin', 'name' => 'profile_api', 'version' => 1.0)  			),  			'provides' => array( -				array('name' => 'profile_api', 'version' => 1.3) +				array('type' => 'plugin', 'name' => 'profile_api', 'version' => 1.3), +				array('type' => 'php_extension', 'name' => 'big_math', 'version' => 1.0)  			),  			'admin' => array( @@ -161,53 +110,160 @@ ___END;  		$this->assertEqual($this->manifest18->getManifest(), $manifest_array);  	} +	public function testElggPluginManifest17() { +		$manifest_array = array( +			'author' => 'Anyone', +			'version' => '1.0', +			'description' => 'A 1.7-style manifest.', +			'website' => 'http://www.elgg.org/', +			'copyright' => '(C) Elgg 2010', +			'license' => 'GNU Public License version 2', +			'elgg_version' => '2009030702' +		); + +		$this->assertEqual($this->manifest17->getManifest(), $manifest_array); +	} + +  	public function testElggPluginManifestGetApiVersion() {  		$this->assertEqual($this->manifest18->getApiVersion(), 1.8); +		$this->assertEqual($this->manifest17->getApiVersion(), 1.7);  	} +	public function testElggPluginManifestGetPluginID() { +		$this->assertEqual($this->manifest18->getPluginID(), 'plugin_test_18'); +		$this->assertEqual($this->manifest17->getPluginID(), 'plugin_test_17'); +	} + + +	// normalized attributes  	public function testElggPluginManifestGetName() {  		$this->assertEqual($this->manifest18->getName(), 'Test Manifest'); +		$this->assertEqual($this->manifest17->getName(), 'Plugin Test 17');  	}  	public function testElggPluginManifestGetAuthor() {  		$this->assertEqual($this->manifest18->getAuthor(), 'Anyone'); +		$this->assertEqual($this->manifest17->getAuthor(), 'Anyone');  	}  	public function testElggPluginManifestGetVersion() {  		$this->assertEqual($this->manifest18->getVersion(), 1.0); +		$this->assertEqual($this->manifest17->getVersion(), 1.0);  	}  	public function testElggPluginManifestGetBlurb() {  		$this->assertEqual($this->manifest18->getBlurb(), 'A concise description.'); +		$this->assertEqual($this->manifest17->getBlurb(), 'A 1.7-style manifest.');  	}  	public function testElggPluginManifestGetWebsite() {  		$this->assertEqual($this->manifest18->getWebsite(), 'http://www.elgg.org/'); +		$this->assertEqual($this->manifest17->getWebsite(), 'http://www.elgg.org/');  	}  	public function testElggPluginManifestGetCopyright() {  		$this->assertEqual($this->manifest18->getCopyright(), '(C) Elgg 2010'); +		$this->assertEqual($this->manifest18->getCopyright(), '(C) Elgg 2010');  	}  	public function testElggPluginManifestGetLicense() {  		$this->assertEqual($this->manifest18->getLicense(), 'GNU Public License version 2'); +		$this->assertEqual($this->manifest17->getLicense(), 'GNU Public License version 2');  	} -	// 1.7 interface +	public function testElggPluginManifestGetRequires() { +		$requires = array( +			array('type' => 'elgg', 'version' => '3009030802', 'comparison' => 'lt'), +			array('type' => 'elgg_release', 'version' => '1.8-svn', 'comparison' => 'ge'), +			array('type' => 'php_extension', 'name' => 'gd', 'version' => '', 'comparison' => '='), +			array('type' => 'php_ini', 'name' => 'short_open_tag', 'value' => 'off', 'comparison' => '='), +			array('type' => 'php_extension', 'name' => 'made_up', 'version' => '1.0', 'comparison' => '='), +			array('type' => 'plugin', 'name' => 'fake_plugin', 'version' => '1.0', 'comparison' => 'ge'), +			array('type' => 'plugin', 'name' => 'profile', 'version' => '1.0', 'comparison' => 'ge'), +			array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3', 'comparison' => 'lt'), +		); -	public function testElggPluginManifest17() { -		$manifest_array = array( -			'author' => 'Anyone', -			'version' => '1.0', -			'description' => 'A 1.7-style manifest', -			'website' => 'http://www.elgg.org/', -			'copyright' => '(C) Elgg2008-2009', -			'license' => 'GNU Public License version 2', -			'elgg_version' => '2009030702' +		$this->assertEqual($this->package18->getManifest()->getRequires(), $requires); + +		$this->assertEqual($this->package17->getManifest()->getRequires(), array()); +	} + +	public function testElggPluginManifestGetDescription() { +		$this->assertEqual($this->package18->getManifest()->getDescription(), 'A longer, more interesting description.'); +		$this->assertEqual($this->package17->getManifest()->getDescription(), 'A 1.7-style manifest.'); +	} + +	public function testElggPluginManifestGetDescriptionTranslated() { +		$en = array( +			$this->package18->getManifest()->getDescription() => 'A translated 1.8 description!', +			$this->package17->getManifest()->getDescription() => 'A translated 1.7 description!',  		); -		$this->assertEqual($this->manifest17->getManifest(), $manifest_array); +		add_translation('en', $en); + +		$this->assertEqual($this->package18->getManifest()->getDescription(), 'A translated 1.8 description!'); +		$this->assertEqual($this->package17->getManifest()->getDescription(), 'A translated 1.7 description!'); +	} + +	public function testElggPluginManifestGetCategories() { +		$categories = array( +			'Admin', 'ServiceAPI' +		); + +		$this->assertEqual($this->package18->getManifest()->getCategories(), $categories); +		$this->assertEqual($this->package17->getManifest()->getCategories(), array()); +	} + +	public function testElggPluginManifestGetScreenshots() { +		$screenshots = array( +			array('description' => 'Fun things to do 1', 'path' => 'graphics/plugin_ss1.png'), +			array('description' => 'Fun things to do 2', 'path' => 'graphics/plugin_ss2.png'), +		); + +		$this->assertEqual($this->package18->getManifest()->getScreenshots(), $screenshots); +		$this->assertEqual($this->package17->getManifest()->getScreenshots(), array()); +	} + +	public function testElggPluginManifestGetProvides() { +		$provides = array( +			array('type' => 'plugin', 'name' => 'profile_api', 'version' => 1.3), +			array('type' => 'php_extension', 'name' => 'big_math', 'version' => 1.0), +			array('type' => 'plugin', 'name' => 'plugin_18', 'version' => 1.0) +		); + +		$this->assertEqual($this->package18->getManifest()->getProvides(), $provides); + + +		$provides = array( +			array('type' => 'plugin', 'name' => 'plugin_17', 'version' => '1.0') +		); + +		$this->assertEqual($this->package17->getManifest()->getProvides(), $provides);  	} +	public function testElggPluginManifestGetConflicts() { +		$conflicts = array( +			array( +				'type' => 'plugin', +				'name' => 'profile_api', +				'version' => '1.0', +				'comparison' => '=' +			) +		); + +		$this->assertEqual($this->manifest18->getConflicts(), $conflicts); +		$this->assertEqual($this->manifest17->getConflicts(), array()); +	} + +	// ElggPluginPackage +	public function testElggPluginPackageDetectIDFromPath() { +		$this->assertEqual($this->package18->getID(), 'plugin_18'); +	} + +	public function testElggPluginPackageDetectIDFromPluginID() { +		$package = new ElggPluginPackage('profile'); +		$this->assertEqual($package->getID(), 'profile'); +	}  } diff --git a/engine/tests/test_files/plugin_17/manifest.xml b/engine/tests/test_files/plugin_17/manifest.xml new file mode 100644 index 000000000..bb178ab93 --- /dev/null +++ b/engine/tests/test_files/plugin_17/manifest.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<plugin_manifest> +	<field key="author" value="Anyone" /> +	<field key="version" value="1.0" /> +	<field key="description" value="A 1.7-style manifest." /> +	<field key="website" value="http://www.elgg.org/" /> +	<field key="copyright" value="(C) Elgg 2010" /> +	<field key="license" value="GNU Public License version 2" /> +	<field key="elgg_version" value="2009030702" /> +</plugin_manifest>
\ No newline at end of file diff --git a/engine/tests/test_files/plugin_17/start.php b/engine/tests/test_files/plugin_17/start.php new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/engine/tests/test_files/plugin_17/start.php diff --git a/engine/tests/test_files/plugin_18/manifest.xml b/engine/tests/test_files/plugin_18/manifest.xml new file mode 100644 index 000000000..182117a50 --- /dev/null +++ b/engine/tests/test_files/plugin_18/manifest.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<plugin_manifest version="1.8"> +	<name>Test Manifest</name> +	<author>Anyone</author> +	<version>1.0</version> +	<blurb>A concise description.</blurb> +	<description>A longer, more interesting description.</description> +	<website>http://www.elgg.org/</website> +	<copyright>(C) Elgg 2010</copyright> +	<license>GNU Public License version 2</license> + +	<requires> +		<type>elgg</type> +		<version>3009030802</version> +		<comparison>lt</comparison> +	</requires> + +	<requires> +		<type>elgg_release</type> +		<version>1.8-svn</version> +	</requires> + +	<screenshot> +		<description>Fun things to do 1</description> +		<path>graphics/plugin_ss1.png</path> +	</screenshot> + +	<screenshot> +		<description>Fun things to do 2</description> +		<path>graphics/plugin_ss2.png</path> +	</screenshot> + +	<category>Admin</category> + +	<category>ServiceAPI</category> + +	<admin> +		<on_enable>setup_function</on_enable> +		<on_disable>teardown_function</on_disable> +		<interface_type>simple</interface_type> +	</admin> + +	<requires> +		<type>php_extension</type> +		<name>gd</name> +	</requires> + +	<requires> +		<type>php_ini</type> +		<name>short_open_tag</name> +		<value>off</value> +	</requires> + +	<requires> +		<type>php_extension</type> +		<name>made_up</name> +		<version>1.0</version> +	</requires> + +	<requires> +		<type>plugin</type> +		<name>fake_plugin</name> +		<version>1.0</version> +	</requires> + +	<requires> +		<type>plugin</type> +		<name>profile</name> +		<version>1.0</version> +	</requires> + +	<requires> +		<type>plugin</type> +		<name>profile_api</name> +		<version>1.3</version> +		<comparison>lt</comparison> +	</requires> + +	<conflicts> +		<type>plugin</type> +		<name>profile_api</name> +		<version>1.0</version> +	</conflicts> + +	<provides> +		<type>plugin</type> +		<name>profile_api</name> +		<version>1.3</version> +	</provides> + +	<provides> +		<type>php_extension</type> +		<name>big_math</name> +		<version>1.0</version> +	</provides> + +</plugin_manifest>
\ No newline at end of file diff --git a/engine/tests/test_files/plugin_18/start.php b/engine/tests/test_files/plugin_18/start.php new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/engine/tests/test_files/plugin_18/start.php | 
