aboutsummaryrefslogtreecommitdiff
path: root/engine/lib/plugins.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/lib/plugins.php')
-rw-r--r--engine/lib/plugins.php1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php
new file mode 100644
index 000000000..d5d3db466
--- /dev/null
+++ b/engine/lib/plugins.php
@@ -0,0 +1,1179 @@
+<?php
+/**
+ * Elgg plugins library
+ * Contains functions for managing plugins
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ */
+
+/**
+ * Tells ElggPlugin::start() to include the start.php file.
+ */
+define('ELGG_PLUGIN_INCLUDE_START', 1);
+
+/**
+ * Tells ElggPlugin::start() to automatically register the plugin's views.
+ */
+define('ELGG_PLUGIN_REGISTER_VIEWS', 2);
+
+/**
+ * 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
+ *
+ * @todo Can't namespace these because many plugins directly call
+ * private settings via $entity->$name.
+ */
+//define('ELGG_PLUGIN_SETTING_PREFIX', 'plugin:setting:');
+
+/**
+ * Prefix for plugin user setting names
+ */
+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:');
+
+
+/**
+ * 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();
+ }
+
+ $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);
+
+ return $plugin_ids;
+}
+
+/**
+ * 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);
+
+ if (!$physical_plugins) {
+ 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');
+ }
+
+ // remove from the list of plugins to disable
+ unset($known_plugins[$index]);
+ } else {
+ // add new plugins
+ // priority is force to last in save() if not set.
+ $plugin = new ElggPlugin($plugin_id);
+ $plugin->save();
+ }
+ }
+
+ // 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;
+}
+
+/**
+ * Cache a reference to this plugin by its ID
+ *
+ * @param ElggPlugin $plugin
+ *
+ * @access private
+ */
+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);
+}
+
+/**
+ * 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];
+ }
+
+ $plugin_id = sanitize_string($plugin_id);
+ $db_prefix = get_config('dbprefix');
+
+ $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
+ );
+
+ $plugins = elgg_get_entities($options);
+
+ if ($plugins) {
+ return $plugins[0];
+ }
+
+ return false;
+}
+
+/**
+ * Returns if a plugin exists in the system.
+ *
+ * @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.
+ *
+ * @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
+ *
+ * @return int
+ * @since 1.8.0
+ * @access private
+ */
+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;
+ }
+
+ // can't have a priority of 0.
+ return ($max) ? $max : 1;
+}
+
+/**
+ * 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 (!($site instanceof ElggSite)) {
+ return false;
+ }
+
+ $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;
+ }
+
+ 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;
+
+ continue;
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * 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');
+
+ 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;
+ }
+
+ $old_ia = elgg_set_ignore_access(true);
+ $plugins = elgg_get_entities_from_relationship($options);
+ elgg_set_ignore_access($old_ia);
+
+ return $plugins;
+}
+
+/**
+ * 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.
+ *
+ * @param array $order An array of plugin ids in the order to set them
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_plugin_priorities(array $order) {
+ $name = elgg_namespace_plugin_private_setting('internal', 'priority');
+
+ $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;
+ }
+
+ $priority = array_search($plugin_id, $order) + 1;
+
+ if (!$plugin->set($name, $priority)) {
+ $return = false;
+ break;
+ }
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ 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).
+ *
+ * i.e., if the last plugin was in /mod/foobar/, this 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.
+ *
+ * @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 elgg_get_calling_plugin_id($mainfilename = false) {
+ if (!$mainfilename) {
+ if ($backtrace = debug_backtrace()) {
+ 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)) {
+ return $matches[1];
+ }
+ }
+ }
+ } else {
+ //@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)) {
+ return $matches[1];
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * 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
+ * @since 1.8.0
+ * @access private
+ */
+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()
+ );
+ }
+ }
+ }
+ }
+
+ 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 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 elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') {
+ $provided = elgg_get_plugins_provides($type, $name);
+ if (!$provided) {
+ return array(
+ 'status' => false,
+ 'version' => ''
+ );
+ }
+
+ if ($version) {
+ $status = version_compare($provided['version'], $version, $comparison);
+ } else {
+ $status = true;
+ }
+
+ return array(
+ 'status' => $status,
+ 'value' => $provided['version']
+ );
+}
+
+/**
+ * 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 array $dep An ElggPluginPackage dependency array
+ * @return array
+ * @since 1.8.0
+ * @access private
+ */
+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;
+ }
+
+ // 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 $strings;
+}
+
+/**
+ * Returns the ElggPlugin entity of the last plugin called.
+ *
+ * @return mixed ElggPlugin or false
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_calling_plugin_entity() {
+ $plugin_id = elgg_get_calling_plugin_id();
+
+ if ($plugin_id) {
+ return elgg_get_plugin_from_id($plugin_id);
+ }
+
+ 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();
+ }
+
+ if (!$plugin instanceof ElggPlugin) {
+ return false;
+ }
+
+ $settings = $plugin->getAllUserSettings($user_guid);
+
+ if ($settings && $return_obj) {
+ $return = new stdClass;
+
+ foreach ($settings as $k => $v) {
+ $return->$k = $v;
+ }
+
+ return $return;
+ } else {
+ return $settings;
+ }
+}
+
+/**
+ * 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_id Optional plugin name, if not specified then it
+ * is detected from where you are calling from.
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+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 (!$plugin) {
+ return false;
+ }
+
+ return $plugin->setUserSetting($name, $value, $user_guid);
+}
+
+/**
+ * Unsets a user-specific plugin setting
+ *
+ * @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 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 (!$plugin) {
+ return false;
+ }
+
+ return $plugin->unsetUserSetting($name, $user_guid);
+}
+
+/**
+ * Get a user specific setting for a plugin.
+ *
+ * @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 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 (!$plugin) {
+ return false;
+ }
+
+ return $plugin->getUserSetting($name, $user_guid);
+}
+
+/**
+ * Set a setting for a plugin.
+ *
+ * @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 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();
+ }
+
+ if (!$plugin) {
+ return false;
+ }
+
+ return $plugin->setSetting($name, $value);
+}
+
+/**
+ * Get setting for a plugin.
+ *
+ * @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 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 false;
+ }
+
+ return $plugin->getSetting($name);
+}
+
+/**
+ * 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.
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+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 false;
+ }
+
+ return $plugin->unsetSetting($name);
+}
+
+/**
+ * Unsets all plugin settings for a plugin.
+ *
+ * @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 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();
+ }
+
+ if (!$plugin) {
+ return false;
+ }
+
+ return $plugin->unsetAllSettings();
+}
+
+/**
+ * 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:
+ *
+ * plugin_id => NULL|STR The plugin id. Defaults to calling plugin
+ *
+ * plugin_user_setting_names => NULL|ARR private setting names
+ *
+ * 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 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();
+ }
+
+ $singulars = array('plugin_user_setting_name', 'plugin_user_setting_value',
+ 'plugin_user_setting_name_value_pair');
+
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ // 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'
+ );
+
+ foreach ($map as $plugin => $private) {
+ if (!isset($options[$plugin])) {
+ continue;
+ }
+
+ if (isset($options[$private])) {
+ if (!is_array($options[$private])) {
+ $options[$private] = array($options[$private]);
+ }
+
+ $options[$private] = array_merge($options[$private], $options[$plugin]);
+ } else {
+ $options[$private] = $options[$plugin];
+ }
+ }
+
+
+ $plugin_id = $options['plugin_id'];
+ $prefix = elgg_namespace_plugin_private_setting('user_setting', '', $plugin_id);
+ $options['private_setting_name_prefix'] = $prefix;
+
+ return elgg_get_entities_from_private_settings($options);
+}
+
+/**
+ * Register object, plugin entities as ElggPlugin classes
+ *
+ * @return void
+ * @access private
+ */
+function plugin_run_once() {
+ add_subtype("object", "plugin", "ElggPlugin");
+}
+
+/**
+ * Runs unit tests for the entity objects.
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
+function plugins_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/plugins.php';
+ return $value;
+}
+
+/**
+ * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case.
+ *
+ * @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 _plugins_deactivate_dependency_check($event, $type, $params) {
+ $plugin_id = $params['plugin_entity']->getManifest()->getPluginID();
+ $plugin_name = $params['plugin_entity']->getManifest()->getName();
+
+ $active_plugins = elgg_get_plugins();
+
+ $dependents = array();
+ foreach ($active_plugins as $plugin) {
+ $manifest = $plugin->getManifest();
+ $requires = $manifest->getRequires();
+
+ foreach ($requires as $required) {
+ if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) {
+ // there are active dependents
+ $dependents[$manifest->getPluginID()] = $plugin;
+ }
+ }
+ }
+
+ if ($dependents) {
+ $list = '<ul>';
+ // construct error message and prevent disabling
+ foreach ($dependents as $dependent) {
+ $list .= '<li>' . $dependent->getManifest()->getName() . '</li>';
+ }
+ $list .= '</ul>';
+
+ register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list)));
+
+ return false;
+ }
+}
+
+/**
+ * Initialize the plugin system
+ * Listens to system init and registers actions
+ *
+ * @return void
+ * @access private
+ */
+function plugin_init() {
+ run_function_once("plugin_run_once");
+
+ 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');
+
+ elgg_register_action('admin/plugins/set_priority', '', 'admin');
+
+ elgg_register_library('elgg:markdown', elgg_get_root_path() . 'vendors/markdown/markdown.php');
+}
+
+elgg_register_event_handler('init', 'system', 'plugin_init');