diff options
Diffstat (limited to 'engine/classes/ElggPlugin.php')
| -rw-r--r-- | engine/classes/ElggPlugin.php | 504 |
1 files changed, 343 insertions, 161 deletions
diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 3352105f8..545b9a53c 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -9,11 +9,12 @@ * @subpackage Plugins.Settings */ class ElggPlugin extends ElggObject { - public $package; - public $manifest; + private $package; + private $manifest; private $path; private $pluginID; + private $errorMsg = ''; /** * Set subtype to 'plugin' @@ -29,15 +30,15 @@ class ElggPlugin extends ElggObject { $this->access_id = ACCESS_PUBLIC; } - /** * Loads the plugin by GUID or path. * * @warning Unlike other ElggEntity objects, you cannot null instantiate * ElggPlugin. You must point it to an actual plugin GUID or location. * - * @param mixed $plugin The GUID of the ElggPlugin object or the path of - * the plugin to load. + * @param mixed $plugin The GUID of the ElggPlugin object or the path of the plugin to load. + * + * @throws PluginException */ public function __construct($plugin) { if (!$plugin) { @@ -48,12 +49,14 @@ class ElggPlugin extends ElggObject { // @todo plugins w/id 12345 if (is_numeric($plugin) || is_object($plugin)) { parent::__construct($plugin); - $this->path = get_config('plugins_path') . $this->getID(); + $this->path = elgg_get_plugins_path() . $this->getID(); } else { + $plugin_path = elgg_get_plugins_path(); + // not a full path, so assume an id // use the default path - if (substr($plugin, 0, 1) != '/') { - $plugin = elgg_get_plugin_path() . $plugin; + if (strpos($plugin, $plugin_path) !== 0) { + $plugin = $plugin_path . $plugin; } // path checking is done in the package @@ -75,19 +78,9 @@ class ElggPlugin extends ElggObject { parent::__construct($existing_guid); } - // We have to let the entity load so we can manipulate it with the API. - // If the path is wrong or would cause an exception, catch it, - // disable the plugin, and emit an error. - try { - $this->package = new ElggPluginPackage($this->path, false); - $this->manifest = $this->package->getManifest(); - } catch (Exception $e) { - // we always have to allow the entity to load. - elgg_log("Failed to load $this->guid as a plugin. " . $e->getMessage(), 'WARNING'); - } + _elgg_cache_plugin_by_id($this); } - /** * Save the plugin object. Make sure required values exist. * @@ -126,6 +119,21 @@ class ElggPlugin extends ElggObject { } /** + * Returns the manifest's name if available, otherwise the ID. + * + * @return string + * @since 1.8.1 + */ + public function getFriendlyName() { + $manifest = $this->getManifest(); + if ($manifest) { + return $manifest->getName(); + } + + return $this->getID(); + } + + /** * Returns the plugin's full path with trailing slash. * * @return string @@ -134,17 +142,33 @@ class ElggPlugin extends ElggObject { return sanitise_filepath($this->path); } - /** * Sets the location of this plugin. * - * @param path $id The path to the plugin's dir. + * @param string $id The path to the plugin's dir. * @return bool */ public function setID($id) { return $this->attributes['title'] = $id; } + /** + * Returns an array of available markdown files for this plugin + * + * @return array + */ + public function getAvailableTextFiles() { + $filenames = $this->getPackage()->getTextFilenames(); + + $files = array(); + foreach ($filenames as $filename) { + if ($this->canReadFile($filename)) { + $files[$filename] = "$this->path/$filename"; + } + } + + return $files; + } // Load Priority @@ -158,7 +182,6 @@ class ElggPlugin extends ElggObject { return $this->$name; } - /** * Sets the priority of the plugin * @@ -175,41 +198,29 @@ class ElggPlugin extends ElggObject { $db_prefix = get_config('dbprefix'); $name = elgg_namespace_plugin_private_setting('internal', 'priority'); - // if no priority assume a priority of 0 + // if no priority assume a priority of 1 $old_priority = (int) $this->getPriority(); + $old_priority = (!$old_priority) ? 1 : $old_priority; $max_priority = elgg_get_max_plugin_priority(); - // (int) 0 matches (string) first, so cast to string. - $priority = (string) $priority; - - switch ($priority) { - case '+1': - $priority = $old_priority + 1; - break; - - case '-1': - $priority = $old_priority - 1; - break; - - case 'first': - $priority = 1; - break; - - case 'last': - $priority = $max_priority; - break; + // can't use switch here because it's not strict and + // php evaluates +1 == 1 + if ($priority === '+1') { + $priority = $old_priority + 1; + } elseif ($priority === '-1') { + $priority = $old_priority - 1; + } elseif ($priority === 'first') { + $priority = 1; + } elseif ($priority === 'last') { + $priority = $max_priority; } // should be a number by now - if ($priority) { + if ($priority > 0) { if (!is_numeric($priority)) { return false; } - if ($priority == $old_priority) { - return false; - } - // there's nothing above the max. if ($priority > $max_priority) { $priority = $max_priority; @@ -241,7 +252,6 @@ class ElggPlugin extends ElggObject { // set this priority if ($this->set($name, $priority)) { - //return elgg_plugins_reindex_priorities(); return true; } else { return false; @@ -257,8 +267,6 @@ class ElggPlugin extends ElggObject { /** * Returns a plugin setting * - * @todo These need to be namespaced - * * @param string $name The setting name * @return mixed */ @@ -266,12 +274,46 @@ class ElggPlugin extends ElggObject { return $this->$name; } + /** + * Returns an array of all settings saved for this plugin. + * + * @note Unlike user settings, plugin settings are not namespaced. + * + * @return array An array of key/value pairs. + */ + public function getAllSettings() { + if (!$this->guid) { + return false; + } + + $db_prefix = elgg_get_config('dbprefix'); + // need to remove all namespaced private settings. + $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); + $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID()); + + // Get private settings for user + $q = "SELECT * FROM {$db_prefix}private_settings + WHERE entity_guid = $this->guid + AND name NOT LIKE '$us_prefix%' + AND name NOT LIKE '$is_prefix%'"; + + $private_settings = get_data($q); + + $return = array(); + + if ($private_settings) { + foreach ($private_settings as $setting) { + $return[$setting->name] = $setting->value; + } + } + + return $return; + } /** * Set a plugin setting for the plugin * * @todo This will only work once the plugin has a GUID. - * @todo These need to be namespaced. * * @param string $name The name to set * @param string $value The value to set @@ -279,21 +321,13 @@ class ElggPlugin extends ElggObject { * @return bool */ public function setSetting($name, $value) { - if ($this->guid) { + if (!$this->guid) { return false; } - // Hook to validate setting - $value = elgg_trigger_plugin_hook('plugin:setting', 'plugin', array( - 'plugin' => $this->pluginID, - 'plugin_object' => $this, - 'name' => $name, - 'value' => $value - ), $value); - return $this->$name = $value; + return $this->set($name, $value); } - /** * Removes a plugin setting name and value. * @@ -301,11 +335,10 @@ class ElggPlugin extends ElggObject { * * @return bool */ - public function removeSetting($name) { + public function unsetSetting($name) { return remove_private_setting($this->guid, $name); } - /** * Removes all settings for this plugin. * @@ -313,13 +346,16 @@ class ElggPlugin extends ElggObject { * @todo If we could namespace the plugin settings this would be cleaner. * @return bool */ - public function removeAllSettings() { + public function unsetAllSettings() { $db_prefix = get_config('dbprefix'); - $ps_prefix = elgg_namespace_plugin_private_setting('setting', ''); + + $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); + $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID()); $q = "DELETE FROM {$db_prefix}private_settings WHERE entity_guid = $this->guid - AND name NOT LIKE '$ps_prefix%'"; + AND name NOT LIKE '$us_prefix%' + AND name NOT LIKE '$is_prefix%'"; return delete_data($q); } @@ -330,44 +366,139 @@ class ElggPlugin extends ElggObject { /** * Returns a user's setting for this plugin * - * @param int $user_guid The user GUID * @param string $name The setting name + * @param int $user_guid The user GUID * * @return mixed The setting string value or false */ - public function getUserSetting($user_guid, $name) { + public function getUserSetting($name, $user_guid = null) { + $user_guid = (int)$user_guid; + + if ($user_guid) { + $user = get_entity($user_guid); + } else { + $user = elgg_get_logged_in_user_entity(); + } + + if (!($user instanceof ElggUser)) { + return false; + } + $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID()); - return get_private_setting($user_guid, $name); + return get_private_setting($user->guid, $name); + } + + /** + * Returns an array of all user settings saved for this plugin for the user. + * + * @note Plugin settings are saved with a prefix. This removes that prefix. + * + * @param int $user_guid The user GUID. Defaults to logged in. + * @return array An array of key/value pairs. + */ + public function getAllUserSettings($user_guid = null) { + $user_guid = (int)$user_guid; + + if ($user_guid) { + $user = get_entity($user_guid); + } else { + $user = elgg_get_logged_in_user_entity(); + } + + if (!($user instanceof ElggUser)) { + return false; + } + + $db_prefix = elgg_get_config('dbprefix'); + // send an empty name so we just get the first part of the namespace + $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); + $ps_prefix_len = strlen($ps_prefix); + + // Get private settings for user + $q = "SELECT * FROM {$db_prefix}private_settings + WHERE entity_guid = {$user->guid} + AND name LIKE '$ps_prefix%'"; + + $private_settings = get_data($q); + + $return = array(); + + if ($private_settings) { + foreach ($private_settings as $setting) { + $name = substr($setting->name, $ps_prefix_len); + $value = $setting->value; + + $return[$name] = $value; + } + } + + return $return; } /** * Sets a user setting for a plugin * - * @param int $user_guid The user GUID * @param string $name The setting name * @param string $value The setting value + * @param int $user_guid The user GUID * * @return mixed The new setting ID or false */ - public function setUserSetting($user_guid, $name, $value) { + public function setUserSetting($name, $value, $user_guid = null) { + $user_guid = (int)$user_guid; + + if ($user_guid) { + $user = get_entity($user_guid); + } else { + $user = elgg_get_logged_in_user_entity(); + } + + if (!($user instanceof ElggUser)) { + return false; + } + + // Hook to validate setting + // note: this doesn't pass the namespaced name + $value = elgg_trigger_plugin_hook('usersetting', 'plugin', array( + 'user' => $user, + 'plugin' => $this, + 'plugin_id' => $this->getID(), + 'name' => $name, + 'value' => $value + ), $value); + + // set the namespaced name. $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID()); - return set_private_setting($user_guid, $name, $value); + + return set_private_setting($user->guid, $name, $value); } /** * Removes a user setting name and value. * - * @param int $user_guid The user GUID * @param string $name The user setting name - * + * @param int $user_guid The user GUID * @return bool */ - public function removeUserSetting($user_guid, $name) { + public function unsetUserSetting($name, $user_guid = null) { + $user_guid = (int)$user_guid; + + if ($user_guid) { + $user = get_entity($user_guid); + } else { + $user = elgg_get_logged_in_user_entity(); + } + + if (!($user instanceof ElggUser)) { + return false; + } + + // set the namespaced name. $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID()); - return remove_private_setting($user_guid, $name); - } + return remove_private_setting($user->guid, $name); + } /** * Removes all User Settings for this plugin @@ -378,7 +509,7 @@ class ElggPlugin extends ElggObject { * @param int $user_guid The user GUID to remove user settings. * @return bool */ - public function removeAllUserSettings($user_guid) { + public function unsetAllUserSettings($user_guid) { $db_prefix = get_config('dbprefix'); $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); @@ -389,7 +520,6 @@ class ElggPlugin extends ElggObject { return delete_data($q); } - /** * Removes this plugin's user settings for all users. * @@ -398,7 +528,7 @@ class ElggPlugin extends ElggObject { * * @return bool */ - public function removeAllUsersSettings() { + public function unsetAllUsersSettings() { $db_prefix = get_config('dbprefix'); $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); @@ -415,27 +545,29 @@ class ElggPlugin extends ElggObject { * Returns if the plugin is complete, meaning has all required files * and Elgg can read them and they make sense. * - * @todo bad name? This could be confused with isValid() from ElggPackage. + * @todo bad name? This could be confused with isValid() from ElggPluginPackage. * * @return bool */ public function isValid() { if (!$this->getID()) { + $this->errorMsg = elgg_echo('ElggPlugin:NoId', array($this->guid)); return false; } - if (!$this->package instanceof ElggPluginPackage) { + if (!$this->getPackage() instanceof ElggPluginPackage) { + $this->errorMsg = elgg_echo('ElggPlugin:NoPluginPackagePackage', array($this->getID(), $this->guid)); return false; } - if (!$this->package->isValid()) { + if (!$this->getPackage()->isValid()) { + $this->errorMsg = $this->getPackage()->getError(); return false; } return true; } - /** * Is this plugin active? * @@ -449,28 +581,34 @@ class ElggPlugin extends ElggObject { if ($site_guid) { $site = get_entity($site_guid); - - if (!($site instanceof ElggSite)) { - return false; - } } else { $site = get_config('site'); } + if (!($site instanceof ElggSite)) { + return false; + } + return check_entity_relationship($this->guid, 'active_plugin', $site->guid); } - /** * Checks if this plugin can be activated on the current * Elgg installation. * + * @todo remove $site_guid param or implement it + * * @param mixed $site_guid Optional site guid * @return bool */ public function canActivate($site_guid = null) { - if ($this->package) { - return $this->package->isValid() && $this->package->checkDependencies(); + if ($this->getPackage()) { + $result = $this->getPackage()->isValid() && $this->getPackage()->checkDependencies(); + if (!$result) { + $this->errorMsg = $this->getPackage()->getError(); + } + + return $result; } return false; @@ -508,24 +646,14 @@ class ElggPlugin extends ElggObject { // if there are any on_enable functions, start the plugin now and run them // Note: this will not run re-run the init hooks! - $functions = $this->manifest->getOnActivate(); - if ($return && $functions) { - $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES - | ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS; - - $this->start($flags); - foreach ($functions as $function) { - if (!is_callable($function)) { - $return = false; - } else { - $on_enable = call_user_func($function); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === false) ? false: true; - } - - if ($return === false) { - break; - } + if ($return) { + if ($this->canReadFile('activate.php')) { + $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES | + ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS; + + $this->start($flags); + + $return = $this->includeFile('activate.php'); } } @@ -539,7 +667,6 @@ class ElggPlugin extends ElggObject { return false; } - /** * Deactivates the plugin. * @@ -559,27 +686,10 @@ class ElggPlugin extends ElggObject { $return = elgg_trigger_event('deactivate', 'plugin', $params); - // run any deactivate functions - // check for the manifest in case we haven't fully loaded the plugin. - if ($this->manifest) { - $functions = $this->manifest->getOnDeactivate(); - } else { - $functions = array(); - } - - if ($return && $functions) { - foreach ($functions as $function) { - if (!is_callable($function)) { - $return = false; - } else { - $on_enable = call_user_func($function); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === false) ? false : true; - } - - if ($return === false) { - break; - } + // run any deactivate code + if ($return) { + if ($this->canReadFile('deactivate.php')) { + $return = $this->includeFile('deactivate.php'); } } @@ -590,7 +700,6 @@ class ElggPlugin extends ElggObject { } } - /** * Start the plugin. * @@ -599,13 +708,18 @@ class ElggPlugin extends ElggObject { * @throws PluginException */ public function start($flags) { - if (!$this->canActivate()) { - return false; - } + //if (!$this->canActivate()) { + // return false; + //} + // include classes + if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) { + $this->registerClasses(); + } + // include start file if ($flags & ELGG_PLUGIN_INCLUDE_START) { - $this->includeStart(); + $this->includeFile('start.php'); } // include views @@ -618,11 +732,6 @@ class ElggPlugin extends ElggObject { $this->registerLanguages(); } - // include classes - if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) { - $this->registerClasses(); - } - return true; } @@ -630,20 +739,39 @@ class ElggPlugin extends ElggObject { // start helpers /** - * Includes the plugin's start file + * Includes one of the plugins files + * + * @param string $filename The name of the file * * @throws PluginException - * @return true + * @return mixed The return value of the included file (or 1 if there is none) */ - protected function includeStart() { - $start = "$this->path/start.php"; - if (!include($start)) { - $msg = elgg_echo('ElggPlugin:Exception:CannotIncludeStart', - array($this->getID(), $this->guid, $this->path)); + protected function includeFile($filename) { + // This needs to be here to be backwards compatible for 1.0-1.7. + // They expect the global config object to be available in start.php. + if ($filename == 'start.php') { + global $CONFIG; + } + + $filepath = "$this->path/$filename"; + + if (!$this->canReadFile($filename)) { + $msg = elgg_echo('ElggPlugin:Exception:CannotIncludeFile', + array($filename, $this->getID(), $this->guid, $this->path)); throw new PluginException($msg); } - return true; + return include $filepath; + } + + /** + * Checks whether a plugin file with the given name exists + * + * @param string $filename The name of the file + * @return bool + */ + protected function canReadFile($filename) { + return is_readable($this->path . '/' . $filename); } /** @@ -723,12 +851,7 @@ class ElggPlugin extends ElggObject { return true; } - // but need to have working ones. - if (!elgg_register_classes($classes_path)) { - $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterClasses', - array($this->getID(), $this->guid, $classes_path)); - throw new PluginException($msg); - } + elgg_register_classes($classes_path); return true; } @@ -759,7 +882,7 @@ class ElggPlugin extends ElggObject { // 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); + $meta = $this->getPrivateSetting($name); if ($meta === false) { // Can't find it, so return null @@ -770,7 +893,9 @@ class ElggPlugin extends ElggObject { } /** - * Save a value to private settings. + * Save a value as private setting or attribute. + * + * Attributes include title and description. * * @param string $name Name * @param mixed $value Value @@ -785,11 +910,19 @@ class ElggPlugin extends ElggObject { } $this->attributes[$name] = $value; + + return true; } else { - return set_private_setting($this->guid, $name, $value); - } + // Hook to validate setting + $value = elgg_trigger_plugin_hook('setting', 'plugin', array( + 'plugin_id' => $this->pluginID, + 'plugin' => $this, + 'name' => $name, + 'value' => $value + ), $value); - return true; + return $this->setPrivateSetting($name, $value); + } } /** @@ -821,4 +954,53 @@ class ElggPlugin extends ElggObject { return remove_entity_relationship($this->guid, 'active_plugin', $site->guid); } } -}
\ No newline at end of file + + /** + * Returns the last error message registered. + * + * @return string|null + */ + public function getError() { + return $this->errorMsg; + } + + /** + * Returns this plugin's ElggPluginManifest object + * + * @return ElggPluginManifest + */ + public function getManifest() { + if ($this->manifest instanceof ElggPluginManifest) { + return $this->manifest; + } + + try { + $this->manifest = $this->getPackage()->getManifest(); + } catch (Exception $e) { + elgg_log("Failed to load manifest for plugin $this->guid. " . $e->getMessage(), 'WARNING'); + $this->errorMsg = $e->getmessage(); + } + + return $this->manifest; + } + + /** + * Returns this plugin's ElggPluginPackage object + * + * @return ElggPluginPackage + */ + public function getPackage() { + if ($this->package instanceof ElggPluginPackage) { + return $this->package; + } + + try { + $this->package = new ElggPluginPackage($this->path, false); + } catch (Exception $e) { + elgg_log("Failed to load package for $this->guid. " . $e->getMessage(), 'WARNING'); + $this->errorMsg = $e->getmessage(); + } + + return $this->package; + } +} |
