aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-12-03 03:11:49 +0000
committerbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-12-03 03:11:49 +0000
commit4b10b550a2449827c643c896424eb0277c43b049 (patch)
treea0b563666d98f3e32f2b502c2d6474afa3d17d86
parent4ad5937e6077a60ecc7ee7828fb8975af7491862 (diff)
downloadelgg-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
-rw-r--r--engine/classes/ElggPluginManifest.php299
-rw-r--r--engine/classes/ElggPluginManifestParser.php27
-rw-r--r--engine/classes/ElggPluginManifestParser17.php39
-rw-r--r--engine/classes/ElggPluginManifestParser18.php43
-rw-r--r--engine/classes/ElggPluginPackage.php763
-rw-r--r--engine/lib/plugins.php81
-rw-r--r--engine/tests/api/plugins.php250
-rw-r--r--engine/tests/test_files/plugin_17/manifest.xml10
-rw-r--r--engine/tests/test_files/plugin_17/start.php0
-rw-r--r--engine/tests/test_files/plugin_18/manifest.xml97
-rw-r--r--engine/tests/test_files/plugin_18/start.php0
-rw-r--r--languages/en.php61
12 files changed, 1466 insertions, 204 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
diff --git a/languages/en.php b/languages/en.php
index 7155cf223..b1796f51f 100644
--- a/languages/en.php
+++ b/languages/en.php
@@ -63,6 +63,65 @@ $english = array(
'InvalidClassException:NotValidElggStar' => "GUID:%d is not a valid %s",
'PluginException:MisconfiguredPlugin' => "%s is a misconfigured plugin. It has been disabled. Please search the Elgg wiki for possible causes (http://docs.elgg.org/wiki/).",
+ 'PluginException:InvalidID' => "%s is an invalid plugin ID.",
+ 'PluginException:InvalidPath' => "%s is an invalid plugin path.",
+ 'PluginException:InvalidManifest' => 'Invalid manifest file for plugin %s',
+ 'PluginException:InvalidPlugin' => '%s is not a valid plugin.',
+ 'PluginException:InvalidPlugin:Details' => '%s is not a valid plugin: %s',
+ 'ElggPluginPackage:InvalidPlugin:MissingFile' => 'Missing file %s in package',
+ 'ElggPluginPackage:InvalidPlugin:InvalidDependency' => 'Invalid dependency type "%s"',
+ 'ElggPluginPackage:InvalidPlugin:InvalidProvides' => 'Invalid provides type "%s"',
+ 'ElggPluginPackage:InvalidPlugin:CircularDep' => 'Invalid %s dependency "%s" in plugin %s. Plugins cannot conflict with or require something they provide!',
+
+ 'PluginException:ParserError' => 'Error parsing manifest with API version %s in plugin %s.',
+ 'PluginException:NoAvailableParser' => 'Cannot find a parser for manifest API version %s in plugin %s.',
+ 'PluginException:ParserErrorMissingRequiredAttribute' => "Missing required '%s' attribute in manifest for plugin %s.",
+
+ 'ElggPluginPackage:UnknownDep' => 'Unknown dependency %s in plugin %s',
+
+ 'ElggPluginPackage:Requires:Plugin:lt' => '%s requires the %s plugin below version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:Plugin:le' => '%s requires the %s plugin version %s or below. Version %s is installed.',
+ 'ElggPluginPackage:Requires:Plugin:=' => '%s requires the %s plugin version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:Plugin:!=' => '%s requires the %s plugin not equal to version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:Plugin:ge' => '%s requires the %s plugin version %s or higher. Version %s is installed.',
+ 'ElggPluginPackage:Requires:Plugin:gt' => '%s requires the %s plugin higher than version %s. Version %s is installed.',
+
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:lt' => '%s requires the %s plugin below version %s.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:le' => '%s requires the %s plugin version %s or below.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:=' => '%s requires the %s plugin version %s.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:!=' => '%s requires the %s plugin not equal to version %s.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:ge' => '%s requires the %s plugin version %s or higher.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:gt' => '%s requires the %s plugin higher than version %s.',
+ 'ElggPluginPackage:Requires:Plugin:NotEnabled:NoVersion' => '%s requires the %s plugin.',
+
+ 'ElggPluginPackage:Requires:Elgg:lt' => '%s requires Elgg below version %s.',
+ 'ElggPluginPackage:Requires:Elgg:le' => '%s requires Elgg %s or below.',
+ 'ElggPluginPackage:Requires:Elgg:=' => '%s requires Elgg version %s.',
+ 'ElggPluginPackage:Requires:Elgg:!=' => '%s requires Elgg not equal to version %s.',
+ 'ElggPluginPackage:Requires:Elgg:ge' => '%s requires Elgg version %s or higher.',
+ 'ElggPluginPackage:Requires:Elgg:gt' => '%s requires Elgg higher than version %s.',
+
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:lt' => '%s requires the %s PHP extension below version %s.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:le' => '%s requires the %s PHP extension version %s or below.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:=' => '%s requires the %s PHP extension version %s.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:!=' => '%s requires the %s PHP extension not equal to version %s.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:ge' => '%s requires the %s PHP extension version %s or higher.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:gt' => '%s requires the %s PHP extension higher than version %s.',
+ 'ElggPluginPackage:Requires:PhpExtension:NotInstalled:NoVersion' => '%s requires the %s PHP extension.',
+
+ 'ElggPluginPackage:Requires:PhpExtension:lt' => '%s requires the %s PHP extension below version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:PhpExtension:le' => '%s requires the %s PHP extension version %s or below. Version %s is installed.',
+ 'ElggPluginPackage:Requires:PhpExtension:=' => '%s requires the %s PHP extension version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:PhpExtension:!=' => '%s requires the %s PHP extension not equal to version %s. Version %s is installed.',
+ 'ElggPluginPackage:Requires:PhpExtension:ge' => '%s requires the %s PHP extension version %s or higher. Version %s is installed.',
+ 'ElggPluginPackage:Requires:PhpExtension:gt' => '%s requires the %s PHP extension higher than version %s. Version %s is installed.',
+
+ 'ElggPluginPackage:Requires:PhpIni:lt' => '%s requires the %s php.ini option set less than %s. Currently set to %s.',
+ 'ElggPluginPackage:Requires:PhpIni:le' => '%s requires the %s php.ini option set less than or equal to %s. Currently set to %s.',
+ 'ElggPluginPackage:Requires:PhpIni:=' => '%s requires the %s php.ini option set to %s. Currently set to %s.',
+ 'ElggPluginPackage:Requires:PhpIni:!=' => '%s requires the %s php.ini option not set to %s. Currently set to %s.',
+ 'ElggPluginPackage:Requires:PhpIni:ge' => '%s requires the %s php.ini option set greater than or equal to %s. Currently set to %s.',
+ 'ElggPluginPackage:Requires:PhpIni:gt' => '%s requires the %s php.ini option set greater than %s. Currently set to %s.',
'InvalidParameterException:NonElggUser' => "Passing a non-ElggUser to an ElggUser constructor!",
@@ -770,7 +829,7 @@ Once you have logged in, we highly recommend that you change your password.
'upgrading' => 'Upgrading...',
'upgrade:db' => 'Your database was upgraded.',
'upgrade:core' => 'Your elgg installation was upgraded.',
-
+
'deprecated:function' => '%s() was deprecated by %s()',
/**