diff options
Diffstat (limited to 'core/PluginsManager.php')
-rw-r--r-- | core/PluginsManager.php | 1346 |
1 files changed, 649 insertions, 697 deletions
diff --git a/core/PluginsManager.php b/core/PluginsManager.php index de5a580d8a..5e4c7e3a5f 100644 --- a/core/PluginsManager.php +++ b/core/PluginsManager.php @@ -32,664 +32,614 @@ require_once PIWIK_INCLUDE_PATH . '/core/PluginsFunctions/Sql.php'; */ class Piwik_PluginsManager { - /** - * @var Event_Dispatcher - */ - public $dispatcher; - - protected $pluginsToLoad = array(); - - protected $doLoadPlugins = true; - protected $loadedPlugins = array(); - - protected $doLoadAlwaysActivatedPlugins = true; - protected $pluginToAlwaysActivate = array( - 'CoreHome', - 'CoreUpdater', - 'CoreAdminHome', - 'CorePluginsAdmin', - 'Installation', - 'SitesManager', - 'UsersManager', - 'API', - 'Proxy', - 'LanguagesManager', - ); - - static private $instance = null; - - /** - * Returns the singleton Piwik_PluginsManager - * - * @return Piwik_PluginsManager - */ - static public function getInstance() - { - if (self::$instance == null) - { - self::$instance = new self; - } - return self::$instance; - } - - private function __construct() - { - $this->dispatcher = Event_Dispatcher::getInstance(); - } - - /** - * Update Plugins config - * - * @param array $plugins Plugins - */ - private function updatePluginsConfig($plugins) - { - $section = Piwik_Config::getInstance()->Plugins; - $section['Plugins'] = $plugins; - Piwik_Config::getInstance()->Plugins = $section; - } - - /** - * Update Plugins_Tracker config - * - * @param array $plugins Plugins - */ - private function updatePluginsTrackerConfig($plugins) - { - $section = Piwik_Config::getInstance()->Plugins_Tracker; - $section['Plugins_Tracker'] = $plugins; - Piwik_Config::getInstance()->Plugins_Tracker = $section; - } - - /** - * Update PluginsInstalled config - * - * @param array $plugins Plugins - */ - private function updatePluginsInstalledConfig($plugins) - { - $section = Piwik_Config::getInstance()->PluginsInstalled; - $section['PluginsInstalled'] = $plugins; - Piwik_Config::getInstance()->PluginsInstalled = $section; - } - - /** - * Returns true if plugin is always activated - * - * @param string $name Name of plugin - * @return bool - */ - public function isPluginAlwaysActivated( $name ) - { - return in_array( $name, $this->pluginToAlwaysActivate); - } - - /** - * Returns true if plugin has been activated - * - * @param string $name Name of plugin - * @return bool - */ - public function isPluginActivated( $name ) - { - return in_array( $name, $this->pluginsToLoad) - || $this->isPluginAlwaysActivated( $name ); - } - - /** - * Returns true if plugin is loaded (in memory) - * - * @param string $name Name of plugin - * @return bool - */ - public function isPluginLoaded( $name ) - { - return isset($this->loadedPlugins[$name]); - } - - /** - * Reads the directories inside the plugins/ directory and returns their names in an array - * - * @return array - */ - public function readPluginsDirectory() - { - $pluginsName = _glob( PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR); - $result = array(); - if ($pluginsName != false) - { - foreach ($pluginsName as $path) - { - $name = basename($path); - if (file_exists($path.'/'.$name.'.php')) // only add folder if a Plugin/Plugin.php file exists - { - $result[] = $name; - } - } - } - return $result; - } - - /** - * Deactivate plugin - * - * @param string $pluginName Name of plugin - */ - public function deactivatePlugin($pluginName) - { - $plugins = $this->pluginsToLoad; - $key = array_search($pluginName, $plugins); - - $plugin = $this->loadPlugin($pluginName); - if ($plugin !== null) - { - $plugin->deactivate(); - } - - if($key !== false) - { - unset($plugins[$key]); - } - $this->updatePluginsConfig($plugins); - - $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker']; - if(!is_null($pluginsTracker)) - { - $key = array_search($pluginName, $pluginsTracker); - if($key !== false) - { - unset($pluginsTracker[$key]); - $this->updatePluginsTrackerConfig($pluginsTracker); - } - } - - // Delete merged js/css files to force regenerations to exclude the deactivated plugin - Piwik_Config::getInstance()->forceSave(); - Piwik::deleteAllCacheOnUpdate(); - } - - /** - * Install loaded plugins - */ - public function installLoadedPlugins() - { - foreach($this->getLoadedPlugins() as $plugin) - { - try { - $this->installPluginIfNecessary( $plugin ); - }catch(Exception $e){ - echo $e->getMessage(); - } - } - } - - /** - * Activate the specified plugin and install (if needed) - * - * @param string $pluginName Name of plugin - * @throws Exception - */ - public function activatePlugin($pluginName) - { - $plugins = Piwik_Config::getInstance()->Plugins['Plugins']; - if(in_array($pluginName, $plugins)) - { - throw new Exception("Plugin '$pluginName' already activated."); - } - - $existingPlugins = $this->readPluginsDirectory(); - if( array_search($pluginName, $existingPlugins) === false) - { - // ToDo: This fails in tracker-mode. We should log this however. - //Piwik::log(sprintf("Unable to find the plugin '%s' in activatePlugin.", $pluginName)); - return; - } - - $plugin = $this->loadPlugin($pluginName); - if ($plugin === null) - { - return; - } - - $this->installPluginIfNecessary($plugin); - - $plugin->activate(); - - // we add the plugin to the list of activated plugins - if(!in_array($pluginName, $plugins)) - { - $plugins[] = $pluginName; - } - else - { - // clean up if we find a dupe - $plugins = array_unique($plugins); - } - - // the config file will automatically be saved with the new plugin - $this->updatePluginsConfig($plugins); - Piwik_Config::getInstance()->forceSave(); - - // Delete merged js/css files to force regenerations to include the activated plugin - Piwik::deleteAllCacheOnUpdate(); - } - - /** - * Load the specified plugins - * - * @param array $pluginsToLoad Array of plugins to load - */ - public function loadPlugins( array $pluginsToLoad ) - { - // case no plugins to load - if(is_null($pluginsToLoad)) - { - $pluginsToLoad = array(); - } - $this->pluginsToLoad = $pluginsToLoad; - $this->reloadPlugins(); - } - - /** - * Disable plugin loading - */ - public function doNotLoadPlugins() - { - $this->doLoadPlugins = false; - } - - /** - * Disable loading of "always activated" plugins - */ - public function doNotLoadAlwaysActivatedPlugins() - { - $this->doLoadAlwaysActivatedPlugins = false; - } - - /** - * Load translations for loaded plugins - * - * @param bool|string $language Optional language code - */ - public function loadPluginTranslations($language = false) - { - if(empty($language)) - { - $language = Piwik_Translate::getInstance()->getLanguageToLoad(); - } - $plugins = $this->getLoadedPlugins(); - - foreach($plugins as $plugin) - { - $this->loadTranslation( $plugin, $language ); - } - } - - /** - * Execute postLoad() hook for loaded plugins - * - * @see Piwik_Plugin::postLoad() - */ - public function postLoadPlugins() - { - $plugins = $this->getLoadedPlugins(); - foreach($plugins as $plugin) - { - $plugin->postLoad(); - } - } - - /** - * Returns an array containing the plugins class names (eg. 'Piwik_UserCountry' and NOT 'UserCountry') - * - * @return array - */ - public function getLoadedPluginsName() - { - return array_map('get_class', $this->getLoadedPlugins()); - } - - /** - * Returns an array of key,value with the following format: array( - * 'UserCountry' => Piwik_Plugin $pluginObject, - * 'UserSettings' => Piwik_Plugin $pluginObject, - * ); - * - * @return array - */ - public function getLoadedPlugins() - { - return $this->loadedPlugins; - } - - /** - * Returns the given Piwik_Plugin object - * - * @param string $name - * @throws Exception - * @return array - */ - public function getLoadedPlugin($name) - { - if(!isset($this->loadedPlugins[$name])) - { - throw new Exception("The plugin '$name' has not been loaded."); - } - return $this->loadedPlugins[$name]; - } - - /** - * Load the plugins classes installed. - * Register the observers for every plugin. - */ - private function reloadPlugins() - { - $this->pluginsToLoad = array_unique($this->pluginsToLoad); - - if($this->doLoadAlwaysActivatedPlugins) - { - $this->pluginsToLoad = array_merge($this->pluginsToLoad, $this->pluginToAlwaysActivate); - } - - foreach($this->pluginsToLoad as $pluginName) - { - if(!$this->isPluginLoaded($pluginName)) - { - $newPlugin = $this->loadPlugin($pluginName); - if ($newPlugin === null) - { - continue; - } - - if($this->doLoadPlugins - && $this->isPluginActivated($pluginName)) - { - $this->addPluginObservers( $newPlugin ); - } - } - } - } - - /** - * Loads the plugin filename and instantiates the plugin with the given name, eg. UserCountry - * Do NOT give the class name ie. Piwik_UserCountry, but give the plugin name ie. UserCountry - * - * @param string $pluginName - * @throws Exception - * @return Piwik_Plugin|null - */ - public function loadPlugin( $pluginName ) - { - if(isset($this->loadedPlugins[$pluginName])) - { - return $this->loadedPlugins[$pluginName]; - } - $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName); - $pluginClassName = sprintf('Piwik_%s', $pluginName); - - if( !Piwik_Common::isValidFilename($pluginName)) - { - throw new Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName)); - } - - $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginFileName; - - if(!file_exists($path)) - { - // ToDo: We should log this - but this will crash in Tracker mode since core/Piwik is not loaded - //Piwik::log(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path)); - throw new Exception(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path)); - } - - // Don't remove this. - // Our autoloader can't find plugins/PluginName/PluginName.php - require_once $path; // prefixed by PIWIK_INCLUDE_PATH - - if(!class_exists($pluginClassName, false)) - { - throw new Exception("The class $pluginClassName couldn't be found in the file '$path'"); - } - $newPlugin = new $pluginClassName(); - - if(!($newPlugin instanceof Piwik_Plugin)) - { - throw new Exception("The plugin $pluginClassName in the file $path must inherit from Piwik_Plugin."); - } - - $this->addLoadedPlugin( $pluginName, $newPlugin); - - return $newPlugin; - } - - /** - * Unload plugin - * - * @param Piwik_Plugin $plugin - * @throws Exception - */ - public function unloadPlugin( $plugin ) - { - if(!($plugin instanceof Piwik_Plugin )) - { - $oPlugin = $this->loadPlugin( $plugin ); - if ($oPlugin === null) - { - unset($this->loadedPlugins[$plugin]); - return; - } - - $plugin = $oPlugin; - } - $hooks = $plugin->getListHooksRegistered(); - - foreach($hooks as $hookName => $methodToCall) - { - $success = $this->dispatcher->removeObserver( array( $plugin, $methodToCall), $hookName ); - if($success !== true) - { - throw new Exception("Error unloading plugin = ".$plugin->getPluginName() . ", method = $methodToCall, hook = $hookName "); - } - } - unset($this->loadedPlugins[$plugin->getPluginName()]); - } - - /** - * Unload all loaded plugins - */ - public function unloadPlugins() - { - $pluginsLoaded = $this->getLoadedPlugins(); - foreach($pluginsLoaded as $plugin) - { - $this->unloadPlugin($plugin); - } - } - - /** - * Install loaded plugins - */ - private function installPlugins() - { - foreach($this->getLoadedPlugins() as $plugin) - { - $this->installPlugin($plugin); - } - } - - /** - * Install a specific plugin - * - * @param Piwik_Plugin $plugin - * @throws Piwik_PluginsManager_PluginException if installation fails - */ - private function installPlugin( Piwik_Plugin $plugin ) - { - try{ - $plugin->install(); - } catch(Exception $e) { - throw new Piwik_PluginsManager_PluginException($plugin->getPluginName(), $e->getMessage()); - } - } - - - /** - * For the given plugin, add all the observers of this plugin. - * - * @param Piwik_Plugin $plugin - */ - private function addPluginObservers( Piwik_Plugin $plugin ) - { - $hooks = $plugin->getListHooksRegistered(); - - foreach($hooks as $hookName => $methodToCall) - { - $this->dispatcher->addObserver( array( $plugin, $methodToCall), $hookName ); - } - } - - /** - * Add a plugin in the loaded plugins array - * - * @param string $pluginName plugin name without prefix (eg. 'UserCountry') - * @param Piwik_Plugin $newPlugin - */ - private function addLoadedPlugin( $pluginName, Piwik_Plugin $newPlugin ) - { - $this->loadedPlugins[$pluginName] = $newPlugin; - } - - /** - * Load translation - * - * @param Piwik_Plugin $plugin - * @param string $langCode - * @throws Exception - * @return void - */ - private function loadTranslation( $plugin, $langCode ) - { - // we are in Tracker mode if Piwik_Loader is not (yet) loaded - if(!class_exists('Piwik_Loader', false)) - { - return ; - } - - $infos = $plugin->getInformation(); - if(!isset($infos['translationAvailable'])) - { - $infos['translationAvailable'] = false; - } - $translationAvailable = $infos['translationAvailable']; - - if(!$translationAvailable) - { - return; - } - - $pluginName = $plugin->getPluginName(); - - $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginName .'/lang/%s.php'; - - $defaultLangPath = sprintf($path, $langCode); - $defaultEnglishLangPath = sprintf($path, 'en'); - - $translations = array(); - - if(file_exists($defaultLangPath)) - { - require $defaultLangPath; - } - elseif(file_exists($defaultEnglishLangPath)) - { - require $defaultEnglishLangPath; - } - else - { - throw new Exception("Language file not found for the plugin '$pluginName'."); - } - Piwik_Translate::getInstance()->mergeTranslationArray($translations); - } - - /** - * Return names of installed plugins - * - * @return array - */ - public function getInstalledPluginsName() - { - $pluginNames = Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled']; - return $pluginNames; - } - - /** - * Returns names of plugins that should be loaded, but cannot be since their - * files cannot be found. - * - * @return array - */ - public function getMissingPlugins() - { - $missingPlugins = array(); - if (isset(Piwik_Config::getInstance()->Plugins['Plugins'])) - { - $plugins = Piwik_Config::getInstance()->Plugins['Plugins']; - foreach ($plugins as $pluginName) - { - // if a plugin is listed in the config, but is not loaded, it does not exist in the folder - if (!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginName)) - { - $missingPlugins[] = $pluginName; - } - } - } - return $missingPlugins; - } - - /** - * Install a plugin, if necessary - * - * @param Piwik_Plugin $plugin - */ - private function installPluginIfNecessary( Piwik_Plugin $plugin ) - { - $pluginName = $plugin->getPluginName(); - - $saveConfig = false; - - // is the plugin already installed or is it the first time we activate it? - $pluginsInstalled = $this->getInstalledPluginsName(); - if(!in_array($pluginName, $pluginsInstalled)) - { - $this->installPlugin($plugin); - $pluginsInstalled[] = $pluginName; - $this->updatePluginsInstalledConfig($pluginsInstalled); - $saveConfig = true; - } - - $information = $plugin->getInformation(); - - // if the plugin is to be loaded during the statistics logging - if(isset($information['TrackerPlugin']) - && $information['TrackerPlugin'] === true) - { - $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker']; - if(is_null($pluginsTracker)) - { - $pluginsTracker = array(); - } - if(!in_array($pluginName, $pluginsTracker)) - { - $pluginsTracker[] = $pluginName; - $this->updatePluginsTrackerConfig($pluginsTracker); - $saveConfig = true; - } - } - - if($saveConfig) - { - Piwik_Config::getInstance()->forceSave(); - } - } + /** + * @var Event_Dispatcher + */ + public $dispatcher; + + protected $pluginsToLoad = array(); + + protected $doLoadPlugins = true; + protected $loadedPlugins = array(); + + protected $doLoadAlwaysActivatedPlugins = true; + protected $pluginToAlwaysActivate = array( + 'CoreHome', + 'CoreUpdater', + 'CoreAdminHome', + 'CorePluginsAdmin', + 'Installation', + 'SitesManager', + 'UsersManager', + 'API', + 'Proxy', + 'LanguagesManager', + ); + + static private $instance = null; + + /** + * Returns the singleton Piwik_PluginsManager + * + * @return Piwik_PluginsManager + */ + static public function getInstance() + { + if (self::$instance == null) { + self::$instance = new self; + } + return self::$instance; + } + + private function __construct() + { + $this->dispatcher = Event_Dispatcher::getInstance(); + } + + /** + * Update Plugins config + * + * @param array $plugins Plugins + */ + private function updatePluginsConfig($plugins) + { + $section = Piwik_Config::getInstance()->Plugins; + $section['Plugins'] = $plugins; + Piwik_Config::getInstance()->Plugins = $section; + } + + /** + * Update Plugins_Tracker config + * + * @param array $plugins Plugins + */ + private function updatePluginsTrackerConfig($plugins) + { + $section = Piwik_Config::getInstance()->Plugins_Tracker; + $section['Plugins_Tracker'] = $plugins; + Piwik_Config::getInstance()->Plugins_Tracker = $section; + } + + /** + * Update PluginsInstalled config + * + * @param array $plugins Plugins + */ + private function updatePluginsInstalledConfig($plugins) + { + $section = Piwik_Config::getInstance()->PluginsInstalled; + $section['PluginsInstalled'] = $plugins; + Piwik_Config::getInstance()->PluginsInstalled = $section; + } + + /** + * Returns true if plugin is always activated + * + * @param string $name Name of plugin + * @return bool + */ + public function isPluginAlwaysActivated($name) + { + return in_array($name, $this->pluginToAlwaysActivate); + } + + /** + * Returns true if plugin has been activated + * + * @param string $name Name of plugin + * @return bool + */ + public function isPluginActivated($name) + { + return in_array($name, $this->pluginsToLoad) + || $this->isPluginAlwaysActivated($name); + } + + /** + * Returns true if plugin is loaded (in memory) + * + * @param string $name Name of plugin + * @return bool + */ + public function isPluginLoaded($name) + { + return isset($this->loadedPlugins[$name]); + } + + /** + * Reads the directories inside the plugins/ directory and returns their names in an array + * + * @return array + */ + public function readPluginsDirectory() + { + $pluginsName = _glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR); + $result = array(); + if ($pluginsName != false) { + foreach ($pluginsName as $path) { + $name = basename($path); + if (file_exists($path . '/' . $name . '.php')) // only add folder if a Plugin/Plugin.php file exists + { + $result[] = $name; + } + } + } + return $result; + } + + /** + * Deactivate plugin + * + * @param string $pluginName Name of plugin + */ + public function deactivatePlugin($pluginName) + { + $plugins = $this->pluginsToLoad; + $key = array_search($pluginName, $plugins); + + $plugin = $this->loadPlugin($pluginName); + if ($plugin !== null) { + $plugin->deactivate(); + } + + if ($key !== false) { + unset($plugins[$key]); + } + $this->updatePluginsConfig($plugins); + + $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker']; + if (!is_null($pluginsTracker)) { + $key = array_search($pluginName, $pluginsTracker); + if ($key !== false) { + unset($pluginsTracker[$key]); + $this->updatePluginsTrackerConfig($pluginsTracker); + } + } + + // Delete merged js/css files to force regenerations to exclude the deactivated plugin + Piwik_Config::getInstance()->forceSave(); + Piwik::deleteAllCacheOnUpdate(); + } + + /** + * Install loaded plugins + */ + public function installLoadedPlugins() + { + foreach ($this->getLoadedPlugins() as $plugin) { + try { + $this->installPluginIfNecessary($plugin); + } catch (Exception $e) { + echo $e->getMessage(); + } + } + } + + /** + * Activate the specified plugin and install (if needed) + * + * @param string $pluginName Name of plugin + * @throws Exception + */ + public function activatePlugin($pluginName) + { + $plugins = Piwik_Config::getInstance()->Plugins['Plugins']; + if (in_array($pluginName, $plugins)) { + throw new Exception("Plugin '$pluginName' already activated."); + } + + $existingPlugins = $this->readPluginsDirectory(); + if (array_search($pluginName, $existingPlugins) === false) { + // ToDo: This fails in tracker-mode. We should log this however. + //Piwik::log(sprintf("Unable to find the plugin '%s' in activatePlugin.", $pluginName)); + return; + } + + $plugin = $this->loadPlugin($pluginName); + if ($plugin === null) { + return; + } + + $this->installPluginIfNecessary($plugin); + + $plugin->activate(); + + // we add the plugin to the list of activated plugins + if (!in_array($pluginName, $plugins)) { + $plugins[] = $pluginName; + } else { + // clean up if we find a dupe + $plugins = array_unique($plugins); + } + + // the config file will automatically be saved with the new plugin + $this->updatePluginsConfig($plugins); + Piwik_Config::getInstance()->forceSave(); + + // Delete merged js/css files to force regenerations to include the activated plugin + Piwik::deleteAllCacheOnUpdate(); + } + + /** + * Load the specified plugins + * + * @param array $pluginsToLoad Array of plugins to load + */ + public function loadPlugins(array $pluginsToLoad) + { + // case no plugins to load + if (is_null($pluginsToLoad)) { + $pluginsToLoad = array(); + } + $this->pluginsToLoad = $pluginsToLoad; + $this->reloadPlugins(); + } + + /** + * Disable plugin loading + */ + public function doNotLoadPlugins() + { + $this->doLoadPlugins = false; + } + + /** + * Disable loading of "always activated" plugins + */ + public function doNotLoadAlwaysActivatedPlugins() + { + $this->doLoadAlwaysActivatedPlugins = false; + } + + /** + * Load translations for loaded plugins + * + * @param bool|string $language Optional language code + */ + public function loadPluginTranslations($language = false) + { + if (empty($language)) { + $language = Piwik_Translate::getInstance()->getLanguageToLoad(); + } + $plugins = $this->getLoadedPlugins(); + + foreach ($plugins as $plugin) { + $this->loadTranslation($plugin, $language); + } + } + + /** + * Execute postLoad() hook for loaded plugins + * + * @see Piwik_Plugin::postLoad() + */ + public function postLoadPlugins() + { + $plugins = $this->getLoadedPlugins(); + foreach ($plugins as $plugin) { + $plugin->postLoad(); + } + } + + /** + * Returns an array containing the plugins class names (eg. 'Piwik_UserCountry' and NOT 'UserCountry') + * + * @return array + */ + public function getLoadedPluginsName() + { + return array_map('get_class', $this->getLoadedPlugins()); + } + + /** + * Returns an array of key,value with the following format: array( + * 'UserCountry' => Piwik_Plugin $pluginObject, + * 'UserSettings' => Piwik_Plugin $pluginObject, + * ); + * + * @return array + */ + public function getLoadedPlugins() + { + return $this->loadedPlugins; + } + + /** + * Returns the given Piwik_Plugin object + * + * @param string $name + * @throws Exception + * @return array + */ + public function getLoadedPlugin($name) + { + if (!isset($this->loadedPlugins[$name])) { + throw new Exception("The plugin '$name' has not been loaded."); + } + return $this->loadedPlugins[$name]; + } + + /** + * Load the plugins classes installed. + * Register the observers for every plugin. + */ + private function reloadPlugins() + { + $this->pluginsToLoad = array_unique($this->pluginsToLoad); + + if ($this->doLoadAlwaysActivatedPlugins) { + $this->pluginsToLoad = array_merge($this->pluginsToLoad, $this->pluginToAlwaysActivate); + } + + foreach ($this->pluginsToLoad as $pluginName) { + if (!$this->isPluginLoaded($pluginName)) { + $newPlugin = $this->loadPlugin($pluginName); + if ($newPlugin === null) { + continue; + } + + if ($this->doLoadPlugins + && $this->isPluginActivated($pluginName) + ) { + $this->addPluginObservers($newPlugin); + } + } + } + } + + /** + * Loads the plugin filename and instantiates the plugin with the given name, eg. UserCountry + * Do NOT give the class name ie. Piwik_UserCountry, but give the plugin name ie. UserCountry + * + * @param string $pluginName + * @throws Exception + * @return Piwik_Plugin|null + */ + public function loadPlugin($pluginName) + { + if (isset($this->loadedPlugins[$pluginName])) { + return $this->loadedPlugins[$pluginName]; + } + $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName); + $pluginClassName = sprintf('Piwik_%s', $pluginName); + + if (!Piwik_Common::isValidFilename($pluginName)) { + throw new Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName)); + } + + $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginFileName; + + if (!file_exists($path)) { + // ToDo: We should log this - but this will crash in Tracker mode since core/Piwik is not loaded + //Piwik::log(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path)); + throw new Exception(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path)); + } + + // Don't remove this. + // Our autoloader can't find plugins/PluginName/PluginName.php + require_once $path; // prefixed by PIWIK_INCLUDE_PATH + + if (!class_exists($pluginClassName, false)) { + throw new Exception("The class $pluginClassName couldn't be found in the file '$path'"); + } + $newPlugin = new $pluginClassName(); + + if (!($newPlugin instanceof Piwik_Plugin)) { + throw new Exception("The plugin $pluginClassName in the file $path must inherit from Piwik_Plugin."); + } + + $this->addLoadedPlugin($pluginName, $newPlugin); + + return $newPlugin; + } + + /** + * Unload plugin + * + * @param Piwik_Plugin $plugin + * @throws Exception + */ + public function unloadPlugin($plugin) + { + if (!($plugin instanceof Piwik_Plugin)) { + $oPlugin = $this->loadPlugin($plugin); + if ($oPlugin === null) { + unset($this->loadedPlugins[$plugin]); + return; + } + + $plugin = $oPlugin; + } + $hooks = $plugin->getListHooksRegistered(); + + foreach ($hooks as $hookName => $methodToCall) { + $success = $this->dispatcher->removeObserver(array($plugin, $methodToCall), $hookName); + if ($success !== true) { + throw new Exception("Error unloading plugin = " . $plugin->getPluginName() . ", method = $methodToCall, hook = $hookName "); + } + } + unset($this->loadedPlugins[$plugin->getPluginName()]); + } + + /** + * Unload all loaded plugins + */ + public function unloadPlugins() + { + $pluginsLoaded = $this->getLoadedPlugins(); + foreach ($pluginsLoaded as $plugin) { + $this->unloadPlugin($plugin); + } + } + + /** + * Install loaded plugins + */ + private function installPlugins() + { + foreach ($this->getLoadedPlugins() as $plugin) { + $this->installPlugin($plugin); + } + } + + /** + * Install a specific plugin + * + * @param Piwik_Plugin $plugin + * @throws Piwik_PluginsManager_PluginException if installation fails + */ + private function installPlugin(Piwik_Plugin $plugin) + { + try { + $plugin->install(); + } catch (Exception $e) { + throw new Piwik_PluginsManager_PluginException($plugin->getPluginName(), $e->getMessage()); + } + } + + + /** + * For the given plugin, add all the observers of this plugin. + * + * @param Piwik_Plugin $plugin + */ + private function addPluginObservers(Piwik_Plugin $plugin) + { + $hooks = $plugin->getListHooksRegistered(); + + foreach ($hooks as $hookName => $methodToCall) { + $this->dispatcher->addObserver(array($plugin, $methodToCall), $hookName); + } + } + + /** + * Add a plugin in the loaded plugins array + * + * @param string $pluginName plugin name without prefix (eg. 'UserCountry') + * @param Piwik_Plugin $newPlugin + */ + private function addLoadedPlugin($pluginName, Piwik_Plugin $newPlugin) + { + $this->loadedPlugins[$pluginName] = $newPlugin; + } + + /** + * Load translation + * + * @param Piwik_Plugin $plugin + * @param string $langCode + * @throws Exception + * @return void + */ + private function loadTranslation($plugin, $langCode) + { + // we are in Tracker mode if Piwik_Loader is not (yet) loaded + if (!class_exists('Piwik_Loader', false)) { + return; + } + + $infos = $plugin->getInformation(); + if (!isset($infos['translationAvailable'])) { + $infos['translationAvailable'] = false; + } + $translationAvailable = $infos['translationAvailable']; + + if (!$translationAvailable) { + return; + } + + $pluginName = $plugin->getPluginName(); + + $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginName . '/lang/%s.php'; + + $defaultLangPath = sprintf($path, $langCode); + $defaultEnglishLangPath = sprintf($path, 'en'); + + $translations = array(); + + if (file_exists($defaultLangPath)) { + require $defaultLangPath; + } elseif (file_exists($defaultEnglishLangPath)) { + require $defaultEnglishLangPath; + } else { + throw new Exception("Language file not found for the plugin '$pluginName'."); + } + Piwik_Translate::getInstance()->mergeTranslationArray($translations); + } + + /** + * Return names of installed plugins + * + * @return array + */ + public function getInstalledPluginsName() + { + $pluginNames = Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled']; + return $pluginNames; + } + + /** + * Returns names of plugins that should be loaded, but cannot be since their + * files cannot be found. + * + * @return array + */ + public function getMissingPlugins() + { + $missingPlugins = array(); + if (isset(Piwik_Config::getInstance()->Plugins['Plugins'])) { + $plugins = Piwik_Config::getInstance()->Plugins['Plugins']; + foreach ($plugins as $pluginName) { + // if a plugin is listed in the config, but is not loaded, it does not exist in the folder + if (!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginName)) { + $missingPlugins[] = $pluginName; + } + } + } + return $missingPlugins; + } + + /** + * Install a plugin, if necessary + * + * @param Piwik_Plugin $plugin + */ + private function installPluginIfNecessary(Piwik_Plugin $plugin) + { + $pluginName = $plugin->getPluginName(); + + $saveConfig = false; + + // is the plugin already installed or is it the first time we activate it? + $pluginsInstalled = $this->getInstalledPluginsName(); + if (!in_array($pluginName, $pluginsInstalled)) { + $this->installPlugin($plugin); + $pluginsInstalled[] = $pluginName; + $this->updatePluginsInstalledConfig($pluginsInstalled); + $saveConfig = true; + } + + $information = $plugin->getInformation(); + + // if the plugin is to be loaded during the statistics logging + if (isset($information['TrackerPlugin']) + && $information['TrackerPlugin'] === true + ) { + $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker']; + if (is_null($pluginsTracker)) { + $pluginsTracker = array(); + } + if (!in_array($pluginName, $pluginsTracker)) { + $pluginsTracker[] = $pluginName; + $this->updatePluginsTrackerConfig($pluginsTracker); + $saveConfig = true; + } + } + + if ($saveConfig) { + Piwik_Config::getInstance()->forceSave(); + } + } } /** @@ -698,41 +648,41 @@ class Piwik_PluginsManager */ class Piwik_PluginsManager_PluginException extends Exception { - function __construct($pluginName, $message) - { - parent::__construct("There was a problem installing the plugin ". $pluginName . ": " . $message. " + function __construct($pluginName, $message) + { + parent::__construct("There was a problem installing the plugin " . $pluginName . ": " . $message . " If this plugin has already been installed, and if you want to hide this message</b>, you must add the following line under the [PluginsInstalled] entry in your config/config.ini.php file: - PluginsInstalled[] = $pluginName" ); - } + PluginsInstalled[] = $pluginName"); + } } /** * Post an event to the dispatcher which will notice the observers * - * @param string $eventName The event name - * @param mixed $object Object, array or string that the listeners can read and/or modify. + * @param string $eventName The event name + * @param mixed $object Object, array or string that the listeners can read and/or modify. * Listeners can call $object =& $notification->getNotificationObject(); to fetch and then modify this variable. - * @param array $info Additional array of data that can be used by the listeners, but not edited - * @param bool $pending Should the notification be posted to plugins that register after the notification was sent? + * @param array $info Additional array of data that can be used by the listeners, but not edited + * @param bool $pending Should the notification be posted to plugins that register after the notification was sent? * @return void */ -function Piwik_PostEvent( $eventName, &$object = null, $info = array(), $pending = false ) +function Piwik_PostEvent($eventName, &$object = null, $info = array(), $pending = false) { - $notification = new Piwik_Event_Notification($object, $eventName, $info); - Piwik_PluginsManager::getInstance()->dispatcher->postNotification( $notification, $pending, $bubble = false ); + $notification = new Piwik_Event_Notification($object, $eventName, $info); + Piwik_PluginsManager::getInstance()->dispatcher->postNotification($notification, $pending, $bubble = false); } /** * Register an action to execute for a given event * - * @param string $hookName Name of event - * @param function $function Callback hook + * @param string $hookName Name of event + * @param function $function Callback hook */ -function Piwik_AddAction( $hookName, $function ) +function Piwik_AddAction($hookName, $function) { - Piwik_PluginsManager::getInstance()->dispatcher->addObserver( $function, $hookName ); + Piwik_PluginsManager::getInstance()->dispatcher->addObserver($function, $hookName); } /** @@ -745,27 +695,29 @@ function Piwik_AddAction( $hookName, $function ) */ class Piwik_Event_Notification extends Event_Notification { - static $showProfiler = false; - - /** - * Use notification counter to profile runtime execution - * time and memory usage. - */ - function increaseNotificationCount(/* array($className|object, $method) */) { - parent::increaseNotificationCount(); - if(self::$showProfiler && func_num_args() == 1) - { - $callback = func_get_arg(0); - if(is_array($callback)) { - $className = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; - $method = $callback[1]; - - echo "after $className -> $method <br />"; - echo "-"; Piwik::printTimer(); - echo "<br />"; - echo "-"; Piwik::printMemoryLeak(); - echo "<br />"; - } - } - } + static $showProfiler = false; + + /** + * Use notification counter to profile runtime execution + * time and memory usage. + */ + function increaseNotificationCount( /* array($className|object, $method) */) + { + parent::increaseNotificationCount(); + if (self::$showProfiler && func_num_args() == 1) { + $callback = func_get_arg(0); + if (is_array($callback)) { + $className = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; + $method = $callback[1]; + + echo "after $className -> $method <br />"; + echo "-"; + Piwik::printTimer(); + echo "<br />"; + echo "-"; + Piwik::printMemoryLeak(); + echo "<br />"; + } + } + } } |