update Piwik to version 2.16 (fixes #91)
This commit is contained in:
parent
296343bf3b
commit
d885a4baa9
5833 changed files with 418860 additions and 226988 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
namespace Piwik;
|
||||
|
||||
use Piwik\Container\StaticContainer;
|
||||
use Piwik\Plugin\Dependency;
|
||||
use Piwik\Plugin\MetadataLoader;
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/Plugin/MetadataLoader.php';
|
|||
|
||||
/**
|
||||
* Base class of all Plugin Descriptor classes.
|
||||
*
|
||||
*
|
||||
* Any plugin that wants to add event observers to one of Piwik's {@hook # hooks},
|
||||
* or has special installation/uninstallation logic must implement this class.
|
||||
* Plugins that can specify everything they need to in the _plugin.json_ files,
|
||||
|
|
@ -26,16 +27,16 @@ require_once PIWIK_INCLUDE_PATH . '/core/Plugin/MetadataLoader.php';
|
|||
*
|
||||
* Class implementations should be named after the plugin they are a part of
|
||||
* (eg, `class UserCountry extends Plugin`).
|
||||
*
|
||||
*
|
||||
* ### Plugin Metadata
|
||||
*
|
||||
*
|
||||
* In addition to providing a place for plugins to install/uninstall themselves
|
||||
* and add event observers, this class is also responsible for loading metadata
|
||||
* found in the plugin.json file.
|
||||
*
|
||||
*
|
||||
* The plugin.json file must exist in the root directory of a plugin. It can
|
||||
* contain the following information:
|
||||
*
|
||||
*
|
||||
* - **description**: An internationalized string description of what the plugin
|
||||
* does.
|
||||
* - **homepage**: The URL to the plugin's website.
|
||||
|
|
@ -44,18 +45,18 @@ require_once PIWIK_INCLUDE_PATH . '/core/Plugin/MetadataLoader.php';
|
|||
* - **license_homepage**: URL to website describing the license used.
|
||||
* - **version**: The plugin version (eg, 1.0.1).
|
||||
* - **theme**: `true` or `false`. If `true`, the plugin will be treated as a theme.
|
||||
*
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
*
|
||||
* **How to extend**
|
||||
*
|
||||
*
|
||||
* use Piwik\Common;
|
||||
* use Piwik\Plugin;
|
||||
* use Piwik\Db;
|
||||
*
|
||||
*
|
||||
* class MyPlugin extends Plugin
|
||||
* {
|
||||
* public function getListHooksRegistered()
|
||||
* public function registerEvents()
|
||||
* {
|
||||
* return array(
|
||||
* 'API.getReportMetadata' => 'getReportMetadata',
|
||||
|
|
@ -65,17 +66,17 @@ require_once PIWIK_INCLUDE_PATH . '/core/Plugin/MetadataLoader.php';
|
|||
* )
|
||||
* );
|
||||
* }
|
||||
*
|
||||
*
|
||||
* public function install()
|
||||
* {
|
||||
* Db::exec("CREATE TABLE " . Common::prefixTable('mytable') . "...");
|
||||
* }
|
||||
*
|
||||
*
|
||||
* public function uninstall()
|
||||
* {
|
||||
* Db::exec("DROP TABLE IF EXISTS " . Common::prefixTable('mytable'));
|
||||
* }
|
||||
*
|
||||
*
|
||||
* public function getReportMetadata(&$metadata)
|
||||
* {
|
||||
* // ...
|
||||
|
|
@ -86,7 +87,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/Plugin/MetadataLoader.php';
|
|||
* // ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class Plugin
|
||||
|
|
@ -105,6 +106,15 @@ class Plugin
|
|||
*/
|
||||
private $pluginInformation;
|
||||
|
||||
/**
|
||||
* As the cache is used quite often we avoid having to create instances all the time. We reuse it which is not
|
||||
* perfect but efficient. If the cache is used we need to make sure to call setId() before usage as there
|
||||
* is maybe a different key set since last usage.
|
||||
*
|
||||
* @var \Piwik\Cache\Eager
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
|
@ -121,11 +131,27 @@ class Plugin
|
|||
}
|
||||
$this->pluginName = $pluginName;
|
||||
|
||||
$metadataLoader = new MetadataLoader($pluginName);
|
||||
$this->pluginInformation = $metadataLoader->load();
|
||||
$cacheId = 'Plugin' . $pluginName . 'Metadata';
|
||||
$cache = Cache::getEagerCache();
|
||||
|
||||
if ($this->hasDefinedPluginInformationInPluginClass() && $metadataLoader->hasPluginJson()) {
|
||||
throw new \Exception('Plugin ' . $pluginName . ' has defined the method getInformation() and as well as having a plugin.json file. Please delete the getInformation() method from the plugin class. Alternatively, you may delete the plugin directory from plugins/' . $pluginName);
|
||||
if ($cache->contains($cacheId)) {
|
||||
$this->pluginInformation = $cache->fetch($cacheId);
|
||||
} else {
|
||||
$metadataLoader = new MetadataLoader($pluginName);
|
||||
$this->pluginInformation = $metadataLoader->load();
|
||||
|
||||
if ($this->hasDefinedPluginInformationInPluginClass() && $metadataLoader->hasPluginJson()) {
|
||||
throw new \Exception('Plugin ' . $pluginName . ' has defined the method getInformation() and as well as having a plugin.json file. Please delete the getInformation() method from the plugin class. Alternatively, you may delete the plugin directory from plugins/' . $pluginName);
|
||||
}
|
||||
|
||||
$cache->save($cacheId, $this->pluginInformation);
|
||||
}
|
||||
}
|
||||
|
||||
private function createCacheIfNeeded()
|
||||
{
|
||||
if (is_null($this->cache)) {
|
||||
$this->cache = Cache::getEagerCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +173,7 @@ class Plugin
|
|||
|
||||
/**
|
||||
* Returns plugin information, including:
|
||||
*
|
||||
*
|
||||
* - 'description' => string // 1-2 sentence description of the plugin
|
||||
* - 'author' => string // plugin author
|
||||
* - 'author_homepage' => string // author homepage URL (or email "mailto:youremail@example.org")
|
||||
|
|
@ -166,12 +192,12 @@ class Plugin
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of hooks with associated event observers.
|
||||
*
|
||||
* Returns a list of events with associated event observers.
|
||||
*
|
||||
* Derived classes should use this method to associate callbacks with events.
|
||||
*
|
||||
* @return array eg,
|
||||
*
|
||||
*
|
||||
* array(
|
||||
* 'API.getReportMetadata' => 'myPluginFunction',
|
||||
* 'Another.event' => array(
|
||||
|
|
@ -183,10 +209,20 @@ class Plugin
|
|||
* 'before' => true // execute before callbacks w/o ordering
|
||||
* )
|
||||
* )
|
||||
* @since 2.15.0
|
||||
*/
|
||||
public function registerEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 2.15.0 use {@link registerEvents()} instead.
|
||||
* @return array
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
{
|
||||
return array();
|
||||
return $this->registerEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,12 +237,12 @@ class Plugin
|
|||
/**
|
||||
* Installs the plugin. Derived classes should implement this class if the plugin
|
||||
* needs to:
|
||||
*
|
||||
*
|
||||
* - create tables
|
||||
* - update existing tables
|
||||
* - etc.
|
||||
*
|
||||
* @throws Exception if installation of fails for some reason.
|
||||
*
|
||||
* @throws \Exception if installation of fails for some reason.
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
|
|
@ -216,10 +252,10 @@ class Plugin
|
|||
/**
|
||||
* Uninstalls the plugins. Derived classes should implement this method if the changes
|
||||
* made in {@link install()} need to be undone during uninstallation.
|
||||
*
|
||||
*
|
||||
* In most cases, if you have an {@link install()} method, you should provide
|
||||
* an {@link uninstall()} method.
|
||||
*
|
||||
*
|
||||
* @throws \Exception if uninstallation of fails for some reason.
|
||||
*/
|
||||
public function uninstall()
|
||||
|
|
@ -276,6 +312,89 @@ class Plugin
|
|||
return $this->pluginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a component such as a Menu or Tasks within this plugin.
|
||||
*
|
||||
* @param string $componentName The name of the component you want to look for. In case you request a
|
||||
* component named 'Menu' it'll look for a file named 'Menu.php' within the
|
||||
* root of the plugin folder that implements a class named
|
||||
* Piwik\Plugin\$PluginName\Menu . If such a file exists but does not implement
|
||||
* this class it'll silently ignored.
|
||||
* @param string $expectedSubclass If not empty, a check will be performed whether a found file extends the
|
||||
* given subclass. If the requested file exists but does not extend this class
|
||||
* a warning will be shown to advice a developer to extend this certain class.
|
||||
*
|
||||
* @return \stdClass|null Null if the requested component does not exist or an instance of the found
|
||||
* component.
|
||||
*/
|
||||
public function findComponent($componentName, $expectedSubclass)
|
||||
{
|
||||
$this->createCacheIfNeeded();
|
||||
|
||||
$cacheId = 'Plugin' . $this->pluginName . $componentName . $expectedSubclass;
|
||||
|
||||
$componentFile = sprintf('%s/plugins/%s/%s.php', PIWIK_INCLUDE_PATH, $this->pluginName, $componentName);
|
||||
|
||||
if ($this->cache->contains($cacheId)) {
|
||||
$classname = $this->cache->fetch($cacheId);
|
||||
|
||||
if (empty($classname)) {
|
||||
return null; // might by "false" in case has no menu, widget, ...
|
||||
}
|
||||
|
||||
if (file_exists($componentFile)) {
|
||||
include_once $componentFile;
|
||||
}
|
||||
} else {
|
||||
$this->cache->save($cacheId, false); // prevent from trying to load over and over again for instance if there is no Menu for a plugin
|
||||
|
||||
if (!file_exists($componentFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
require_once $componentFile;
|
||||
|
||||
$classname = sprintf('Piwik\\Plugins\\%s\\%s', $this->pluginName, $componentName);
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!empty($expectedSubclass) && !is_subclass_of($classname, $expectedSubclass)) {
|
||||
Log::warning(sprintf('Cannot use component %s for plugin %s, class %s does not extend %s',
|
||||
$componentName, $this->pluginName, $classname, $expectedSubclass));
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->cache->save($cacheId, $classname);
|
||||
}
|
||||
|
||||
return StaticContainer::get($classname);
|
||||
}
|
||||
|
||||
public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass)
|
||||
{
|
||||
$this->createCacheIfNeeded();
|
||||
|
||||
$cacheId = 'Plugin' . $this->pluginName . $directoryWithinPlugin . $expectedSubclass;
|
||||
|
||||
if ($this->cache->contains($cacheId)) {
|
||||
$components = $this->cache->fetch($cacheId);
|
||||
|
||||
if ($this->includeComponents($components)) {
|
||||
return $components;
|
||||
} else {
|
||||
// problem including one cached file, refresh cache
|
||||
}
|
||||
}
|
||||
|
||||
$components = $this->doFindMultipleComponents($directoryWithinPlugin, $expectedSubclass);
|
||||
|
||||
$this->cache->save($cacheId, $components);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect whether there are any missing dependencies.
|
||||
*
|
||||
|
|
@ -315,12 +434,97 @@ class Plugin
|
|||
{
|
||||
foreach ($backtrace as $tracepoint) {
|
||||
// try and discern the plugin name
|
||||
if (isset($tracepoint['class'])
|
||||
&& preg_match("/Piwik\\\\Plugins\\\\([a-zA-Z_0-9]+)\\\\/", $tracepoint['class'], $matches)
|
||||
) {
|
||||
return $matches[1];
|
||||
if (isset($tracepoint['class'])) {
|
||||
$className = self::getPluginNameFromNamespace($tracepoint['class']);
|
||||
if ($className) {
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the plugin name from a namespace name or a fully qualified class name. Returns `false`
|
||||
* if we can't find one.
|
||||
*
|
||||
* @param string $namespaceOrClassName The namespace or class string.
|
||||
* @return string|false
|
||||
*/
|
||||
public static function getPluginNameFromNamespace($namespaceOrClassName)
|
||||
{
|
||||
if (preg_match("/Piwik\\\\Plugins\\\\([a-zA-Z_0-9]+)\\\\/", $namespaceOrClassName, $matches)) {
|
||||
return $matches[1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method in your plugin class if you want your plugin to be loaded during tracking.
|
||||
*
|
||||
* Note: If you define your own dimension or handle a tracker event, your plugin will automatically
|
||||
* be detected as a tracker plugin.
|
||||
*
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
public function isTrackerPlugin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $directoryWithinPlugin
|
||||
* @param $expectedSubclass
|
||||
* @return array
|
||||
*/
|
||||
private function doFindMultipleComponents($directoryWithinPlugin, $expectedSubclass)
|
||||
{
|
||||
$components = array();
|
||||
|
||||
$baseDir = PIWIK_INCLUDE_PATH . '/plugins/' . $this->pluginName . '/' . $directoryWithinPlugin;
|
||||
$files = Filesystem::globr($baseDir, '*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
require_once $file;
|
||||
|
||||
$fileName = str_replace(array($baseDir . '/', '.php'), '', $file);
|
||||
$klassName = sprintf('Piwik\\Plugins\\%s\\%s\\%s', $this->pluginName, $directoryWithinPlugin, str_replace('/', '\\', $fileName));
|
||||
|
||||
if (!class_exists($klassName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($expectedSubclass) && !is_subclass_of($klassName, $expectedSubclass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$klass = new \ReflectionClass($klassName);
|
||||
|
||||
if ($klass->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$components[$file] = $klassName;
|
||||
}
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $components
|
||||
* @return bool true if all files were included, false if any file cannot be read
|
||||
*/
|
||||
private function includeComponents($components)
|
||||
{
|
||||
foreach ($components as $file => $klass) {
|
||||
if (!is_readable($file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach ($components as $file => $klass) {
|
||||
include_once $file;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue