diff options
Diffstat (limited to 'engine/classes/ElggPluginManifest.php')
| -rw-r--r-- | engine/classes/ElggPluginManifest.php | 656 | 
1 files changed, 656 insertions, 0 deletions
| diff --git a/engine/classes/ElggPluginManifest.php b/engine/classes/ElggPluginManifest.php new file mode 100644 index 000000000..6912c2b08 --- /dev/null +++ b/engine/classes/ElggPluginManifest.php @@ -0,0 +1,656 @@ +<?php +/** + * 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's XML namespace. + * + * @package    Elgg.Core + * @subpackage Plugins + * @since      1.8 + */ +class ElggPluginManifest { + +	/** +	 * The parser object +	 */ +	protected $parser; + +	/** +	 * The root for plugin manifest namespaces. +	 * This is in the format http://www.elgg.org/plugin_manifest/<version> +	 */ +	protected $namespace_root = 'http://www.elgg.org/plugin_manifest/'; + +	/** +	 * The expected structure of a plugins requires element +	 */ +	private $depsStructPlugin = array( +		'type' => '', +		'name' => '', +		'version' => '', +		'comparison' => 'ge' +	); + +	/** +	 * The expected structure of a priority element +	 */ +	private $depsStructPriority = array( +		'type' => '', +		'priority' => '', +		'plugin' => '' +	); + +	/* +	 * The expected structure of elgg_version and elgg_release requires element +	 */ +	private $depsStructElgg = array( +		'type' => '', +		'version' => '', +		'comparison' => 'ge' +	); + +	/** +	 * The expected structure of a requires php_ini dependency element +	 */ +	private $depsStructPhpIni = array( +		'type' => '', +		'name' => '', +		'value' => '', +		'comparison' => '=' +	); + +	/** +	 * The expected structure of a requires php_extension dependency element +	 */ +	private $depsStructPhpExtension = 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 +	 */ +	protected $apiVersion; + +	/** +	 * The optional plugin id this manifest belongs to. +	 * +	 * @var string +	 */ +	protected $pluginID; + +	/** +	 * Load a manifest file, XmlElement or path to manifest.xml file +	 * +	 * @param mixed  $manifest  A string, XmlElement, or path of a manifest file. +	 * @param string $plugin_id Optional ID of the owning plugin. Used to +	 *                          fill in some values automatically. +	 */ +	public function __construct($manifest, $plugin_id = null) { +		if ($plugin_id) { +			$this->pluginID = $plugin_id; +		} + +		// see if we need to construct the xml object. +		if ($manifest instanceof ElggXMLElement) { +			$manifest_obj = $manifest; +		} else { +			if (substr(trim($manifest), 0, 1) == '<') { +				// this is a string +				$raw_xml = $manifest; +			} elseif (is_file($manifest)) { +				// this is a file +				$raw_xml = file_get_contents($manifest); +			} + +			$manifest_obj = xml_to_object($raw_xml); +		} + +		if (!$manifest_obj) { +			throw new PluginException(elgg_echo('PluginException:InvalidManifest', +						array($this->getPluginID()))); +		} + +		// set manifest api version +		if (isset($manifest_obj->attributes['xmlns'])) { +			$namespace = $manifest_obj->attributes['xmlns']; +			$version = str_replace($this->namespace_root, '', $namespace); +		} else { +			$version = 1.7; +		} + +		$this->apiVersion = $version; + +		$parser_class_name = 'ElggPluginManifestParser' . str_replace('.', '', $this->apiVersion); + +		// @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; +		} + +		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()))); +		} + +		if (!$this->parser->parse()) { +			throw new PluginException(elgg_echo('PluginException:ParserError', +						array($this->apiVersion, $this->getPluginID()))); +		} + +		return true; +	} + +	/** +	 * Returns the API version in use. +	 * +	 * @return int +	 */ +	public function getApiVersion() { +		return $this->apiVersion; +	} + +	/** +	 * Returns the plugin ID. +	 * +	 * @return string +	 */ +	public function getPluginID() { +		if ($this->pluginID) { +			return $this->pluginID; +		} else { +			return elgg_echo('unknown'); +		} +	} + +	/** +	 * Returns the manifest array. +	 * +	 * Used for backward compatibility.  Specific +	 * methods should be called instead. +	 * +	 * @return array +	 */ +	public function getManifest() { +		return $this->parser->getManifest(); +	} + +	/*************************************** +	 * Parsed and Normalized Manifest Data * +	 ***************************************/ + +	/** +	 * Returns the plugin name +	 * +	 * @return string +	 */ +	public function getName() { +		$name = $this->parser->getAttribute('name'); + +		if (!$name && $this->pluginID) { +			$name = ucwords(str_replace('_', ' ', $this->pluginID)); +		} + +		return $name; +	} + + +	/** +	 * Return the description +	 * +	 * @return string +	 */ +	public function getDescription() { +		return $this->parser->getAttribute('description'); +	} + +	/** +	 * Return the short description +	 * +	 * @return string +	 */ +	public function getBlurb() { +		$blurb = $this->parser->getAttribute('blurb'); + +		if (!$blurb) { +			$blurb = elgg_get_excerpt($this->getDescription()); +		} + +		return $blurb; +	} + +	/** +	 * Returns the license +	 * +	 * @return string +	 */ +	public function getLicense() { +		// license vs licence.  Use license. +		$en_us = $this->parser->getAttribute('license'); +		if ($en_us) { +			return $en_us; +		} else { +			return $this->parser->getAttribute('licence'); +		} +	} + +	/** +	 * Returns the repository url +	 * +	 * @return string +	 */ +	public function getRepositoryURL() { +		return $this->parser->getAttribute('repository'); +	} + +	/** +	 * Returns the bug tracker page +	 * +	 * @return string +	 */ +	public function getBugTrackerURL() { +		return $this->parser->getAttribute('bugtracker'); +	} + +	/** +	 * Returns the donations page +	 * +	 * @return string +	 */ +	public function getDonationsPageURL() { +		return $this->parser->getAttribute('donations'); +	} + +	/** +	 * Returns the version of the plugin. +	 * +	 * @return float +	 */ +	public function getVersion() { +		return $this->parser->getAttribute('version'); +	} + +	/** +	 * Returns the plugin author. +	 * +	 * @return string +	 */ +	public function getAuthor() { +		return $this->parser->getAttribute('author'); +	} + +	/** +	 * Return the copyright +	 * +	 * @return string +	 */ +	public function getCopyright() { +		return $this->parser->getAttribute('copyright'); +	} + +	/** +	 * Return the website +	 * +	 * @return string +	 */ +	public function getWebsite() { +		return $this->parser->getAttribute('website'); +	} + +	/** +	 * Return the categories listed for this plugin +	 * +	 * @return array +	 */ +	public function getCategories() { +		$bundled_plugins = array('blog', 'bookmarks', 'categories', +			'custom_index', 'dashboard', 'developers', 'diagnostics', +			'embed', 'externalpages', 'file', 'garbagecollector', +			'groups', 'htmlawed', 'invitefriends', 'likes', +			'logbrowser', 'logrotate', 'members', 'messageboard', +			'messages', 'notifications', 'oauth_api', 'pages', 'profile', +			'reportedcontent', 'search', 'tagcloud', 'thewire', 'tinymce', +			'twitter', 'twitter_api', 'uservalidationbyemail', 'zaudio', +		); + +		$cats = $this->parser->getAttribute('category'); + +		if (!$cats) { +			$cats = array(); +		} + +		if (in_array('bundled', $cats) && !in_array($this->getPluginID(), $bundled_plugins)) { +			unset($cats[array_search('bundled', $cats)]); +		} + +		return $cats; +	} + +	/** +	 * Return the screenshots listed. +	 * +	 * @return array +	 */ +	public function getScreenshots() { +		$ss = $this->parser->getAttribute('screenshot'); + +		if (!$ss) { +			$ss = array(); +		} + +		$normalized = array(); +		foreach ($ss as $s) { +			$normalized[] = $this->buildStruct($this->screenshotStruct, $s); +		} + +		return $normalized; +	} + +	/** +	 * Return the list of provides by this plugin. +	 * +	 * @return array +	 */ +	public function getProvides() { +		// normalize for 1.7 +		if ($this->getApiVersion() < 1.8) { +			$provides = array(); +		} else { +			$provides = $this->parser->getAttribute('provides'); +		} + +		if (!$provides) { +			$provides = array(); +		} + +		// always provide ourself if we can +		if ($this->pluginID) { +			$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() { +		// rewrite the 1.7 style elgg_version as a real requires. +		if ($this->apiVersion < 1.8) { +			$elgg_version = $this->parser->getAttribute('elgg_version'); +			if ($elgg_version) { +				$reqs = array( +					array( +						'type' => 'elgg_version', +						'version' => $elgg_version, +						'comparison' => 'ge' +					) +				); +			} else { +				$reqs = array(); +			} +		} else { +			$reqs = $this->parser->getAttribute('requires'); +		} + +		if (!$reqs) { +			$reqs = array(); +		} + +		$normalized = array(); +		foreach ($reqs as $req) { +			$normalized[] = $this->normalizeDep($req); +		} + +		return $normalized; +	} + +	/** +	 * Returns the suggests elements. +	 * +	 * @return array +	 */ +	public function getSuggests() { +		$suggests = $this->parser->getAttribute('suggests'); + +		if (!$suggests) { +			$suggests = array(); +		} + +		$normalized = array(); +		foreach ($suggests as $suggest) { +			$normalized[] = $this->normalizeDep($suggest); +		} + +		return $normalized; +	} + +	/** +	 * Normalizes a dependency array using the defined structs. +	 * Can be used with either requires or suggests. +	 * +	 * @param array $dep A dependency array. +	 * @return array The normalized deps array. +	 */ +	private function normalizeDep($dep) { +		switch ($dep['type']) { +			case 'elgg_version': +			case 'elgg_release': +				$struct = $this->depsStructElgg; +				break; + +			case 'plugin': +				$struct = $this->depsStructPlugin; +				break; + +			case 'priority': +				$struct = $this->depsStructPriority; +				break; + +			case 'php_extension': +				$struct = $this->depsStructPhpExtension; +				break; + +			case 'php_ini': +				$struct = $this->depsStructPhpIni; + +				// also normalize boolean values +				if (isset($dep['value'])) { +					switch (strtolower($dep['value'])) { +						case 'yes': +						case 'true': +						case 'on': +						case 1: +							$dep['value'] = 1; +							break; + +						case 'no': +						case 'false': +						case 'off': +						case 0: +						case '': +							$dep['value'] = 0; +							break; +					} +				} +				break; +			default: +				// unrecognized so we just return the raw dependency +				return $dep; +		} + +		$normalized_dep = $this->buildStruct($struct, $dep); + +		// normalize comparison operators +		if (isset($normalized_dep['comparison'])) { +			switch ($normalized_dep['comparison']) { +				case '<': +					$normalized_dep['comparison'] = 'lt'; +					break; + +				case '<=': +					$normalized_dep['comparison'] = 'le'; +					break; + +				case '>': +					$normalized_dep['comparison'] = 'gt'; +					break; + +				case '>=': +					$normalized_dep['comparison'] = 'ge'; +					break; + +				case '==': +				case 'eq': +					$normalized_dep['comparison'] = '='; +					break; + +				case '<>': +				case 'ne': +					$normalized_dep['comparison'] = '!='; +					break; +			} +		} + +		return $normalized_dep; +	} + +	/** +	 * Returns the conflicts listed +	 * +	 * @return array +	 */ +	public function getConflicts() { +		// normalize for 1.7 +		if ($this->getApiVersion() < 1.8) { +			$conflicts = array(); +		} else { +			$conflicts = $this->parser->getAttribute('conflicts'); +		} + +		if (!$conflicts) { +			$conflicts = array(); +		} + +		$normalized = array(); + +		foreach ($conflicts as $conflict) { +			$normalized[] = $this->buildStruct($this->depsConflictsStruct, $conflict); +		} + +		return $normalized; +	} + +	/** +	 * Should this plugin be activated when Elgg is installed +	 * +	 *  @return bool +	 */ +	public function getActivateOnInstall() { +		$activate = $this->parser->getAttribute('activate_on_install'); +		switch (strtolower($activate)) { +			case 'yes': +			case 'true': +			case 'on': +			case 1: +				return true; + +			case 'no': +			case 'false': +			case 'off': +			case 0: +			case '': +				return false; +		} +	} + +	/** +	 * 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_extract($index, $array, $default); +		} + +		return $return; +	} + +	/** +	 * Returns a category's friendly name. This can be localized by +	 * defining the string 'admin:plugins:category:<category>'. If no +	 * localization is found, returns the category with _ and - converted to ' ' +	 * and then ucwords()'d. +	 * +	 * @param str $category The category as defined in the manifest. +	 * @return str A human-readable category +	 */ +	static public function getFriendlyCategory($category) { +		$cat_raw_string = "admin:plugins:category:$category"; +		$cat_display_string = elgg_echo($cat_raw_string); +		if ($cat_display_string == $cat_raw_string) { +			$category = str_replace(array('-', '_'), ' ', $category); +			$cat_display_string = ucwords($category); +		} +		return $cat_display_string; +	} +} | 
