diff options
Diffstat (limited to 'engine/lib/plugins.php')
| -rw-r--r-- | engine/lib/plugins.php | 1538 |
1 files changed, 910 insertions, 628 deletions
diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index b8cf5c2d2..d5d3db466 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -3,304 +3,562 @@ * Elgg plugins library * Contains functions for managing plugins * - * @package Elgg - * @subpackage Core - * @author Curverider Ltd - * @link http://elgg.org/ + * @package Elgg.Core + * @subpackage Plugins */ +/** + * Tells ElggPlugin::start() to include the start.php file. + */ +define('ELGG_PLUGIN_INCLUDE_START', 1); -/// Cache enabled plugins per page -$ENABLED_PLUGINS_CACHE = NULL; +/** + * Tells ElggPlugin::start() to automatically register the plugin's views. + */ +define('ELGG_PLUGIN_REGISTER_VIEWS', 2); /** - * PluginException - * - * A plugin Exception, thrown when an Exception occurs relating to the plugin mechanism. Subclass for specific plugin Exceptions. + * Tells ElggPlugin::start() to automatically register the plugin's languages. + */ +define('ELGG_PLUGIN_REGISTER_LANGUAGES', 4); + +/** + * Tells ElggPlugin::start() to automatically register the plugin's classes. + */ +define('ELGG_PLUGIN_REGISTER_CLASSES', 8); + +/** + * Prefix for plugin setting names * - * @package Elgg - * @subpackage Exceptions + * @todo Can't namespace these because many plugins directly call + * private settings via $entity->$name. */ -class PluginException extends Exception {} +//define('ELGG_PLUGIN_SETTING_PREFIX', 'plugin:setting:'); /** - * @class ElggPlugin Object representing a plugin's settings for a given site. - * This class is currently a stub, allowing a plugin to saving settings in an object's metadata for each site. - * @author Curverider Ltd + * Prefix for plugin user setting names */ -class ElggPlugin extends ElggObject { - protected function initialise_attributes() { - parent::initialise_attributes(); +define('ELGG_PLUGIN_USER_SETTING_PREFIX', 'plugin:user_setting:'); + +/** + * Internal settings prefix + * + * @todo This could be resolved by promoting ElggPlugin to a 5th type. + */ +define('ELGG_PLUGIN_INTERNAL_PREFIX', 'elgg:internal:'); - $this->attributes['subtype'] = "plugin"; - } - public function __construct($guid = null) { - parent::__construct($guid); +/** + * Returns a list of plugin IDs (dir names) from a dir. + * + * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path. + * + * @return array + * @since 1.8.0 + * @access private + */ +function elgg_get_plugin_ids_in_dir($dir = null) { + if (!$dir) { + $dir = elgg_get_plugins_path(); } - /** - * Override entity get and sets in order to save data to private data store. - */ - public function get($name) { - // See if its in our base attribute - if (isset($this->attributes[$name])) { - return $this->attributes[$name]; + $plugin_ids = array(); + $handle = opendir($dir); + + if ($handle) { + while ($plugin_id = readdir($handle)) { + // must be directory and not begin with a . + if (substr($plugin_id, 0, 1) !== '.' && is_dir($dir . $plugin_id)) { + $plugin_ids[] = $plugin_id; + } } + } + + sort($plugin_ids); - // No, so see if its in the private data store. - // get_private_setting() returns false if it doesn't exist - $meta = get_private_setting($this->guid, $name); + return $plugin_ids; +} - if ($meta === false) { - // Can't find it, so return null - return NULL; +/** + * Discovers plugins in the plugins_path setting and creates ElggPlugin + * entities for them if they don't exist. If there are plugins with entities + * but not actual files, will disable the ElggPlugin entities and mark as inactive. + * The ElggPlugin object holds config data, so don't delete. + * + * @todo Crappy name? + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_generate_plugin_entities() { + // @todo $site unused, can remove? + $site = get_config('site'); + + $dir = elgg_get_plugins_path(); + $db_prefix = elgg_get_config('dbprefix'); + + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'selects' => array('plugin_oe.*'), + 'joins' => array("JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid"), + 'limit' => ELGG_ENTITIES_NO_VALUE + ); + + $old_ia = elgg_set_ignore_access(true); + $old_access = access_get_show_hidden_status(); + access_show_hidden_entities(true); + $known_plugins = elgg_get_entities_from_relationship($options); + /* @var ElggPlugin[] $known_plugins */ + + if (!$known_plugins) { + $known_plugins = array(); + } + + // map paths to indexes + $id_map = array(); + foreach ($known_plugins as $i => $plugin) { + // if the ID is wrong, delete the plugin because we can never load it. + $id = $plugin->getID(); + if (!$id) { + $plugin->delete(); + unset($known_plugins[$i]); + continue; } + $id_map[$plugin->getID()] = $i; + } + + $physical_plugins = elgg_get_plugin_ids_in_dir($dir); - return $meta; + if (!$physical_plugins) { + return false; } - /** - * Override entity get and sets in order to save data to private data store. - */ - public function set($name, $value) { - if (array_key_exists($name, $this->attributes)) { - // Check that we're not trying to change the guid! - if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) { - return false; + // check real plugins against known ones + foreach ($physical_plugins as $plugin_id) { + // is this already in the db? + if (array_key_exists($plugin_id, $id_map)) { + $index = $id_map[$plugin_id]; + $plugin = $known_plugins[$index]; + // was this plugin deleted and its entity disabled? + if (!$plugin->isEnabled()) { + $plugin->enable(); + $plugin->deactivate(); + $plugin->setPriority('last'); } - $this->attributes[$name] = $value; + // remove from the list of plugins to disable + unset($known_plugins[$index]); } else { - return set_private_setting($this->guid, $name, $value); + // add new plugins + // priority is force to last in save() if not set. + $plugin = new ElggPlugin($plugin_id); + $plugin->save(); } + } - return true; + // everything remaining in $known_plugins needs to be disabled + // because they are entities, but their dirs were removed. + // don't delete the entities because they hold settings. + foreach ($known_plugins as $plugin) { + if ($plugin->isActive()) { + $plugin->deactivate(); + } + // remove the priority. + $name = elgg_namespace_plugin_private_setting('internal', 'priority'); + remove_private_setting($plugin->guid, $name); + $plugin->disable(); } + + access_show_hidden_entities($old_access); + elgg_set_ignore_access($old_ia); + + elgg_reindex_plugin_priorities(); + + return true; } /** - * Returns a list of plugins to load, in the order that they should be loaded. - * - * @return array List of plugins + * Cache a reference to this plugin by its ID + * + * @param ElggPlugin $plugin + * + * @access private */ -function get_plugin_list() { - global $CONFIG; +function _elgg_cache_plugin_by_id(ElggPlugin $plugin) { + $map = (array) elgg_get_config('plugins_by_id_map'); + $map[$plugin->getID()] = $plugin; + elgg_set_config('plugins_by_id_map', $map); +} - if (!empty($CONFIG->pluginlistcache)) { - return $CONFIG->pluginlistcache; +/** + * Returns an ElggPlugin object with the path $path. + * + * @param string $plugin_id The id (dir name) of the plugin. NOT the guid. + * @return ElggPlugin|false + * @since 1.8.0 + */ +function elgg_get_plugin_from_id($plugin_id) { + $map = (array) elgg_get_config('plugins_by_id_map'); + if (isset($map[$plugin_id])) { + return $map[$plugin_id]; } - if ($site = get_entity($CONFIG->site_guid)) { - $pluginorder = $site->pluginorder; - if (!empty($pluginorder)) { - $plugins = unserialize($pluginorder); - - $CONFIG->pluginlistcache = $plugins; - return $plugins; - } else { - $plugins = array(); + $plugin_id = sanitize_string($plugin_id); + $db_prefix = get_config('dbprefix'); - if ($handle = opendir($CONFIG->pluginspath)) { - while ($mod = readdir($handle)) { - if (!in_array($mod,array('.','..','.svn','CVS')) && is_dir($CONFIG->pluginspath . "/" . $mod)) { - $plugins[] = $mod; - } - } - } + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'joins' => array("JOIN {$db_prefix}objects_entity oe on oe.guid = e.guid"), + 'selects' => array("oe.title", "oe.description"), + 'wheres' => array("oe.title = '$plugin_id'"), + 'limit' => 1 + ); - sort($plugins); + $plugins = elgg_get_entities($options); - $CONFIG->pluginlistcache = $plugins; - return $plugins; - } + if ($plugins) { + return $plugins[0]; } return false; } /** - * Regenerates the list of known plugins and saves it to the current site + * Returns if a plugin exists in the system. * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * @warning This checks only plugins that are registered in the system! + * If the plugin cache is outdated, be sure to regenerate it with + * {@link elgg_generate_plugin_objects()} first. * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * @param string $id The plugin ID. + * @since 1.8.0 + * @return bool + */ +function elgg_plugin_exists($id) { + $plugin = elgg_get_plugin_from_id($id); + + return ($plugin) ? true : false; +} + +/** + * Returns the highest priority of the plugins * - * @param array $pluginorder Optionally, a list of existing plugins and their orders - * @return array The new list of plugins and their orders + * @return int + * @since 1.8.0 + * @access private */ -function regenerate_plugin_list($pluginorder = false) { - global $CONFIG; +function elgg_get_max_plugin_priority() { + $db_prefix = get_config('dbprefix'); + $priority = elgg_namespace_plugin_private_setting('internal', 'priority'); + $plugin_subtype = get_subtype_id('object', 'plugin'); + + $q = "SELECT MAX(CAST(ps.value AS unsigned)) as max + FROM {$db_prefix}entities e, {$db_prefix}private_settings ps + WHERE ps.name = '$priority' + AND ps.entity_guid = e.guid + AND e.type = 'object' and e.subtype = $plugin_subtype"; + + $data = get_data($q); + if ($data) { + $max = $data[0]->max; + } else { + $max = 1; + } - $CONFIG->pluginlistcache = null; + // can't have a priority of 0. + return ($max) ? $max : 1; +} - if ($site = get_entity($CONFIG->site_guid)) { - if (empty($pluginorder)) { - $pluginorder = $site->pluginorder; - $pluginorder = unserialize($pluginorder); - } else { - ksort($pluginorder); - } +/** + * Returns if a plugin is active for a current site. + * + * @param string $plugin_id The plugin ID + * @param int $site_guid The site guid + * @since 1.8.0 + * @return bool + */ +function elgg_is_active_plugin($plugin_id, $site_guid = null) { + if ($site_guid) { + $site = get_entity($site_guid); + } else { + $site = elgg_get_site_entity(); + } - if (empty($pluginorder)) { - $pluginorder = array(); - } + if (!($site instanceof ElggSite)) { + return false; + } - $max = 0; - if (sizeof($pluginorder)) { - foreach($pluginorder as $key => $plugin) { - if (is_dir($CONFIG->pluginspath . "/" . $plugin)) { - if ($key > $max) - $max = $key; - } else { - unset($pluginorder[$key]); - } - } - } - // Add new plugins to the end - if ($handle = opendir($CONFIG->pluginspath)) { - while ($mod = readdir($handle)) { - if (!in_array($mod,array('.','..','.svn','CVS')) && is_dir($CONFIG->pluginspath . "/" . $mod)) { - if (!in_array($mod, $pluginorder)) { - $max = $max + 10; - $pluginorder[$max] = $mod; - } - } - } + $plugin = elgg_get_plugin_from_id($plugin_id); + + if (!$plugin) { + return false; + } + + return $plugin->isActive($site->guid); +} + +/** + * Loads all active plugins in the order specified in the tool admin panel. + * + * @note This is called on every page load. If a plugin is active and problematic, it + * will be disabled and a visible error emitted. This does not check the deps system because + * that was too slow. + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_load_plugins() { + $plugins_path = elgg_get_plugins_path(); + $start_flags = ELGG_PLUGIN_INCLUDE_START | + ELGG_PLUGIN_REGISTER_VIEWS | + ELGG_PLUGIN_REGISTER_LANGUAGES | + ELGG_PLUGIN_REGISTER_CLASSES; + + if (!$plugins_path) { + return false; + } + + // temporary disable all plugins if there is a file called 'disabled' in the plugin dir + if (file_exists("$plugins_path/disabled")) { + if (elgg_is_admin_logged_in() && elgg_in_context('admin')) { + system_message(elgg_echo('plugins:disabled')); } + return false; + } - ksort($pluginorder); + if (elgg_get_config('system_cache_loaded')) { + $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS; + } + + if (elgg_get_config('i18n_loaded_from_cache')) { + $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_LANGUAGES; + } + + $return = true; + $plugins = elgg_get_plugins('active'); + if ($plugins) { + foreach ($plugins as $plugin) { + try { + $plugin->start($start_flags); + } catch (Exception $e) { + $plugin->deactivate(); + $msg = elgg_echo('PluginException:CannotStart', + array($plugin->getID(), $plugin->guid, $e->getMessage())); + elgg_add_admin_notice('cannot_start' . $plugin->getID(), $msg); + $return = false; - // Now reorder the keys .. - $key = 10; - $plugins = array(); - if (sizeof($pluginorder)) { - foreach($pluginorder as $plugin) { - $plugins[$key] = $plugin; - $key = $key + 10; + continue; } } + } - $plugins = serialize($plugins); - - $site->pluginorder = $plugins; + return $return; +} - // Regenerate caches - elgg_view_regenerate_simplecache(); - elgg_filepath_cache_reset(); +/** + * Returns an ordered list of plugins + * + * @param string $status The status of the plugins. active, inactive, or all. + * @param mixed $site_guid Optional site guid + * @return ElggPlugin[] + * @since 1.8.0 + * @access private + */ +function elgg_get_plugins($status = 'active', $site_guid = null) { + $db_prefix = get_config('dbprefix'); + $priority = elgg_namespace_plugin_private_setting('internal', 'priority'); - return $plugins; + if (!$site_guid) { + $site = get_config('site'); + $site_guid = $site->guid; + } + // grab plugins + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'limit' => ELGG_ENTITIES_NO_VALUE, + 'selects' => array('plugin_oe.*'), + 'joins' => array( + "JOIN {$db_prefix}private_settings ps on ps.entity_guid = e.guid", + "JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid" + ), + 'wheres' => array("ps.name = '$priority'"), + 'order_by' => "CAST(ps.value as unsigned), e.guid" + ); + + switch ($status) { + case 'active': + $options['relationship'] = 'active_plugin'; + $options['relationship_guid'] = $site_guid; + $options['inverse_relationship'] = true; + break; + + case 'inactive': + $options['wheres'][] = "NOT EXISTS ( + SELECT 1 FROM {$db_prefix}entity_relationships active_er + WHERE active_er.guid_one = e.guid + AND active_er.relationship = 'active_plugin' + AND active_er.guid_two = $site_guid)"; + break; + + case 'all': + default: + break; } - return false; -} + $old_ia = elgg_set_ignore_access(true); + $plugins = elgg_get_entities_from_relationship($options); + elgg_set_ignore_access($old_ia); + return $plugins; +} /** - * For now, loads plugins directly + * Reorder plugins to an order specified by the array. + * Plugins not included in this array will be appended to the end. + * + * @note This doesn't use the ElggPlugin->setPriority() method because + * all plugins are being changed and we don't want it to automatically + * reorder plugins. * - * @todo Add proper plugin handler that launches plugins in an admin-defined order and activates them on admin request - * @package Elgg - * @subpackage Core + * @param array $order An array of plugin ids in the order to set them + * @return bool + * @since 1.8.0 + * @access private */ -function load_plugins() { - global $CONFIG; +function elgg_set_plugin_priorities(array $order) { + $name = elgg_namespace_plugin_private_setting('internal', 'priority'); - if (!empty($CONFIG->pluginspath)) { - // See if we have cached values for things - $cached_view_paths = elgg_filepath_cache_load(); - if ($cached_view_paths) { - $CONFIG->views = unserialize($cached_view_paths); + $plugins = elgg_get_plugins('any'); + if (!$plugins) { + return false; + } + + $return = true; + + // reindex to get standard counting. no need to increment by 10. + // though we do start with 1 + $order = array_values($order); + + $missing_plugins = array(); + foreach ($plugins as $plugin) { + $plugin_id = $plugin->getID(); + + if (!in_array($plugin_id, $order)) { + $missing_plugins[] = $plugin; + continue; } - // temporary disable all plugins if there is a file called 'disabled' in the plugin dir - if (file_exists($CONFIG->pluginspath . "disabled")) { - return; + $priority = array_search($plugin_id, $order) + 1; + + if (!$plugin->set($name, $priority)) { + $return = false; + break; } + } - $plugins = get_plugin_list(); - - if (sizeof($plugins)) { - foreach($plugins as $mod) { - if (is_plugin_enabled($mod)) { - if (file_exists($CONFIG->pluginspath . $mod)) { - if (!include($CONFIG->pluginspath . $mod . "/start.php")) { - // automatically disable the bad plugin - disable_plugin($mod); - - // register error rather than rendering the site unusable with exception - register_error(sprintf(elgg_echo('PluginException:MisconfiguredPlugin'), $mod)); - - // continue loading remaining plugins - continue; - } - - if (!$cached_view_paths) { - $view_dir = $CONFIG->pluginspath . $mod . '/views/'; - - if (is_dir($view_dir) && ($handle = opendir($view_dir))) { - while (FALSE !== ($view_type = readdir($handle))) { - $view_type_dir = $view_dir . $view_type; - - if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) { - if (autoregister_views('', $view_type_dir, $view_dir, $view_type)) { - // add the valid view type. - if (!in_array($view_type, $CONFIG->view_types)) { - $CONFIG->view_types[] = $view_type; - } - } - } - } - } - } - - if (is_dir($CONFIG->pluginspath . $mod . "/languages")) { - register_translations($CONFIG->pluginspath . $mod . "/languages/"); - } - } - } + // set the missing plugins' priorities + if ($return && $missing_plugins) { + if (!isset($priority)) { + $priority = 0; + } + foreach ($missing_plugins as $plugin) { + $priority++; + if (!$plugin->set($name, $priority)) { + $return = false; + break; } } + } - // Cache results - if (!$cached_view_paths) { - elgg_filepath_cache_save(serialize($CONFIG->views)); - } + return $return; +} + +/** + * Reindexes all plugin priorities starting at 1. + * + * @todo Can this be done in a single sql command? + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_reindex_plugin_priorities() { + return elgg_set_plugin_priorities(array()); +} + +/** + * Namespaces a string to be used as a private setting for a plugin. + * + * @param string $type The type of value: user_setting or internal. + * @param string $name The name to namespace. + * @param string $id The plugin's ID to namespace with. Required for user_setting. + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_namespace_plugin_private_setting($type, $name, $id = null) { + switch ($type) { + // commented out because it breaks $plugin->$name access to variables + //case 'setting': + // $name = ELGG_PLUGIN_SETTING_PREFIX . $name; + // break; + + case 'user_setting': + if (!$id) { + $id = elgg_get_calling_plugin_id(); + } + $name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name"; + break; + + case 'internal': + $name = ELGG_PLUGIN_INTERNAL_PREFIX . $name; + break; } + + return $name; } /** - * Get the name of the most recent plugin to be called in the call stack (or the plugin that owns the current page, if any). + * Get the name of the most recent plugin to be called in the + * call stack (or the plugin that owns the current page, if any). + * + * i.e., if the last plugin was in /mod/foobar/, this would return foo_bar. * - * i.e., if the last plugin was in /mod/foobar/, get_plugin_name would return foo_bar. + * @param boolean $mainfilename If set to true, this will instead determine the + * context from the main script filename called by + * the browser. Default = false. * - * @param boolean $mainfilename If set to true, this will instead determine the context from the main script filename called by the browser. Default = false. * @return string|false Plugin name, or false if no plugin name was called + * @since 1.8.0 + * @access private + * + * @todo get rid of this */ -function get_plugin_name($mainfilename = false) { +function elgg_get_calling_plugin_id($mainfilename = false) { if (!$mainfilename) { if ($backtrace = debug_backtrace()) { - foreach($backtrace as $step) { + foreach ($backtrace as $step) { $file = $step['file']; - $file = str_replace("\\","/",$file); - $file = str_replace("//","/",$file); - if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\/start\.php$/",$file,$matches)) { + $file = str_replace("\\", "/", $file); + $file = str_replace("//", "/", $file); + if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\/start\.php$/", $file, $matches)) { return $matches[1]; } } } } else { - //if (substr_count($file,'handlers/pagehandler')) { - if (preg_match("/pg\/([a-zA-Z0-9\-\_]*)\//",$_SERVER['REQUEST_URI'],$matches)) { - return $matches[1]; + //@todo this is a hack -- plugins do not have to match their page handler names! + if ($handler = get_input('handler', FALSE)) { + return $handler; } else { $file = $_SERVER["SCRIPT_NAME"]; - $file = str_replace("\\","/",$file); - $file = str_replace("//","/",$file); - if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\//",$file,$matches)) { + $file = str_replace("\\", "/", $file); + $file = str_replace("//", "/", $file); + if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\//", $file, $matches)) { return $matches[1]; } } @@ -309,589 +567,613 @@ function get_plugin_name($mainfilename = false) { } /** - * Load and parse a plugin manifest from a plugin XML file. - * - * Example file: + * Returns an array of all provides from all active plugins. * - * <plugin_manifest> - * <!-- Basic information --> - * <field key="name" value="My Plugin" /> - * <field key="description" value="My Plugin's concise description" /> - * <field key="version" value="1.0" /> - * <field key="category" value="theme" /> - * <field key="category" value="bundled" /> - * <field key="screenshot" value="path/relative/to/my_plugin.jpg" /> - * <field key="screenshot" value="path/relative/to/my_plugin_2.jpg" /> + * Array in the form array( + * 'provide_type' => array( + * 'provided_name' => array( + * 'version' => '1.8', + * 'provided_by' => 'provider_plugin_id' + * ) + * ) + * ) * - * <field key="author" value="Curverider Ltd" /> - * <field key="website" value="http://www.elgg.org/" /> - * <field key="copyright" value="(C) Curverider 2008-2010" /> - * <field key="licence" value="GNU Public License version 2" /> - * </plugin_manifest> + * @param string $type The type of provides to return + * @param string $name A specific provided name to return. Requires $provide_type. * - * @param string $plugin Plugin name. - * @return array of values + * @return array + * @since 1.8.0 + * @access private */ -function load_plugin_manifest($plugin) { - global $CONFIG; - - $xml = xml_to_object(file_get_contents($CONFIG->pluginspath . $plugin. "/manifest.xml")); - - if ($xml) { - // set up some defaults to normalize expected values to arrays - $elements = array( - 'screenshot' => array(), - 'category' => array() - ); - - foreach ($xml->children as $element) { - $key = $element->attributes['key']; - $value = $element->attributes['value']; - - // create arrays if multiple fields are set - if (array_key_exists($key, $elements)) { - if (!is_array($elements[$key])) { - $orig = $elements[$key]; - $elements[$key] = array($orig); +function elgg_get_plugins_provides($type = null, $name = null) { + static $provides = null; + $active_plugins = elgg_get_plugins('active'); + + if (!isset($provides)) { + $provides = array(); + + foreach ($active_plugins as $plugin) { + $plugin_provides = array(); + $manifest = $plugin->getManifest(); + if ($manifest instanceof ElggPluginManifest) { + $plugin_provides = $plugin->getManifest()->getProvides(); + } + if ($plugin_provides) { + foreach ($plugin_provides as $provided) { + $provides[$provided['type']][$provided['name']] = array( + 'version' => $provided['version'], + 'provided_by' => $plugin->getID() + ); } - - $elements[$key][] = $value; - } else { - $elements[$key] = $value; } } + } - // handle plugins that don't define a name - if (!isset($elements['name'])) { - $elements['name'] = ucwords($plugin); + 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 $elements; } - return false; + return $provides; } /** - * This function checks a plugin manifest 'elgg_version' value against the current install - * returning TRUE if the elgg_version is >= the current install's version. - * @param $manifest_elgg_version_string The build version (eg 2009010201). - * @return bool + * 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 array An array in the form array( + * 'status' => bool Does the provide exist?, + * 'value' => string The version provided + * ) + * @since 1.8.0 + * @access private */ -function check_plugin_compatibility($manifest_elgg_version_string) { - $version = get_version(); - - if (strpos($manifest_elgg_version_string, '.') === false) { - // Using version - $req_version = (int)$manifest_elgg_version_string; +function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') { + $provided = elgg_get_plugins_provides($type, $name); + if (!$provided) { + return array( + 'status' => false, + 'version' => '' + ); + } - return ($version >= $req_version); + if ($version) { + $status = version_compare($provided['version'], $version, $comparison); + } else { + $status = true; } - return false; + return array( + 'status' => $status, + 'value' => $provided['version'] + ); } /** - * Shorthand function for finding the plugin settings. + * Returns an array of parsed strings for a dependency in the + * format: array( + * 'type' => requires, conflicts, or provides. + * 'name' => The name of the requirement / conflict + * 'value' => A string representing the expected value: <1, >=3, !=enabled + * 'local_value' => The current value, ("Not installed") + * 'comment' => Free form text to help resovle the problem ("Enable / Search for plugin <link>") + * ) * - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you - * are calling from. + * @param array $dep An ElggPluginPackage dependency array + * @return array + * @since 1.8.0 + * @access private */ -function find_plugin_settings($plugin_name = "") { - $options = array('type' => 'object', 'subtype' => 'plugin', 'limit' => 9999); - $plugins = elgg_get_entities($options); - $plugin_name = sanitise_string($plugin_name); - if (!$plugin_name) { - $plugin_name = get_plugin_name(); +function elgg_get_plugin_dependency_strings($dep) { + $dep_system = elgg_extract('type', $dep); + $info = elgg_extract('dep', $dep); + $type = elgg_extract('type', $info); + + if (!$dep_system || !$info || !$type) { + return false; } - if ($plugins) { - foreach ($plugins as $plugin) { - if (strcmp($plugin->title, $plugin_name)==0) { - return $plugin; + // rewrite some of these to be more readable + switch($info['comparison']) { + case 'lt': + $comparison = '<'; + break; + case 'gt': + $comparison = '>'; + break; + case 'ge': + $comparison = '>='; + break; + case 'le': + $comparison = '<='; + break; + default; + $comparison = $info['comparison']; + break; + } + + /* + 'requires' 'plugin oauth_lib' <1.3 1.3 'downgrade' + 'requires' 'php setting bob' >3 3 'change it' + 'conflicts' 'php setting' >3 4 'change it' + 'conflicted''plugin profile' any 1.8 'disable profile' + 'provides' 'plugin oauth_lib' 1.3 -- -- + 'priority' 'before blog' -- after 'move it' + */ + $strings = array(); + $strings['type'] = elgg_echo('ElggPlugin:Dependencies:' . ucwords($dep_system)); + + switch ($type) { + case 'elgg_version': + case 'elgg_release': + // 'Elgg Version' + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Elgg'); + $strings['expected_value'] = "$comparison {$info['version']}"; + $strings['local_value'] = $dep['value']; + $strings['comment'] = ''; + break; + + case 'php_extension': + // PHP Extension %s [version] + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpExtension', array($info['name'])); + if ($info['version']) { + $strings['expected_value'] = "$comparison {$info['version']}"; + $strings['local_value'] = $dep['value']; + } else { + $strings['expected_value'] = ''; + $strings['local_value'] = ''; } + $strings['comment'] = ''; + break; + + case 'php_ini': + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpIni', array($info['name'])); + $strings['expected_value'] = "$comparison {$info['value']}"; + $strings['local_value'] = $dep['value']; + $strings['comment'] = ''; + break; + + case 'plugin': + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Plugin', array($info['name'])); + $expected = $info['version'] ? "$comparison {$info['version']}" : elgg_echo('any'); + $strings['expected_value'] = $expected; + $strings['local_value'] = $dep['value'] ? $dep['value'] : '--'; + $strings['comment'] = ''; + break; + + case 'priority': + $expected_priority = ucwords($info['priority']); + $real_priority = ucwords($dep['value']); + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Priority'); + $strings['expected_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$expected_priority", array($info['plugin'])); + $strings['local_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$real_priority", array($info['plugin'])); + $strings['comment'] = ''; + break; + } + + if ($dep['type'] == 'suggests') { + if ($dep['status']) { + $strings['comment'] = elgg_echo('ok'); + } else { + $strings['comment'] = elgg_echo('ElggPlugin:Dependencies:Suggests:Unsatisfied'); + } + } else { + if ($dep['status']) { + $strings['comment'] = elgg_echo('ok'); + } else { + $strings['comment'] = elgg_echo('error'); } } - return false; + return $strings; } /** - * Find the plugin settings for a user. + * Returns the ElggPlugin entity of the last plugin called. * - * @param string $plugin_name Plugin name. - * @param int $user_guid The guid who's settings to retrieve. - * @return array of settings in an associative array minus prefix. + * @return mixed ElggPlugin or false + * @since 1.8.0 + * @access private */ -function find_plugin_usersettings($plugin_name = "", $user_guid = 0) { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; +function elgg_get_calling_plugin_entity() { + $plugin_id = elgg_get_calling_plugin_id(); - if (!$plugin_name) { - $plugin_name = get_plugin_name(); + if ($plugin_id) { + return elgg_get_plugin_from_id($plugin_id); } - if ($user_guid == 0) { - $user_guid = get_loggedin_userid(); + return false; +} + +/** + * Returns an array of all plugin settings for a user. + * + * @param mixed $user_guid The user GUID or null for the currently logged in user. + * @param string $plugin_id The plugin ID + * @param bool $return_obj Return settings as an object? This can be used to in reusable + * views where the settings are passed as $vars['entity']. + * @return array + * @since 1.8.0 + */ +function elgg_get_all_plugin_user_settings($user_guid = null, $plugin_id = null, $return_obj = false) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - // Get metadata for user - $all_metadata = get_all_private_settings($user_guid); //get_metadata_for_entity($user_guid); - if ($all_metadata) { - $prefix = "plugin:settings:$plugin_name:"; - $return = new stdClass; + if (!$plugin instanceof ElggPlugin) { + return false; + } - foreach ($all_metadata as $key => $meta) { - $name = substr($key, strlen($prefix)); - $value = $meta; + $settings = $plugin->getAllUserSettings($user_guid); - if (strpos($key, $prefix) === 0) { - $return->$name = $value; - } + if ($settings && $return_obj) { + $return = new stdClass; + + foreach ($settings as $k => $v) { + $return->$k = $v; } return $return; + } else { + return $settings; } - - return false; } /** * Set a user specific setting for a plugin. * - * @param string $name The name - note, can't be "title". - * @param mixed $value The value. - * @param int $user_guid Optional user. - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @param string $name The name - note, can't be "title". + * @param mixed $value The value. + * @param int $user_guid Optional user. + * @param string $plugin_id Optional plugin name, if not specified then it + * is detected from where you are calling from. + * + * @return bool + * @since 1.8.0 */ -function set_plugin_usersetting($name, $value, $user_guid = 0, $plugin_name = "") { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity($user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - //$user->$prefix = $value; - //$user->save(); - - // Hook to validate setting - $value = trigger_plugin_hook('plugin:usersetting', 'user', array( - 'user' => $user, - 'plugin' => $plugin_name, - 'name' => $name, - 'value' => $value - ), $value); - - return set_private_setting($user->guid, $prefix, $value); + if (!$plugin) { + return false; } - return false; + return $plugin->setUserSetting($name, $value, $user_guid); } /** - * Clears a user-specific plugin setting + * Unsets a user-specific plugin setting * - * @param str $name Name of the plugin setting - * @param int $user_guid Defaults to logged in user - * @param str $plugin_name Defaults to contextual plugin name - * @return bool Success + * @param string $name Name of the setting + * @param int $user_guid Defaults to logged in user + * @param string $plugin_id Defaults to contextual plugin name + * + * @return bool + * @since 1.8.0 */ -function clear_plugin_usersetting($name, $user_guid=0, $plugin_name='') { - $plugin_name = sanitise_string($plugin_name); - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity((int) $user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_unset_plugin_user_setting($name, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - - return remove_private_setting($user->getGUID(), $prefix); + if (!$plugin) { + return false; } - return FALSE; + return $plugin->unsetUserSetting($name, $user_guid); } /** * Get a user specific setting for a plugin. * - * @param string $name The name. - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @param string $name The name of the setting. + * @param int $user_guid Guid of owning user + * @param string $plugin_id Optional plugin name, if not specified + * it is detected from where you are calling. + * + * @return mixed + * @since 1.8.0 */ -function get_plugin_usersetting($name, $user_guid = 0, $plugin_name = "") { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity($user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_get_plugin_user_setting($name, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - return get_private_setting($user->guid, $prefix); //$user->$prefix; + if (!$plugin) { + return false; } - return false; + return $plugin->getUserSetting($name, $user_guid); } /** * Set a setting for a plugin. * - * @param string $name The name - note, can't be "title". - * @param mixed $value The value. - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @param string $name The name of the setting - note, can't be "title". + * @param mixed $value The value. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @return bool + * @since 1.8.0 */ -function set_plugin_setting($name, $value, $plugin_name = "") { - if (!$plugin_name) { - $plugin_name = get_plugin_name(); +function elgg_set_plugin_setting($name, $value, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - $plugin = find_plugin_settings($plugin_name); if (!$plugin) { - $plugin = new ElggPlugin(); - } - - if ($name!='title') { - // Hook to validate setting - $value = trigger_plugin_hook('plugin:setting', 'plugin', array( - 'plugin' => $plugin_name, - 'name' => $name, - 'value' => $value - ), $value); - - $plugin->title = $plugin_name; - $plugin->access_id = ACCESS_PUBLIC; - $plugin->save(); - $plugin->$name = $value; - - return $plugin->getGUID(); + return false; } - return false; + return $plugin->setSetting($name, $value); } /** * Get setting for a plugin. * - * @param string $name The name. - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @param string $name The name of the setting. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @return mixed + * @since 1.8.0 + * @todo make $plugin_id required in future version */ -function get_plugin_setting($name, $plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); +function elgg_get_plugin_setting($name, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); + } - if ($plugin) { - return $plugin->$name; + if (!$plugin) { + return false; } - return false; + return $plugin->getSetting($name); } /** - * Clear a plugin setting. + * Unsets a plugin setting. + * + * @param string $name The name of the setting. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. * - * @param string $name The name. - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @return bool + * @since 1.8.0 */ -function clear_plugin_setting($name, $plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); +function elgg_unset_plugin_setting($name, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); + } - if ($plugin) { - return remove_private_setting($plugin->guid, $name); + if (!$plugin) { + return false; } - return FALSE; + return $plugin->unsetSetting($name); } /** - * Clear all plugin settings. + * Unsets all plugin settings for a plugin. * - * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @return bool + * @since 1.8.0 */ -function clear_all_plugin_settings($plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); - - if ($plugin) { - return remove_all_private_settings($plugin->guid); +function elgg_unset_all_plugin_settings($plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - return FALSE; -} - -/** - * Return an array of installed plugins. - */ -function get_installed_plugins() { - global $CONFIG; - - $installed_plugins = array(); - - if (!empty($CONFIG->pluginspath)) { - $plugins = get_plugin_list(); - - foreach($plugins as $mod) { - // require manifest. - if (!$manifest = load_plugin_manifest($mod)) { - continue; - } - $installed_plugins[$mod] = array(); - $installed_plugins[$mod]['active'] = is_plugin_enabled($mod); - $installed_plugins[$mod]['manifest'] = $manifest; - } + if (!$plugin) { + return false; } - return $installed_plugins; + return $plugin->unsetAllSettings(); } /** - * Enable a plugin for a site (default current site) + * Returns entities based upon plugin settings. + * Takes all the options for {@see elgg_get_entities_from_private_settings()} + * in addition to the ones below. + * + * @param array $options Array in the format: * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * plugin_id => NULL|STR The plugin id. Defaults to calling plugin * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * plugin_user_setting_names => NULL|ARR private setting names * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. + * plugin_user_setting_values => NULL|ARR metadata values + * + * plugin_user_setting_name_value_pairs => NULL|ARR ( + * name => 'name', + * value => 'value', + * 'operand' => '=', + * ) + * Currently if multiple values are sent via + * an array (value => array('value1', 'value2') + * the pair's operand will be forced to "IN". + * + * plugin_user_setting_name_value_pairs_operator => NULL|STR The operator to use for combining + * (name = value) OPERATOR (name = value); default AND + * + * @return mixed int If count, int. If not count, array. false on errors. */ -function enable_plugin($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; - - $plugin = sanitise_string($plugin); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; +function elgg_get_entities_from_plugin_user_settings(array $options = array()) { + // if they're passing it don't bother + if (!isset($options['plugin_id'])) { + $options['plugin_id'] = elgg_get_calling_plugin_id(); } - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite")); - } + $singulars = array('plugin_user_setting_name', 'plugin_user_setting_value', + 'plugin_user_setting_name_value_pair'); - if (!$plugin_info = load_plugin_manifest($plugin)) { - return FALSE; - } - - // getMetadata() doesn't return an array if only one plugin is enabled - if ($enabled = $site->enabled_plugins) { - if (!is_array($enabled)) { - $enabled = array($enabled); - } - } else { - $enabled = array(); - } + $options = elgg_normalise_plural_options_array($options, $singulars); - $enabled[] = $plugin; - $enabled = array_unique($enabled); + // rewrite plugin_user_setting_name_* to the right PS ones. + $map = array( + 'plugin_user_setting_names' => 'private_setting_names', + 'plugin_user_setting_values' => 'private_setting_values', + 'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs', + 'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator' + ); - if ($return = $site->setMetaData('enabled_plugins', $enabled)) { - - // for other plugins that want to hook into this. - if ($return && !trigger_elgg_event('enable', 'plugin', array('plugin' => $plugin, 'manifest' => $plugin_info))) { - $return = FALSE; + foreach ($map as $plugin => $private) { + if (!isset($options[$plugin])) { + continue; } - // for this plugin's on_enable - if ($return && isset($plugin_info['on_enable'])) { - // pull in the actual plugin's start so the on_enable function is callabe - // NB: this will not run re-run the init hooks! - $start = "{$CONFIG->pluginspath}$plugin/start.php"; - if (!file_exists($start) || !include($start)) { - $return = FALSE; + if (isset($options[$private])) { + if (!is_array($options[$private])) { + $options[$private] = array($options[$private]); } - // need language files for the messages - $translations = "{$CONFIG->pluginspath}$plugin/languages/"; - register_translations($translations); - if (!is_callable($plugin_info['on_enable'])) { - $return = FALSE; - } else { - $on_enable = call_user_func($plugin_info['on_enable']); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === FALSE) ? FALSE : TRUE; - } + $options[$private] = array_merge($options[$private], $options[$plugin]); + } else { + $options[$private] = $options[$plugin]; } + } - // disable the plugin if the on_enable or trigger results failed - if (!$return) { - array_pop($enabled); - $site->setMetaData('enabled_plugins', $enabled); - } - $ENABLED_PLUGINS_CACHE = $enabled; - } + $plugin_id = $options['plugin_id']; + $prefix = elgg_namespace_plugin_private_setting('user_setting', '', $plugin_id); + $options['private_setting_name_prefix'] = $prefix; - return $return; + return elgg_get_entities_from_private_settings($options); } /** - * Disable a plugin for a site (default current site) + * Register object, plugin entities as ElggPlugin classes * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * @return void + * @access private + */ +function plugin_run_once() { + add_subtype("object", "plugin", "ElggPlugin"); +} + +/** + * Runs unit tests for the entity objects. * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * @param string $hook unit_test + * @param string $type system + * @param mixed $value Array of tests + * @param mixed $params Params * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. + * @return array + * @access private */ -function disable_plugin($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; - - $plugin = sanitise_string($plugin); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite")); - } - - if (!$plugin_info = load_plugin_manifest($plugin)) { - return FALSE; - } - - // getMetadata() doesn't return an array if only one plugin is enabled - if ($enabled = $site->enabled_plugins) { - if (!is_array($enabled)) { - $enabled = array($enabled); - } - } else { - $enabled = array(); - } - - $old_enabled = $enabled; - - // remove the disabled plugin from the array - if (FALSE !== $i = array_search($plugin, $enabled)) { - unset($enabled[$i]); - } - - // if we're unsetting all the plugins, this will return an empty array. - // it will fail with FALSE, though. - $return = (FALSE === $site->enabled_plugins = $enabled) ? FALSE : TRUE; - - if ($return) { - // for other plugins that want to hook into this. - if ($return && !trigger_elgg_event('disable', 'plugin', array('plugin' => $plugin, 'manifest' => $plugin_info))) { - $return = FALSE; - } - - // for this plugin's on_disable - if ($return && isset($plugin_info['on_disable'])) { - if (!is_callable($plugin_info['on_disable'])) { - $return = FALSE; - } else { - $on_disable = call_user_func($plugin_info['on_disable']); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === FALSE) ? FALSE : TRUE; - } - } - - // disable the plugin if the on_enable or trigger results failed - if (!$return) { - $site->enabled_plugins = $old_enabled; - $ENABLED_PLUGINS_CACHE = $old_enabled; - } else { - $ENABLED_PLUGINS_CACHE = $enabled; - } - } - - return $return; +function plugins_test($hook, $type, $value, $params) { + global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/plugins.php'; + return $value; } /** - * Return whether a plugin is enabled or not. + * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case. * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. - * @return bool + * @param string $event deactivate + * @param string $type plugin + * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key + * @return bool false to block plugin deactivation action + * + * @access private */ -function is_plugin_enabled($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; +function _plugins_deactivate_dependency_check($event, $type, $params) { + $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); + $plugin_name = $params['plugin_entity']->getManifest()->getName(); - if (!file_exists($CONFIG->pluginspath . $plugin)) { - return false; - } + $active_plugins = elgg_get_plugins(); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } + $dependents = array(); + foreach ($active_plugins as $plugin) { + $manifest = $plugin->getManifest(); + $requires = $manifest->getRequires(); - if (!$ENABLED_PLUGINS_CACHE) { - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite")); - } - - $enabled_plugins = $site->enabled_plugins; - if ($enabled_plugins && !is_array($enabled_plugins)) { - $enabled_plugins = array($enabled_plugins); + foreach ($requires as $required) { + if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) { + // there are active dependents + $dependents[$manifest->getPluginID()] = $plugin; + } } - $ENABLED_PLUGINS_CACHE = $enabled_plugins; } - foreach ($ENABLED_PLUGINS_CACHE as $e) { - if ($e == $plugin) { - return true; + if ($dependents) { + $list = '<ul>'; + // construct error message and prevent disabling + foreach ($dependents as $dependent) { + $list .= '<li>' . $dependent->getManifest()->getName() . '</li>'; } - } + $list .= '</ul>'; - return false; -} + register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list))); -/** - * Run once and only once. - */ -function plugin_run_once() { - // Register a class - add_subtype("object", "plugin", "ElggPlugin"); + return false; + } } /** - * Initialise the file modules. - * Listens to system boot and registers any appropriate file types and classes + * Initialize the plugin system + * Listens to system init and registers actions + * + * @return void + * @access private */ function plugin_init() { - // Now run this stuff, but only once run_function_once("plugin_run_once"); - // Register some actions - register_action("plugins/settings/save", false, "", true); - register_action("plugins/usersettings/save"); + elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); + + // note - plugins are booted by the time this handler is registered + // deactivation due to error may have already occurred + elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check'); + + elgg_register_action("plugins/settings/save", '', 'admin'); + elgg_register_action("plugins/usersettings/save"); + + elgg_register_action('admin/plugins/activate', '', 'admin'); + elgg_register_action('admin/plugins/deactivate', '', 'admin'); + elgg_register_action('admin/plugins/activate_all', '', 'admin'); + elgg_register_action('admin/plugins/deactivate_all', '', 'admin'); - register_action('admin/plugins/enable', false, "", true); // Enable - register_action('admin/plugins/disable', false, "", true); // Disable - register_action('admin/plugins/enableall', false, "", true); // Enable all - register_action('admin/plugins/disableall', false, "", true); // Disable all + elgg_register_action('admin/plugins/set_priority', '', 'admin'); - register_action('admin/plugins/reorder', false, "", true); // Reorder + elgg_register_library('elgg:markdown', elgg_get_root_path() . 'vendors/markdown/markdown.php'); } -// Register a startup event -register_elgg_event_handler('init','system','plugin_init'); +elgg_register_event_handler('init', 'system', 'plugin_init'); |
