diff options
Diffstat (limited to 'engine/classes/ElggPluginPackage.php')
| -rw-r--r-- | engine/classes/ElggPluginPackage.php | 576 |
1 files changed, 225 insertions, 351 deletions
diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php index 8bbacce22..37eb4bf4d 100644 --- a/engine/classes/ElggPluginPackage.php +++ b/engine/classes/ElggPluginPackage.php @@ -28,6 +28,17 @@ class ElggPluginPackage { ); /** + * The optional files that can be read and served through the markdown page handler + * @var array + */ + private $textFiles = array( + 'README.txt', 'CHANGES.txt', + 'INSTALL.txt', 'COPYRIGHT.txt', 'LICENSE.txt', + + 'README', 'README.md', 'README.markdown' + ); + + /** * Valid types for provides. * * @var array @@ -42,13 +53,13 @@ class ElggPluginPackage { * @var array */ private $depsSupportedTypes = array( - 'elgg_version', 'elgg_release', 'php_extension', 'php_ini', 'plugin' + 'elgg_version', 'elgg_release', 'php_extension', 'php_ini', 'plugin', 'priority', ); /** * An invalid plugin error. */ - private $invalidPluginError = ''; + private $errorMsg = ''; /** * Any dependencies messages @@ -89,11 +100,13 @@ class ElggPluginPackage { * @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) == '/') { + $plugin_path = elgg_get_plugins_path(); + // @todo wanted to avoid another is_dir() call here. + // should do some profiling to see how much it affects + if (strpos($plugin, $plugin_path) === 0 || is_dir($plugin)) { // this is a path $path = sanitise_filepath($plugin); @@ -103,11 +116,11 @@ class ElggPluginPackage { } else { // this is a plugin id // strict plugin names - if (preg_match('/[^a-z0-9\.\-_]/i', $id)) { + if (preg_match('/[^a-z0-9\.\-_]/i', $plugin)) { throw new PluginException(elgg_echo('PluginException:InvalidID', array($plugin))); } - $path = get_config('pluginspath') . "$plugin/"; + $path = "{$plugin_path}$plugin/"; $id = $plugin; } @@ -119,9 +132,9 @@ class ElggPluginPackage { $this->id = $id; if ($validate && !$this->isValid()) { - if ($this->invalidPluginError) { + if ($this->errorMsg) { throw new PluginException(elgg_echo('PluginException:InvalidPlugin:Details', - array($plugin, $this->invalidPluginError))); + array($plugin, $this->errorMsg))); } else { throw new PluginException(elgg_echo('PluginException:InvalidPlugin', array($plugin))); } @@ -152,14 +165,12 @@ class ElggPluginPackage { 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 = + $this->errorMsg = elgg_echo('ElggPluginPackage:InvalidPlugin:MissingFile', array($file)); break; } @@ -167,23 +178,21 @@ class ElggPluginPackage { // check required files if (!$have_req_files) { - $valid = false; + return $this->valid = false; } // check for valid manifest. if (!$this->loadManifest()) { - $valid = false; + return $this->valid = false; } // can't require or conflict with yourself or something you provide. // make sure provides are all valid. if (!$this->isSaneDeps()) { - $valid = false; + return $this->valid = false; } - $this->valid = $valid; - - return $valid; + return $this->valid = true; } /** @@ -198,6 +207,12 @@ class ElggPluginPackage { * @return bool */ private function isSaneDeps() { + // protection against plugins with no manifest file + if (!$this->getManifest()) { + return false; + } + + // Note: $conflicts and $requires are not unused. They're called dynamically $conflicts = $this->getManifest()->getConflicts(); $requires = $this->getManifest()->getRequires(); $provides = $this->getManifest()->getProvides(); @@ -205,7 +220,7 @@ class ElggPluginPackage { foreach ($provides as $provide) { // only valid provide types if (!in_array($provide['type'], $this->providesSupportedTypes)) { - $this->invalidPluginError = + $this->errorMsg = elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidProvides', array($provide['type'])); return false; } @@ -215,17 +230,17 @@ class ElggPluginPackage { foreach (array('conflicts', 'requires') as $dep_type) { foreach (${$dep_type} as $dep) { if (!in_array($dep['type'], $this->depsSupportedTypes)) { - $this->invalidPluginError = + $this->errorMsg = elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidDependency', array($dep['type'])); return false; } // make sure nothing is providing something it conflicts or requires. - if ($dep['name'] == $name) { + if (isset($dep['name']) && $dep['name'] == $name) { $version_compare = version_compare($provide['version'], $dep['version'], $dep['comparison']); if ($version_compare) { - $this->invalidPluginError = + $this->errorMsg = elgg_echo('ElggPluginPackage:InvalidPlugin:CircularDep', array($dep['type'], $dep['name'], $this->id)); @@ -239,16 +254,6 @@ class ElggPluginPackage { return true; } - /** - * Checks if this plugin can be activated on the current - * Elgg installation. - * - * @return bool - */ - public function canActivate() { - return $this->checkDependencies(); - } - /************ * Manifest * @@ -261,7 +266,9 @@ class ElggPluginPackage { */ public function getManifest() { if (!$this->manifest) { - $this->loadManifest(); + if (!$this->loadManifest()) { + return false; + } } return $this->manifest; @@ -275,15 +282,34 @@ class ElggPluginPackage { */ private function loadManifest() { $file = $this->path . 'manifest.xml'; - $this->manifest = new ElggPluginManifest($file, $this->id); - if ($this->manifest) { + try { + $this->manifest = new ElggPluginManifest($file, $this->id); + } catch (Exception $e) { + $this->errorMsg = $e->getMessage(); + return false; + } + + if ($this->manifest instanceof ElggPluginManifest) { return true; } + $this->errorMsg = elgg_echo('unknown_error'); return false; } + /**************** + * Readme Files * + ***************/ + + /** + * Returns an array of present and readable text files + * + * @return array + */ + public function getTextFilenames() { + return $this->textFiles; + } /*********************** * Dependencies system * @@ -305,59 +331,110 @@ class ElggPluginPackage { * @return bool|array */ public function checkDependencies($full_report = false) { + // Note: $conflicts and $requires are not unused. They're called dynamically $requires = $this->getManifest()->getRequires(); $conflicts = $this->getManifest()->getConflicts(); - $enabled_plugins = get_installed_plugins('enabled'); + + $enabled_plugins = elgg_get_plugins('active'); + $this_id = $this->getID(); $report = array(); - foreach (array('requires', 'conflicts') as $dep_type) { + // first, check if any active plugin conflicts with us. + foreach ($enabled_plugins as $plugin) { + $temp_conflicts = array(); + $temp_manifest = $plugin->getManifest(); + if ($temp_manifest instanceof ElggPluginManifest) { + $temp_conflicts = $plugin->getManifest()->getConflicts(); + } + foreach ($temp_conflicts as $conflict) { + if ($conflict['type'] == 'plugin' && $conflict['name'] == $this_id) { + $result = $this->checkDepPlugin($conflict, $enabled_plugins, false); + + // rewrite the conflict to show the originating plugin + $conflict['name'] = $plugin->getManifest()->getName(); + + if (!$full_report && !$result['status']) { + $this->errorMsg = "Conflicts with plugin \"{$plugin->getManifest()->getName()}\"."; + return $result['status']; + } else { + $report[] = array( + 'type' => 'conflicted', + 'dep' => $conflict, + 'status' => $result['status'], + 'value' => $this->getManifest()->getVersion() + ); + } + } + } + } + + $check_types = array('requires', 'conflicts'); + + if ($full_report) { + // Note: $suggests is not unused. It's called dynamically + $suggests = $this->getManifest()->getSuggests(); + $check_types[] = 'suggests'; + } + + foreach ($check_types as $dep_type) { $inverse = ($dep_type == 'conflicts') ? true : false; foreach (${$dep_type} as $dep) { switch ($dep['type']) { case 'elgg_version': - $result = $this->checkDepElgg($dep, get_version()); + $result = $this->checkDepElgg($dep, get_version(), $inverse); break; case 'elgg_release': - $result = $this->checkDepElgg($dep, get_version(true)); + $result = $this->checkDepElgg($dep, get_version(true), $inverse); break; case 'plugin': $result = $this->checkDepPlugin($dep, $enabled_plugins, $inverse); break; + case 'priority': + $result = $this->checkDepPriority($dep, $enabled_plugins, $inverse); + break; + case 'php_extension': - $result = $this->checkDepPhpExtension($dep); + $result = $this->checkDepPhpExtension($dep, $inverse); break; case 'php_ini': - $result = $this->checkDepPhpIni($dep); + $result = $this->checkDepPhpIni($dep, $inverse); break; } // unless we're doing a full report, break as soon as we fail. - if (!$full_report && !$result) { - return $result; + if (!$full_report && !$result['status']) { + $this->errorMsg = "Missing dependencies."; + return $result['status']; } 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 + 'status' => $result['status'], + 'value' => $result['value'] ); } } } if ($full_report) { + // add provides to full report + $provides = $this->getManifest()->getProvides(); + + foreach ($provides as $provide) { + $report[] = array( + 'type' => 'provides', + 'dep' => $provide, + 'status' => true, + 'value' => '' + ); + } + return $report; } @@ -368,7 +445,7 @@ class ElggPluginPackage { * 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 array $plugins A list of plugins as returned by elgg_get_plugins(); * @param bool $inverse Inverse the results to use as a conflicts. * @return bool */ @@ -376,340 +453,151 @@ class ElggPluginPackage { $r = elgg_check_plugins_provides('plugin', $dep['name'], $dep['version'], $dep['comparison']); if ($inverse) { - $r = !$r; + $r['status'] = !$r['status']; } 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. + * Checks if $plugins meets the requirement by $dep. * - * @param array $dep An Elgg manifest.xml deps array + * @param array $dep An Elgg manifest.xml deps array + * @param array $plugins A list of plugins as returned by elgg_get_plugins(); + * @param bool $inverse Inverse the results to use as a conflicts. * @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; + private function checkDepPriority(array $dep, array $plugins, $inverse = false) { + // grab the ElggPlugin using this package. + $plugin_package = elgg_get_plugin_from_id($this->getID()); + $plugin_priority = $plugin_package->getPriority(); + $test_plugin = elgg_get_plugin_from_id($dep['plugin']); + + // If this isn't a plugin or the plugin isn't installed or active + // priority doesn't matter. Use requires to check if a plugin is active. + if (!$plugin_package || !$test_plugin || !$test_plugin->isActive()) { + return array( + 'status' => true, + 'value' => 'uninstalled' + ); } - $r = version_compare($setting, $value, $comparison); - - return $r; - } - - - /************************************** - * Detailed reports for requirements. * - **************************************/ + $test_plugin_priority = $test_plugin->getPriority(); + switch ($dep['priority']) { + case 'before': + $status = $plugin_priority < $test_plugin_priority; + break; - /** - * 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_version': - $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; - } + case 'after': + $status = $plugin_priority > $test_plugin_priority; + break; - if (!$result['status']) { + default; $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)); - } + // get the current value + if ($plugin_priority < $test_plugin_priority) { + $value = 'before'; + } else { + $value = 'after'; } - // 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)); - } - } + if ($inverse) { + $status = !$status; } return array( 'status' => $status, - 'message' => $message + 'value' => $value ); } /** - * Checks if $elgg_version meets the requirement by $require. - * - * Returns an array in the form array('status' => bool, 'message' => 'Any messages') + * Checks if $elgg_version meets the requirement by $dep. * - * @param array $require An Elgg manifest.xml requires array + * @param array $dep An Elgg manifest.xml deps array * @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z) - * @return array + * @param bool $inverse Inverse the result to use as a conflicts. + * @return bool */ - 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)); + private function checkDepElgg(array $dep, $elgg_version, $inverse = false) { + $status = version_compare($elgg_version, $dep['version'], $dep['comparison']); + + if ($inverse) { + $status = !$status; } return array( 'status' => $status, - 'message' => $message + 'value' => $elgg_version ); } /** - * Checks if the PHP extension in $require is loaded. + * Checks if the PHP extension in $dep is loaded. * * @todo Can this be merged with the plugin checker? * - * @param array $require An Elgg manifest.xml deps array - * @return array + * @param array $dep An Elgg manifest.xml deps array + * @param bool $inverse Inverse the result to use as a conflicts. + * @return array An array in the form array( + * 'status' => bool + * 'value' => string The version provided + * ) */ - private function checkRequiresPhpExtension($require) { - $status = true; - $message = ''; - - $name = $require['name']; - $version = $require['version']; - $comparison = $require['comparison']; + private function checkDepPhpExtension(array $dep, $inverse = false) { + $name = $dep['name']; + $version = $dep['version']; + $comparison = $dep['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)); - } - } + $status = extension_loaded($name); // enabled. check version. - if ($status != false) { - $ext_version = phpversion($name); + $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)); + if ($status) { + // some extensions (like gd) don't provide versions. neat. + // don't check version info and return a lie. + if ($ext_version && $version) { + $status = version_compare($ext_version, $version, $comparison); + } + + if (!$ext_version) { + $ext_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)); - } - } + $provides = elgg_check_plugins_provides('php_extension', $name, $version, $comparison); + $status = $provides['status']; + $ext_version = $provides['value']; + } + + if ($inverse) { + $status = !$status; } return array( 'status' => $status, - 'message' => $message + 'value' => $ext_version ); } - /** - * Check if the PHP ini setting satisfies $require. + * Check if the PHP ini setting satisfies $dep. * - * @param array $require An Elgg manifest.xml requires array - * @return array + * @param array $dep An Elgg manifest.xml deps array + * @param bool $inverse Inverse the result to use as a conflicts. + * @return bool */ - private function checkRequiresPhpIni($require) { - $status = true; - $message = ''; - - $name = $require['name']; - $value = $require['value']; - $comparison = $require['comparison']; + private function checkDepPhpIni($dep, $inverse = false) { + $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. @@ -720,41 +608,19 @@ class ElggPluginPackage { $setting = 0; } - if (!version_compare($setting, $value, $comparison)) { - $status = false; - $message = elgg_echo("ElggPluginPackage:Requires:PhpIni:$comparison", - array($this->getManifest()->getPluginID(), $name, $value, $setting)); + $status = version_compare($setting, $value, $comparison); + + if ($inverse) { + $status = !$status; } return array( 'status' => $status, - 'message' => $message + 'value' => $setting ); } /** - * 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 @@ -763,4 +629,12 @@ class ElggPluginPackage { return $this->id; } -}
\ No newline at end of file + /** + * Returns the last error message. + * + * @return string + */ + public function getError() { + return $this->errorMsg; + } +} |
