diff --git a/CHANGELOG.md b/CHANGELOG.md index e6def5b37ad..aae2491c48c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,15 @@ The Product Changelog at **[matomo.org/changelog](https://matomo.org/changelog)* ## Matomo 3.9.0 ### Breaking Changes - * `Referrers.getKeywordsForPageUrl` and `Referrers.getKeywordsForPageTitle` APIs have been deprecated and will be removed in Matomo 4.0.0 * By default, Matomo [application logs](https://matomo.org/faq/troubleshooting/faq_115/) will now be logged in `tmp/logs/matomo.log` instead of `tmp/logs/piwik.log`. This log file path can be edited in your config/config.ini.php in the INI setting `logger_file_path`. ### New Features +* It is now possible to locate plugins in a custom directory by setting an environment variable `MATOMO_PLUGIN_DIRS` or a `$GLOBALS['MATOMO_PLUGIN_DIRS']` variable in `$MATOMO_ROOT/bootstrap.php`. * It is now possible to use monolog's FingersCrossedHandler which buffers all logs and logs all of them in case of warning or error. ### New APIs +* New API methods `Piwik\Plugin\Manager::getPluginsDirectories()` and `Piwik\Plugin\Manager::getPluginDirectory($pluginName)` have been added as it is now possible to locate Matomo plugins in different directories and it should be no longer assumed a plugin is located in the "/plugins" directory. * A new tracker method `disableQueueRequest` has been added to disable queued requests which may be useful when logs are imported. * The event `LanguageManager.getAvailableLanguages` has been deprecated. Use `LanguagesManager.getAvailableLanguages` instead. diff --git a/core/API/Proxy.php b/core/API/Proxy.php index 1f2ca5a59d9..833e9053722 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -442,7 +442,7 @@ private function getRequestParametersArray($requiredParameters, $parametersReque private function includeApiFile($fileName) { $module = self::getModuleNameFromClassName($fileName); - $path = Manager::getPluginsDirectory() . $module . '/API.php'; + $path = Manager::getPluginDirectory($module) . '/API.php'; if (is_readable($path)) { require_once $path; // prefixed by PIWIK_INCLUDE_PATH diff --git a/core/AssetManager/UIAsset/OnDiskUIAsset.php b/core/AssetManager/UIAsset/OnDiskUIAsset.php index cbd702a691d..48557480e02 100644 --- a/core/AssetManager/UIAsset/OnDiskUIAsset.php +++ b/core/AssetManager/UIAsset/OnDiskUIAsset.php @@ -10,6 +10,7 @@ use Exception; use Piwik\AssetManager\UIAsset; +use Piwik\Common; use Piwik\Filesystem; class OnDiskUIAsset extends UIAsset @@ -24,14 +25,27 @@ class OnDiskUIAsset extends UIAsset */ private $relativeLocation; + /** + * @var string + */ + private $relativeRootDir; + /** * @param string $baseDirectory * @param string $fileLocation */ - public function __construct($baseDirectory, $fileLocation) + public function __construct($baseDirectory, $fileLocation, $relativeRootDir = '') { $this->baseDirectory = $baseDirectory; $this->relativeLocation = $fileLocation; + + if (!empty($relativeRootDir) + && is_string($relativeRootDir) + && !Common::stringEndsWith($relativeRootDir, '/')) { + $relativeRootDir .= '/'; + } + + $this->relativeRootDir = $relativeRootDir; } public function getAbsoluteLocation() @@ -41,6 +55,9 @@ public function getAbsoluteLocation() public function getRelativeLocation() { + if (isset($this->relativeRootDir)) { + return $this->relativeRootDir . $this->relativeLocation; + } return $this->relativeLocation; } diff --git a/core/AssetManager/UIAssetFetcher.php b/core/AssetManager/UIAssetFetcher.php index 0b8e9b3a55a..c9086b0e713 100644 --- a/core/AssetManager/UIAssetFetcher.php +++ b/core/AssetManager/UIAssetFetcher.php @@ -9,6 +9,7 @@ namespace Piwik\AssetManager; use Piwik\AssetManager\UIAsset\OnDiskUIAsset; +use Piwik\Plugin\Manager; use Piwik\Theme; abstract class UIAssetFetcher @@ -89,9 +90,39 @@ private function initCatalog() private function populateCatalog() { + $pluginBaseDir = Manager::getPluginsDirectory(); + $pluginWebDirectories = Manager::getAlternativeWebRootDirectories(); + $matomoRootDir = $this->getBaseDirectory(); + foreach ($this->fileLocations as $fileLocation) { + $fileAbsolute = $matomoRootDir . '/' . $fileLocation; + $newUIAsset = new OnDiskUIAsset($this->getBaseDirectory(), $fileLocation); - $this->catalog->addUIAsset($newUIAsset); + if ($newUIAsset->exists()) { + $this->catalog->addUIAsset($newUIAsset); + continue; + } + + $found = false; + + if (strpos($fileAbsolute, $pluginBaseDir) === 0) { + // we iterate over all custom plugin directories only for plugin files, not libs files (not needed there) + foreach ($pluginWebDirectories as $pluginDirectory => $relative) { + $fileTest = str_replace($pluginBaseDir, $pluginDirectory, $fileAbsolute); + $newFileRelative = str_replace($pluginDirectory, '', $fileTest); + $testAsset = new OnDiskUIAsset($pluginDirectory, $newFileRelative, $relative); + if ($testAsset->exists()) { + $this->catalog->addUIAsset($testAsset); + $found = true; + break; + } + } + } + + if (!$found) { + // we add it anyway so it'll trigger an error about the missing file + $this->catalog->addUIAsset($newUIAsset); + } } } diff --git a/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php index a485a513c9b..b3cebc88410 100644 --- a/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php +++ b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php @@ -15,6 +15,7 @@ use Piwik\Common; use Piwik\Exception\StylesheetLessCompileException; use Piwik\Piwik; +use Piwik\Plugin\Manager; class StylesheetUIAssetMerger extends UIAssetMerger { @@ -191,8 +192,9 @@ private function rewriteCssImportPaths($content, $pathsRewriter) private function getCssPathsRewriter($uiAsset) { $baseDirectory = dirname($uiAsset->getRelativeLocation()); + $webDirs = Manager::getAlternativeWebRootDirectories(); - return function ($matches) use ($baseDirectory) { + return function ($matches) use ($baseDirectory, $webDirs) { $absolutePath = PIWIK_DOCUMENT_ROOT . "/$baseDirectory/" . $matches[2]; // Allow to import extension less file @@ -201,13 +203,39 @@ private function getCssPathsRewriter($uiAsset) } // Prevent from rewriting full path - $absolutePath = realpath($absolutePath); - if ($absolutePath) { + $absolutePathReal = realpath($absolutePath); + if ($absolutePathReal) { $relativePath = $baseDirectory . "/" . $matches[2]; $relativePath = str_replace('\\', '/', $relativePath); $publicPath = $matches[1] . $relativePath; } else { - $publicPath = $matches[1] . $matches[2]; + foreach ($webDirs as $absPath => $relativePath) { + if (strpos($baseDirectory, $relativePath) === 0) { + if (strpos($matches[2], '.') === 0) { + // eg ../images/ok.png + $fileRelative = $baseDirectory . '/' . $matches[2]; + $fileAbsolute = $absPath . str_replace($relativePath, '', $fileRelative); + if (file_exists($fileAbsolute)) { + return $matches[1] . $fileRelative; + } + } elseif (strpos($matches[2], 'plugins/') === 0) { + // eg plugins/Foo/images/ok.png + $fileRelative = substr($matches[2], strlen('plugins/')); + $fileAbsolute = $absPath . $fileRelative; + if (file_exists($fileAbsolute)) { + return $matches[1] . $relativePath . $fileRelative; + } + } elseif ($matches[1] === '@import "') { + $fileRelative = $baseDirectory . '/' . $matches[2]; + $fileAbsolute = $absPath . str_replace($relativePath, '', $fileRelative); + if (file_exists($fileAbsolute)) { + return $matches[1] . $baseDirectory . '/' . $matches[2]; + } + } + } + } + + $publicPath = $matches[1] . $matches[2]; } return $publicPath; diff --git a/core/Columns/Updater.php b/core/Columns/Updater.php index 1c750753ccd..fa41a19d9eb 100644 --- a/core/Columns/Updater.php +++ b/core/Columns/Updater.php @@ -342,11 +342,13 @@ public function onNoUpdateAvailable($versionsThatWereChecked) private static function getCurrentDimensionFileChanges() { - $files = Filesystem::globr(Manager::getPluginsDirectory() . '*/Columns', '*.php'); - $times = array(); - foreach ($files as $file) { - $times[$file] = filemtime($file); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $files = Filesystem::globr($pluginsDir . '*/Columns', '*.php'); + + foreach ($files as $file) { + $times[$file] = filemtime($file); + } } return $times; diff --git a/core/Container/ContainerFactory.php b/core/Container/ContainerFactory.php index e1696db1945..39d398782dd 100644 --- a/core/Container/ContainerFactory.php +++ b/core/Container/ContainerFactory.php @@ -121,7 +121,7 @@ private function addEnvironmentConfig(ContainerBuilder $builder, $environment) // add plugin environment configs $plugins = $this->pluginList->getActivatedPlugins(); foreach ($plugins as $plugin) { - $baseDir = Manager::getPluginsDirectory() . $plugin; + $baseDir = Manager::getPluginDirectory($plugin); $environmentFile = $baseDir . '/config/' . $environment . '.php'; if (file_exists($environmentFile)) { @@ -135,7 +135,7 @@ private function addPluginConfigs(ContainerBuilder $builder) $plugins = $this->pluginList->getActivatedPlugins(); foreach ($plugins as $plugin) { - $baseDir = Manager::getPluginsDirectory() . $plugin; + $baseDir = Manager::getPluginDirectory($plugin); $file = $baseDir . '/config/config.php'; if (file_exists($file)) { diff --git a/core/Plugin.php b/core/Plugin.php index fcfdc612a71..c0ac9ecb0a2 100644 --- a/core/Plugin.php +++ b/core/Plugin.php @@ -355,9 +355,9 @@ public function findComponent($componentName, $expectedSubclass) $cacheId = 'Plugin' . $this->pluginName . $componentName . $expectedSubclass; - $pluginsDir = Manager::getPluginsDirectory(); + $pluginsDir = Manager::getPluginDirectory($this->pluginName); - $componentFile = sprintf('%s%s/%s.php', $pluginsDir, $this->pluginName, $componentName); + $componentFile = sprintf('%s/%s.php', $pluginsDir, $componentName); if ($this->cache->contains($cacheId)) { $classname = $this->cache->fetch($cacheId); @@ -537,8 +537,9 @@ private function doFindMultipleComponents($directoryWithinPlugin, $expectedSubcl { $components = array(); - $pluginsDir = Manager::getPluginsDirectory(); - $baseDir = $pluginsDir . $this->pluginName . '/' . $directoryWithinPlugin; + $pluginsDir = Manager::getPluginDirectory($this->pluginName); + $baseDir = $pluginsDir . '/' . $directoryWithinPlugin; + $files = Filesystem::globr($baseDir, '*.php'); foreach ($files as $file) { diff --git a/core/Plugin/ArchivedMetric.php b/core/Plugin/ArchivedMetric.php index 05fa3d86653..f68a1f2c102 100644 --- a/core/Plugin/ArchivedMetric.php +++ b/core/Plugin/ArchivedMetric.php @@ -64,6 +64,11 @@ public function __construct(Dimension $dimension, $aggregation = false) $this->aggregation = $aggregation; } + public function getAggregation() + { + return $this->aggregation; + } + public function setDimension($dimension) { $this->dimension = $dimension; diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index d25234c7354..f48f8416fb6 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -12,11 +12,13 @@ use Piwik\Application\Kernel\PluginList; use Piwik\Cache; use Piwik\Columns\Dimension; +use Piwik\Common; use Piwik\Config; use Piwik\Config as PiwikConfig; use Piwik\Container\StaticContainer; use Piwik\Development; use Piwik\EventDispatcher; +use Piwik\Exception\Exception; use Piwik\Exception\PluginDeactivatedException; use Piwik\Filesystem; use Piwik\Log; @@ -26,7 +28,6 @@ use Piwik\Plugin\Dimension\ActionDimension; use Piwik\Plugin\Dimension\ConversionDimension; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugins\Marketplace\Api\Client; use Piwik\Settings\Storage as SettingsStorage; use Piwik\SettingsPiwik; use Piwik\Theme; @@ -52,6 +53,8 @@ public static function getInstance() protected $doLoadPlugins = true; + protected static $pluginsToPathCache = array(); + private $pluginsLoadedAndActivated; /** @@ -316,21 +319,164 @@ public function isPluginLoaded($name) */ public function readPluginsDirectory() { - $pluginsName = _glob(self::getPluginsDirectory() . '*', GLOB_ONLYDIR); $result = array(); - if ($pluginsName != false) { - foreach ($pluginsName as $path) { - if (self::pluginStructureLooksValid($path)) { - $result[] = basename($path); + foreach (self::getPluginsDirectories() as $pluginsDir) { + $pluginsName = _glob($pluginsDir . '*', GLOB_ONLYDIR); + if ($pluginsName != false) { + foreach ($pluginsName as $path) { + if (self::pluginStructureLooksValid($path)) { + $result[] = basename($path); + } } } } + + sort($result); + return $result; } + public static function initPluginDirectories() + { + $envDirs = getenv('MATOMO_PLUGIN_DIRS'); + if (!empty($envDirs)) { + // we expect it in the format `absoluteStorageDir1;webrootPathRelative1:absoluteStorageDir2;webrootPathRelative1` + if (empty($GLOBALS['MATOMO_PLUGIN_DIRS'])) { + $GLOBALS['MATOMO_PLUGIN_DIRS'] = array(); + } + + $envDirs = explode(':', $envDirs); + foreach ($envDirs as $envDir) { + $envDir = explode(';', $envDir); + $absoluteDir = rtrim($envDir[0], '/') . '/'; + $GLOBALS['MATOMO_PLUGIN_DIRS'][] = array( + 'pluginsPathAbsolute' => $absoluteDir, + 'webrootDirRelativeToMatomo' => isset($envDir[1]) ? $envDir[1] : null, + ); + } + } + + if (!empty($GLOBALS['MATOMO_PLUGIN_DIRS'])) { + foreach ($GLOBALS['MATOMO_PLUGIN_DIRS'] as $pluginDir => &$settings) { + if (!isset($settings['pluginsPathAbsolute'])) { + throw new \Exception('Missing "pluginsPathAbsolute" configuration for plugin dir'); + } + if (!isset($settings['webrootDirRelativeToMatomo'])) { + throw new \Exception('Missing "webrootDirRelativeToMatomo" configuration for plugin dir'); + } + } + + $pluginDirs = self::getPluginsDirectories(); + if (count($pluginDirs) > 1) { + spl_autoload_register(function ($className) use ($pluginDirs) { + if (strpos($className, 'Piwik\Plugins\\') === 0) { + $withoutPrefix = str_replace('Piwik\Plugins\\', '', $className); + $path = str_replace('\\', DIRECTORY_SEPARATOR, $withoutPrefix) . '.php'; + foreach ($pluginDirs as $pluginsDirectory) { + if (file_exists($pluginsDirectory . $path)) { + require_once $pluginsDirectory . $path; + } + } + } + }); + } + } + } + + public static function getAlternativeWebRootDirectories() + { + $dirs = array(); + + if (!empty($GLOBALS['MATOMO_PLUGIN_DIRS'])) { + foreach ($GLOBALS['MATOMO_PLUGIN_DIRS'] as $pluginDir) { + $absolute = rtrim($pluginDir['pluginsPathAbsolute'], '/') . '/'; + $relative = rtrim($pluginDir['webrootDirRelativeToMatomo'], '/') . '/'; + $dirs[$absolute] = $relative; + } + } + + return $dirs; + } + + /** + * Returns the path to all plugins directories. Each plugins directory may contain several plugins. + * All paths have a trailing slash '/'. + * @return string[] + * @api + */ + public static function getPluginsDirectories() + { + $dirs = array(self::getPluginsDirectory()); + + if (!empty($GLOBALS['MATOMO_PLUGIN_DIRS'])) { + $extraDirs = array_map(function ($dir) { + return rtrim($dir['pluginsPathAbsolute'], '/') . '/'; + }, $GLOBALS['MATOMO_PLUGIN_DIRS']); + $dirs = array_merge($dirs, $extraDirs); + } + + return $dirs; + } + + private static function getPluginRealPath($path) + { + if (strpos($path, '../') !== false) { + // for tests, only do it when needed re performance etc + $real = realpath($path); + if ($real && Common::stringEndsWith($path, '/')) { + return rtrim($real, '/') . '/'; + } + if ($real) { + return $real; + } + } + return $path; + } + + /** + * Gets the path to a specific plugin. If the plugin does not exist in any plugins folder, the default plugins + * folder will be assumed. + * + * @param $pluginName + * @return mixed|string + * @api + */ + public static function getPluginDirectory($pluginName) + { + if (isset(self::$pluginsToPathCache[$pluginName])) { + return self::$pluginsToPathCache[$pluginName]; + } + + $corePluginsDir = PIWIK_INCLUDE_PATH . 'plugins/' . $pluginName; + if (is_dir($corePluginsDir)) { + // for faster performance + self::$pluginsToPathCache[$pluginName] = self::getPluginRealPath($corePluginsDir); + return self::$pluginsToPathCache[$pluginName]; + } + + foreach (self::getPluginsDirectories() as $dir) { + $path = $dir . $pluginName; + if (is_dir($path)) { + self::$pluginsToPathCache[$pluginName] = self::getPluginRealPath($path); + return $path; + } + } + + // assume default directory when plugin does not exist just yet + return self::getPluginsDirectory() . $pluginName; + } + + /** + * Returns the path to the directory where core plugins are located. Please note since Matomo 3.9 + * plugins may also be located in other directories and therefore this method has been deprecated. + * @deprecated since Matomo 3.9.0 use {@link (getPluginsDirectories())} or {@link getPluginDirectory($pluginName)} instead + * @return string + */ public static function getPluginsDirectory() { - return PIWIK_INCLUDE_PATH . '/plugins/'; + $path = rtrim(PIWIK_INCLUDE_PATH, '/') . '/plugins/'; + $path = self::getPluginRealPath($path); + return $path; } /** @@ -459,8 +605,11 @@ private function clearCache($pluginName) public static function deletePluginFromFilesystem($plugin) { - $pluginDir = self::getPluginsDirectory(); - Filesystem::unlinkRecursive($pluginDir . $plugin, $deleteRootToo = true); + $pluginDir = self::getPluginDirectory($plugin); + if (strpos($pluginDir, PIWIK_INCLUDE_PATH) === 0) { + // only delete files for plugins within matomo directory... + Filesystem::unlinkRecursive($pluginDir, $deleteRootToo = true); + } } /** @@ -633,7 +782,7 @@ public function loadAllPluginsAndGetTheirInfo() 'uninstallable' => true, ); } else { - $translator->addDirectory(self::getPluginsDirectory() . $pluginName . '/lang'); + $translator->addDirectory(self::getPluginDirectory($pluginName) . '/lang'); $this->loadPlugin($pluginName); $info = array( 'activated' => $this->isPluginActivated($pluginName), @@ -694,7 +843,8 @@ public function isPluginThirdPartyAndBogus($pluginName) return true; } - $path = self::getPluginsDirectory() . $pluginName; + $path = self::getPluginDirectory($pluginName); + if (!$this->isManifestFileFound($path)) { return true; } @@ -1006,14 +1156,14 @@ public function isValidPluginName($pluginName) */ protected function makePluginClass($pluginName) { - $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName); $pluginClassName = $pluginName; if (!$this->isValidPluginName($pluginName)) { - throw new \Exception(sprintf("The plugin filename '%s' is not a valid plugin name", $pluginFileName)); + throw new \Exception(sprintf("The plugin name '%s' is not a valid plugin name", $pluginName)); } - $path = self::getPluginsDirectory() . $pluginFileName; + $path = self::getPluginDirectory($pluginName); + $path = sprintf('%s/%s.php', $path, $pluginName); if (!file_exists($path)) { // Create the smallest minimal Piwik Plugin @@ -1470,7 +1620,7 @@ public function loadPluginTranslations() /** @var Translator $translator */ $translator = StaticContainer::get('Piwik\Translation\Translator'); foreach ($this->getAllPluginsNames() as $pluginName) { - $translator->addDirectory(self::getPluginsDirectory() . $pluginName . '/lang'); + $translator->addDirectory(self::getPluginDirectory($pluginName) . '/lang'); } } } diff --git a/core/Plugin/MetadataLoader.php b/core/Plugin/MetadataLoader.php index 80eae18a80e..a28f4cfc431 100644 --- a/core/Plugin/MetadataLoader.php +++ b/core/Plugin/MetadataLoader.php @@ -133,7 +133,7 @@ private function loadJsonMetadata($path) */ private function getPathToPluginFolder() { - return \Piwik\Plugin\Manager::getPluginsDirectory() . $this->pluginName; + return \Piwik\Plugin\Manager::getPluginDirectory($this->pluginName); } /** diff --git a/core/Theme.php b/core/Theme.php index ed6261334cc..e6eb8756c9b 100644 --- a/core/Theme.php +++ b/core/Theme.php @@ -75,7 +75,8 @@ public function getJavaScriptFiles() public function rewriteAssetsPathToTheme($output) { - if ($this->themeName == \Piwik\Plugin\Manager::DEFAULT_THEME) { + if ($this->themeName == \Piwik\Plugin\Manager::DEFAULT_THEME + && !Manager::getAlternativeWebRootDirectories()) { return $output; } @@ -140,6 +141,15 @@ private function rewriteAssetPathIfOverridesFound($src) if (file_exists($fileToCheck)) { return str_replace($pathAsset, $overridingAsset, $source); } + + // not rewritten by theme, but may be located in custom webroot directory + foreach (Manager::getAlternativeWebRootDirectories() as $absDir => $webRootDirectory) { + $withoutPlugins = str_replace('plugins/', '', $pathAsset); + if (file_exists($absDir . '/' . $withoutPlugins)) { + return $webRootDirectory . $withoutPlugins; + } + } + return $source; } diff --git a/core/Twig.php b/core/Twig.php index 4b5f1e51ea2..cac86242018 100644 --- a/core/Twig.php +++ b/core/Twig.php @@ -313,9 +313,8 @@ protected function addFunction_linkTo() */ private function getDefaultThemeLoader() { - $themeLoader = new Twig_Loader_Filesystem(array( - sprintf("%s%s/templates/", Manager::getPluginsDirectory(), \Piwik\Plugin\Manager::DEFAULT_THEME) - ), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); + $themeDir = Manager::getPluginDirectory(\Piwik\Plugin\Manager::DEFAULT_THEME) . '/templates/'; + $themeLoader = new Twig_Loader_Filesystem(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); return $themeLoader; } @@ -327,13 +326,13 @@ private function getDefaultThemeLoader() */ protected function getCustomThemeLoader(Plugin $theme) { - $pluginsDir = Manager::getPluginsDirectory(); - if (!file_exists(sprintf("%s%s/templates/", $pluginsDir, $theme->getPluginName()))) { + $pluginsDir = Manager::getPluginDirectory($theme->getPluginName()); + $themeDir = $pluginsDir . '/templates/'; + + if (!file_exists($themeDir)) { return false; } - $themeLoader = new Twig_Loader_Filesystem(array( - sprintf("%s%s/templates/", $pluginsDir, $theme->getPluginName()) - ), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); + $themeLoader = new Twig_Loader_Filesystem(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); return $themeLoader; } @@ -522,11 +521,11 @@ private function addPluginNamespaces(Twig_Loader_Filesystem $loader) $pluginManager = \Piwik\Plugin\Manager::getInstance(); $plugins = $pluginManager->getAllPluginsNames(); - $pluginsDir = Manager::getPluginsDirectory(); foreach ($plugins as $name) { - $path = sprintf("%s%s/templates/", $pluginsDir, $name); + $pluginsDir = Manager::getPluginDirectory($name); + $path = sprintf("%s/templates/", $pluginsDir); if (is_dir($path)) { - $loader->addPath($pluginsDir . $name . '/templates', $name); + $loader->addPath(rtrim($path, '/'), $name); } } } @@ -541,12 +540,11 @@ private function addCustomPluginNamespaces(Twig_Loader_Filesystem $loader, $plug $pluginManager = \Piwik\Plugin\Manager::getInstance(); $plugins = $pluginManager->getAllPluginsNames(); - $pluginsDir = Manager::getPluginsDirectory(); - foreach ($plugins as $name) { - $path = sprintf("%s%s/templates/plugins/%s/", $pluginsDir, $pluginName, $name); + $pluginsDir = Manager::getPluginDirectory($name); + $path = sprintf("%s/templates/plugins/%s/", $pluginsDir, $name); if (is_dir($path)) { - $loader->addPath($pluginsDir . $pluginName . '/templates/plugins/'. $name, $name); + $loader->addPath(rtrim($path, '/'), $name); } } } diff --git a/core/Updater.php b/core/Updater.php index 9af07459b2b..85c52e309b6 100644 --- a/core/Updater.php +++ b/core/Updater.php @@ -63,7 +63,13 @@ class Updater public function __construct($pathUpdateFileCore = null, $pathUpdateFilePlugins = null, Columns\Updater $columnsUpdater = null) { $this->pathUpdateFileCore = $pathUpdateFileCore ?: PIWIK_INCLUDE_PATH . '/core/Updates/'; - $this->pathUpdateFilePlugins = $pathUpdateFilePlugins ?: Manager::getPluginsDirectory() . '%s/Updates/'; + + if ($pathUpdateFilePlugins) { + $this->pathUpdateFilePlugins = $pathUpdateFilePlugins; + } else { + $this->pathUpdateFilePlugins = null; + } + $this->columnsUpdater = $columnsUpdater ?: new Columns\Updater(); self::$activeInstance = $this; @@ -345,7 +351,11 @@ private function loadComponentsWithUpdateFile() } elseif (ColumnUpdater::isDimensionComponent($name)) { $componentsWithUpdateFile[$name][PIWIK_INCLUDE_PATH . '/core/Columns/Updater.php'] = $newVersion; } else { - $pathToUpdates = sprintf($this->pathUpdateFilePlugins, $name) . '*.php'; + if ($this->pathUpdateFilePlugins) { + $pathToUpdates = sprintf($this->pathUpdateFilePlugins, $name) . '*.php'; + } else { + $pathToUpdates = Manager::getPluginDirectory($name) . '/Updates/*.php'; + } } if (!empty($pathToUpdates)) { diff --git a/core/bootstrap.php b/core/bootstrap.php index f0cae7495e1..004ce593274 100644 --- a/core/bootstrap.php +++ b/core/bootstrap.php @@ -41,6 +41,8 @@ // Composer autoloader require_once PIWIK_VENDOR_PATH . '/autoload.php'; +\Piwik\Plugin\Manager::initPluginDirectories(); + /** * Eaccelerator does not support closures and is known to be not comptabile with Piwik. Therefore we are disabling * it automatically. At this point it looks like Eaccelerator is no longer under development and the bug has not diff --git a/plugins/CoreConsole/Commands/GenerateAngularComponent.php b/plugins/CoreConsole/Commands/GenerateAngularComponent.php index 081da16b008..c5a7de2ef54 100644 --- a/plugins/CoreConsole/Commands/GenerateAngularComponent.php +++ b/plugins/CoreConsole/Commands/GenerateAngularComponent.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -38,7 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output) . $pluginName); } - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array( 'ExamplePlugin' => $pluginName, 'example-component' => $componentLower, diff --git a/plugins/CoreConsole/Commands/GenerateAngularDirective.php b/plugins/CoreConsole/Commands/GenerateAngularDirective.php index c5c0a767c76..605f417dfd9 100644 --- a/plugins/CoreConsole/Commands/GenerateAngularDirective.php +++ b/plugins/CoreConsole/Commands/GenerateAngularDirective.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -39,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new \Exception('The AngularJS directive ' . $directiveLower . ' already exists in plugin ' . $pluginName); } - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array( 'ExamplePlugin' => $pluginName, 'directive-component' => $directiveLower, diff --git a/plugins/CoreConsole/Commands/GenerateApi.php b/plugins/CoreConsole/Commands/GenerateApi.php index 9fc3c5b7ae8..cbaaa9cdc9c 100644 --- a/plugins/CoreConsole/Commands/GenerateApi.php +++ b/plugins/CoreConsole/Commands/GenerateApi.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,7 +30,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName); $whitelistFiles = array('/API.php'); diff --git a/plugins/CoreConsole/Commands/GenerateArchiver.php b/plugins/CoreConsole/Commands/GenerateArchiver.php index 29bcee7bb6f..18e43c11e81 100644 --- a/plugins/CoreConsole/Commands/GenerateArchiver.php +++ b/plugins/CoreConsole/Commands/GenerateArchiver.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,7 +30,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName, 'EXAMPLEPLUGIN' => strtoupper($pluginName)); $whitelistFiles = array('/Archiver.php'); diff --git a/plugins/CoreConsole/Commands/GenerateCommand.php b/plugins/CoreConsole/Commands/GenerateCommand.php index 9fe95c06d3b..243a61dc75e 100644 --- a/plugins/CoreConsole/Commands/GenerateCommand.php +++ b/plugins/CoreConsole/Commands/GenerateCommand.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -32,7 +33,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $commandName = $this->getCommandName($input, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleCommand'; + $exampleFolder = Manager::getPluginDirectory('ExampleCommand'); $replace = array( 'ExampleCommandDescription' => $commandName, 'ExampleCommand' => $pluginName, diff --git a/plugins/CoreConsole/Commands/GenerateController.php b/plugins/CoreConsole/Commands/GenerateController.php index d5c3b840788..863c3fe8678 100644 --- a/plugins/CoreConsole/Commands/GenerateController.php +++ b/plugins/CoreConsole/Commands/GenerateController.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,7 +30,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName); $whitelistFiles = array('/Controller.php', '/templates', '/templates/index.twig'); diff --git a/plugins/CoreConsole/Commands/GenerateDimension.php b/plugins/CoreConsole/Commands/GenerateDimension.php index 900b6c05336..bc10670cde4 100644 --- a/plugins/CoreConsole/Commands/GenerateDimension.php +++ b/plugins/CoreConsole/Commands/GenerateDimension.php @@ -11,6 +11,7 @@ use Piwik\Common; use Piwik\DbHelper; +use Piwik\Plugin\Manager; use Piwik\Plugin\Report; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -53,7 +54,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $dimensionClassName = $this->getDimensionClassName($dimensionName); $translatedDimensionName = $this->makeTranslationIfPossible($pluginName, ucfirst($dimensionName)); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleTracker'; + $exampleFolder = Manager::getPluginDirectory('ExampleTracker'); $replace = array('example_action_dimension' => strtolower($columnName), 'example_visit_dimension' => strtolower($columnName), 'example_conversion_dimension' => strtolower($columnName), diff --git a/plugins/CoreConsole/Commands/GenerateMenu.php b/plugins/CoreConsole/Commands/GenerateMenu.php index 868a47b8f3c..e6539d7c9f6 100644 --- a/plugins/CoreConsole/Commands/GenerateMenu.php +++ b/plugins/CoreConsole/Commands/GenerateMenu.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,7 +30,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName); $whitelistFiles = array('/Menu.php'); diff --git a/plugins/CoreConsole/Commands/GeneratePlugin.php b/plugins/CoreConsole/Commands/GeneratePlugin.php index a214a40a5f1..ad29a39a940 100644 --- a/plugins/CoreConsole/Commands/GeneratePlugin.php +++ b/plugins/CoreConsole/Commands/GeneratePlugin.php @@ -47,7 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $exampleDescription = $info['description']; if ($isTheme) { - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleTheme'; + $exampleFolder = Plugin\Manager::getPluginDirectory('ExampleTheme'); $replace = array( 'ExampleTheme' => $pluginName, $exampleDescription => $description, @@ -58,7 +58,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Plugin\Manager::getPluginDirectory('ExamplePlugin'); $replace = array( 'ExamplePlugin' => $pluginName, $exampleDescription => $description, diff --git a/plugins/CoreConsole/Commands/GeneratePluginBase.php b/plugins/CoreConsole/Commands/GeneratePluginBase.php index 211dc94db3a..6d99e13a7b0 100644 --- a/plugins/CoreConsole/Commands/GeneratePluginBase.php +++ b/plugins/CoreConsole/Commands/GeneratePluginBase.php @@ -14,6 +14,7 @@ use Piwik\Filesystem; use Piwik\Plugin\ConsoleCommand; use Piwik\Plugin\Dependency; +use Piwik\Plugin\Manager; use Piwik\Version; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -27,12 +28,7 @@ public function isEnabled() public function getPluginPath($pluginName) { - return PIWIK_INCLUDE_PATH . $this->getRelativePluginPath($pluginName); - } - - private function getRelativePluginPath($pluginName) - { - return '/plugins/' . $pluginName; + return Manager::getPluginDirectory($pluginName); } private function createFolderWithinPluginIfNotExists($pluginNameOrCore, $folder) @@ -111,7 +107,7 @@ protected function makeTranslationIfPossible($pluginName, $translatedText, $tran protected function checkAndUpdateRequiredPiwikVersion($pluginName, OutputInterface $output) { $pluginJsonPath = $this->getPluginPath($pluginName) . '/plugin.json'; - $relativePluginJson = $this->getRelativePluginPath($pluginName) . '/plugin.json'; + $relativePluginJson = Manager::getPluginDirectory($pluginName) . '/plugin.json'; if (!file_exists($pluginJsonPath) || !is_writable($pluginJsonPath)) { return; @@ -326,11 +322,13 @@ protected function copyTemplateToPlugin($templateFolder, $pluginName, array $rep protected function getPluginNames() { - $pluginDirs = \_glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR); - $pluginNames = array(); - foreach ($pluginDirs as $pluginDir) { - $pluginNames[] = basename($pluginDir); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginDirs = \_glob($pluginsDir . '*', GLOB_ONLYDIR); + + foreach ($pluginDirs as $pluginDir) { + $pluginNames[] = basename($pluginDir); + } } return $pluginNames; @@ -338,15 +336,17 @@ protected function getPluginNames() protected function getPluginNamesHavingNotSpecificFile($filename) { - $pluginDirs = \_glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR); - $pluginNames = array(); - foreach ($pluginDirs as $pluginDir) { - if (!file_exists($pluginDir . '/' . $filename)) { - $pluginNames[] = basename($pluginDir); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginDirs = \_glob($pluginsDir . '*', GLOB_ONLYDIR); + + foreach ($pluginDirs as $pluginDir) { + if (!file_exists($pluginDir . '/' . $filename)) { + $pluginNames[] = basename($pluginDir); + } } - } + } return $pluginNames; } diff --git a/plugins/CoreConsole/Commands/GenerateReport.php b/plugins/CoreConsole/Commands/GenerateReport.php index 1faa1c9dc6c..b30ec7d61e3 100644 --- a/plugins/CoreConsole/Commands/GenerateReport.php +++ b/plugins/CoreConsole/Commands/GenerateReport.php @@ -45,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $order = $this->getOrder($category); $apiName = $this->getApiName($reportName); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleReport'; + $exampleFolder = Manager::getPluginDirectory('ExampleReport'); $replace = array('GetExampleReport' => ucfirst($apiName), 'getExampleReport' => lcfirst($apiName), 'getApiReport' => lcfirst($apiName), diff --git a/plugins/CoreConsole/Commands/GenerateScheduledTask.php b/plugins/CoreConsole/Commands/GenerateScheduledTask.php index 8337e7146f9..2317805aec2 100644 --- a/plugins/CoreConsole/Commands/GenerateScheduledTask.php +++ b/plugins/CoreConsole/Commands/GenerateScheduledTask.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,7 +30,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName); $whitelistFiles = array('/Tasks.php'); diff --git a/plugins/CoreConsole/Commands/GenerateSettings.php b/plugins/CoreConsole/Commands/GenerateSettings.php index a5b0c4e0776..245446fa6a7 100644 --- a/plugins/CoreConsole/Commands/GenerateSettings.php +++ b/plugins/CoreConsole/Commands/GenerateSettings.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -33,7 +34,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $pluginName = $this->getPluginName($input, $output, $settingsType, $settingsFilename); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleSettingsPlugin'; + $exampleFolder = Manager::getPluginDirectory('ExampleSettingsPlugin'); $replace = array('ExampleSettingsPlugin' => $pluginName); $whitelistFiles = array('/' . $settingsFilename); diff --git a/plugins/CoreConsole/Commands/GenerateTest.php b/plugins/CoreConsole/Commands/GenerateTest.php index 95dac4cfbd7..b489c74d1d5 100644 --- a/plugins/CoreConsole/Commands/GenerateTest.php +++ b/plugins/CoreConsole/Commands/GenerateTest.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Common; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -33,7 +34,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $testType = $this->getTestType($input, $output); $testName = $this->getTestName($input, $output, $testType); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array( 'ExamplePlugin' => $pluginName, 'SimpleTest' => $testName, diff --git a/plugins/CoreConsole/Commands/GenerateUpdate.php b/plugins/CoreConsole/Commands/GenerateUpdate.php index 826448de38e..9dffe019f2c 100644 --- a/plugins/CoreConsole/Commands/GenerateUpdate.php +++ b/plugins/CoreConsole/Commands/GenerateUpdate.php @@ -33,7 +33,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $namespace = $this->getNamespace($component); $className = $this->getUpdateClassName($component, $version); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Plugin\Manager::getPluginDirectory('ExamplePlugin'); $replace = array('Piwik\Plugins\ExamplePlugin\Updates' => $namespace, 'ExamplePlugin' => $component, 'Updates_0_0_2' => $className, diff --git a/plugins/CoreConsole/Commands/GenerateVisualizationPlugin.php b/plugins/CoreConsole/Commands/GenerateVisualizationPlugin.php index 92a50277167..807646b2c2e 100644 --- a/plugins/CoreConsole/Commands/GenerateVisualizationPlugin.php +++ b/plugins/CoreConsole/Commands/GenerateVisualizationPlugin.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; +use Piwik\Plugin\Manager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -39,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->generatePluginFolder($pluginName); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleVisualization'; + $exampleFolder = Manager::getPluginDirectory('ExampleVisualization'); $replace = array( 'SimpleTable' => $visualizationName, 'simpleTable' => lcfirst($visualizationName), diff --git a/plugins/CoreConsole/Commands/GenerateWidget.php b/plugins/CoreConsole/Commands/GenerateWidget.php index 2b603aa1fac..a66efba3c6e 100644 --- a/plugins/CoreConsole/Commands/GenerateWidget.php +++ b/plugins/CoreConsole/Commands/GenerateWidget.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Piwik; +use Piwik\Plugin\Manager; use Piwik\Translate; use Piwik\Widget\WidgetsList; use Symfony\Component\Console\Input\InputInterface; @@ -45,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $widgetMethod = $this->getWidgetMethodName($widgetName); $widgetClass = ucfirst($widgetMethod); - $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $exampleFolder = Manager::getPluginDirectory('ExamplePlugin'); $replace = array('ExamplePlugin' => $pluginName, 'MyExampleWidget' => $widgetClass, 'Example Widget Name' => $this->makeTranslationIfPossible($pluginName, $widgetName), diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index cefb7e4080d..bbcd04cb9fa 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -480,7 +480,8 @@ public function uninstall($redirectAfter = true) $uninstalled = $this->pluginManager->uninstallPlugin($pluginName); if (!$uninstalled) { - $path = Plugin\Manager::getPluginsDirectory() . $pluginName . '/'; + $path = Plugin\Manager::getPluginDirectory($pluginName) . '/'; + $messagePermissions = Filechecks::getErrorMessageMissingPermissions($path); $messageIntro = $this->translator->translate("Warning: \"%s\" could not be uninstalled. Piwik did not have enough permission to delete the files in $path. ", diff --git a/plugins/CorePluginsAdmin/PluginInstaller.php b/plugins/CorePluginsAdmin/PluginInstaller.php index 09ec89de592..78bccb88c43 100644 --- a/plugins/CorePluginsAdmin/PluginInstaller.php +++ b/plugins/CorePluginsAdmin/PluginInstaller.php @@ -129,10 +129,13 @@ public function installOrUpdatePluginFromFile($pathToZip) private function makeSureFoldersAreWritable() { - Filechecks::dieIfDirectoriesNotWritable(array( + $dirs = array( StaticContainer::get('path.tmp') . self::PATH_TO_DOWNLOAD, Manager::getPluginsDirectory() - )); + ); + // we do not require additional plugin directories to be writeable ({@link Manager::getPluginsDirectories()}) + // as we only upload to core plugins directory anyway + Filechecks::dieIfDirectoriesNotWritable($dirs); } /** diff --git a/plugins/ExampleTheme/ExampleTheme.php b/plugins/ExampleTheme/ExampleTheme.php index 0ca5b8fcf75..dd9711c64cd 100644 --- a/plugins/ExampleTheme/ExampleTheme.php +++ b/plugins/ExampleTheme/ExampleTheme.php @@ -22,9 +22,6 @@ public function getListHooksRegistered() public function configureThemeVariables(Plugin\ThemeStyles $vars) { - $vars->fontFamilyBase = 'Arial, Verdana, sans-serif'; - $vars->colorBrand = '#5793d4'; - $vars->colorHeaderBackground = '#0091ea'; - $vars->colorHeaderText = '#0d0d0d'; + $vars->fontFamilyBase = 'Verdana, sans-serif'; } } diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php index 9946da3b884..b41ba9c68b7 100644 --- a/plugins/LanguagesManager/API.php +++ b/plugins/LanguagesManager/API.php @@ -105,11 +105,18 @@ public function getAvailableLanguagesInfo($excludeNonCorePlugins=true) $data = file_get_contents(PIWIK_INCLUDE_PATH . '/lang/en.json'); $englishTranslation = json_decode($data, true); + $pluginDirectories = Manager::getPluginsDirectories(); // merge with plugin translations if any - $pluginFiles = glob(sprintf('%s*/lang/en.json', Manager::getPluginsDirectory())); + + $pluginFiles = array(); + foreach ($pluginDirectories as $pluginsDir) { + $pluginFiles = array_merge($pluginFiles, glob(sprintf('%s*/lang/en.json', $pluginsDir))); + } + foreach ($pluginFiles as $file) { + $fileWithoutPluginDir = str_replace($pluginDirectories, '', $file); - preg_match('/\/plugins\/([^\/]+)\/lang/i', $file, $matches); + preg_match('/([^\/]+)\/lang/i', $fileWithoutPluginDir, $matches); $plugin = $matches[1]; if (!$excludeNonCorePlugins || Manager::getInstance()->isPluginBundledWithCore($plugin)) { @@ -126,10 +133,15 @@ public function getAvailableLanguagesInfo($excludeNonCorePlugins=true) $translations = json_decode($data, true); // merge with plugin translations if any - $pluginFiles = glob(sprintf('%s*/lang/%s.json', Manager::getPluginsDirectory(), $filename)); + $pluginFiles = array(); + foreach ($pluginDirectories as $pluginsDir) { + $pluginFiles = array_merge($pluginFiles, glob(sprintf('%s*/lang/%s.json', $pluginsDir, $filename))); + } + foreach ($pluginFiles as $file) { + $fileWithoutPluginDir = str_replace($pluginDirectories, '', $file); - preg_match('/\/plugins\/([^\/]+)\/lang/i', $file, $matches); + preg_match('/([^\/]+)\/lang/i', $fileWithoutPluginDir, $matches); $plugin = $matches[1]; if (!$excludeNonCorePlugins || Manager::getInstance()->isPluginBundledWithCore($plugin)) { @@ -232,7 +244,7 @@ public function getPluginTranslationsForLanguage($pluginName, $languageCode) return false; } - $languageFile = Manager::getPluginsDirectory() . "$pluginName/lang/$languageCode.json"; + $languageFile = Manager::getPluginDirectory($pluginName) . "/lang/$languageCode.json"; if (!file_exists($languageFile)) { return false; diff --git a/plugins/LanguagesManager/Commands/PluginsWithTranslations.php b/plugins/LanguagesManager/Commands/PluginsWithTranslations.php index 62d13cda70a..2b24b7cacd8 100644 --- a/plugins/LanguagesManager/Commands/PluginsWithTranslations.php +++ b/plugins/LanguagesManager/Commands/PluginsWithTranslations.php @@ -27,9 +27,14 @@ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln("Following plugins contain their own translation files:"); - $pluginFiles = glob(sprintf('%s*/lang/en.json', Manager::getPluginsDirectory())); + $pluginFiles = array(); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginFiles = array_merge($pluginsDir, glob(sprintf('%s*/lang/en.json', $pluginsDir))); + } $pluginFiles = array_map(function($elem){ - return str_replace(array(Manager::getPluginsDirectory(), '/lang/en.json'), '', $elem); + $replace = Manager::getPluginsDirectories(); + $replace[] = '/lang/en.json'; + return str_replace($replace, '', $elem); }, $pluginFiles); $output->writeln(join("\n", $pluginFiles)); diff --git a/plugins/LanguagesManager/Commands/Update.php b/plugins/LanguagesManager/Commands/Update.php index 4e79b8383bb..bb9efbcc1e6 100644 --- a/plugins/LanguagesManager/Commands/Update.php +++ b/plugins/LanguagesManager/Commands/Update.php @@ -158,9 +158,14 @@ public static function getAllPlugins() return $pluginsWithTranslations; } - $pluginsWithTranslations = glob(sprintf('%s*/lang/en.json', Manager::getPluginsDirectory())); + $pluginsWithTranslations = array(); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginsWithTranslations = array_merge($pluginsWithTranslations, glob(sprintf('%s*/lang/en.json', $pluginsDir))); + } $pluginsWithTranslations = array_map(function ($elem) { - return str_replace(array(Manager::getPluginsDirectory(), '/lang/en.json'), '', $elem); + $replace = Manager::getPluginsDirectories(); + $replace[] = '/lang/en.json'; + return str_replace($replace, '', $elem); }, $pluginsWithTranslations); return $pluginsWithTranslations; @@ -188,10 +193,14 @@ public static function getPluginsInCore() $newPlugins = $matches[1]; $pluginsNotInCore = array_merge($submodulePlugins, $newPlugins); - - $pluginsWithTranslations = glob(sprintf('%s*/lang/en.json', Manager::getPluginsDirectory())); + $pluginsWithTranslations = array(); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginsWithTranslations = array_merge($pluginsWithTranslations, glob(sprintf('%s*/lang/en.json', $pluginsDir))); + } $pluginsWithTranslations = array_map(function ($elem) { - return str_replace(array(Manager::getPluginsDirectory(), '/lang/en.json'), '', $elem); + $replace = Manager::getPluginsDirectories(); + $replace[] = '/lang/en.json'; + return str_replace($replace, '', $elem); }, $pluginsWithTranslations); $pluginsInCore = array_diff($pluginsWithTranslations, $pluginsNotInCore); diff --git a/plugins/LanguagesManager/TranslationWriter/Writer.php b/plugins/LanguagesManager/TranslationWriter/Writer.php index 8dc2260cdb7..679433b34ea 100644 --- a/plugins/LanguagesManager/TranslationWriter/Writer.php +++ b/plugins/LanguagesManager/TranslationWriter/Writer.php @@ -202,7 +202,7 @@ protected function getTranslationPathBaseDirectory($base, $lang = null) if ($base == 'tmp') { return sprintf('%s/plugins/%s/lang/%s.json', StaticContainer::get('path.tmp'), $this->pluginName, $lang); } else { - return sprintf('%s%s/lang/%s.json', Manager::getPluginsDirectory(), $this->pluginName, $lang); + return sprintf('%s/lang/%s.json', Manager::getPluginDirectory($this->pluginName), $lang); } } diff --git a/tests/PHPUnit/Fixtures/UITestFixture.php b/tests/PHPUnit/Fixtures/UITestFixture.php index 49b480f793b..1c6c92b9bb4 100644 --- a/tests/PHPUnit/Fixtures/UITestFixture.php +++ b/tests/PHPUnit/Fixtures/UITestFixture.php @@ -70,6 +70,7 @@ public function setUp() self::resetPluginsInstalledConfig(); self::updateDatabase(); self::installAndActivatePlugins($this->getTestEnvironment()); + self::updateDatabase(); // make sure site has an early enough creation date (for period selector tests) Db::get()->update(Common::prefixTable("site"), @@ -127,6 +128,9 @@ public function performSetUp($setupEnvironmentOnly = false) $this->extraTestEnvVars = array( 'loadRealTranslations' => 1, ); + $this->extraPluginsToLoad = array( + 'CustomDirPlugin' + ); parent::performSetUp($setupEnvironmentOnly); diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index 444728bafd8..1fae5acbf9b 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -36,6 +36,13 @@ @set_include_path(PIWIK_INCLUDE_SEARCH_PATH); @ini_set('memory_limit', -1); +$GLOBALS['MATOMO_PLUGIN_DIRS'] = array( + array( + 'pluginsPathAbsolute' => PIWIK_INCLUDE_PATH . '/tests/resources/custompluginsdir', + 'webrootDirRelativeToMatomo' => 'tests/resources/custompluginsdir' + ), +); + require_once PIWIK_INCLUDE_PATH . '/core/bootstrap.php'; require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php'; diff --git a/tests/PHPUnit/phpunit.xml.dist b/tests/PHPUnit/phpunit.xml.dist index caf5cf2d161..0aab2c51782 100644 --- a/tests/PHPUnit/phpunit.xml.dist +++ b/tests/PHPUnit/phpunit.xml.dist @@ -23,6 +23,8 @@ ./System ../../plugins/*/tests ../../plugins/*/Test + ../../tests/resources/custompluginsdir/*/tests/System + ../../tests/resources/custompluginsdir/*/Test/System ../../plugins/*/tests/Integration ../../plugins/*/Test/Integration ../../plugins/*/tests/Unit @@ -34,6 +36,8 @@ ../../plugins/*/tests ../../plugins/*/Test + ../../tests/resources/custompluginsdir/*/tests/System + ../../tests/resources/custompluginsdir/*/Test/System ../../plugins/*/tests/Integration ../../plugins/*/Test/Integration ../../plugins/*/tests/Unit @@ -43,11 +47,15 @@ ./Integration ../../plugins/*/tests/Integration ../../plugins/*/Test/Integration + ../../tests/resources/custompluginsdir/*/tests/Integration + ../../tests/resources/custompluginsdir/*/Test/Integration ./Unit ../../plugins/*/tests/Unit ../../plugins/*/Test/Unit + ../../tests/resources/custompluginsdir/*/tests/Unit + ../../tests/resources/custompluginsdir/*/Test/Unit ../../plugins/*/tests diff --git a/tests/PHPUnit/proxy/includes.php b/tests/PHPUnit/proxy/includes.php index 2b3407f5d54..c6f301623c7 100644 --- a/tests/PHPUnit/proxy/includes.php +++ b/tests/PHPUnit/proxy/includes.php @@ -10,6 +10,13 @@ define('PIWIK_TEST_MODE', true); } +$GLOBALS['MATOMO_PLUGIN_DIRS'] = array( + array( + 'pluginsPathAbsolute' => realpath(PIWIK_INCLUDE_PATH . 'tests/resources/custompluginsdir'), + 'webrootDirRelativeToMatomo' => '../../resources/custompluginsdir' + ), +); + require_once PIWIK_INCLUDE_PATH . '/core/bootstrap.php'; Piwik\SettingsServer::setMaxExecutionTime(0); diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_admin_settings_general.png b/tests/UI/expected-screenshots/UIIntegrationTest_admin_settings_general.png index d692196c24a..065a48ac190 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_admin_settings_general.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_admin_settings_general.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3b6c8c65e4aa9a62b618825e4cde3839f119ca28b839fbf607c788f2f0c3846 -size 1071976 +oid sha256:0766ddf41affb5c22ad653a4bd364dc374ba0b70421fdad8622393d7025cd590 +size 1085586 diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_customdirplugin.png b/tests/UI/expected-screenshots/UIIntegrationTest_customdirplugin.png new file mode 100644 index 00000000000..7809e57f214 --- /dev/null +++ b/tests/UI/expected-screenshots/UIIntegrationTest_customdirplugin.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e5cf38c4abbfc04c5d1de2903bc4a242db8e9a6a8c7be2bf2fd99d24f89808e +size 66157 diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_fatal_error_safemode.png b/tests/UI/expected-screenshots/UIIntegrationTest_fatal_error_safemode.png index aacf0c3d473..c3779e6ac18 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_fatal_error_safemode.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_fatal_error_safemode.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68503ef9dafa19792aba12552ab81872b25247b619851d0ba1b743b31a51c919 -size 203273 +oid sha256:204573e81aa9a39a5cffdacc90e49b6f71735fdd1dec9bc6fa20a77b68525edd +size 208738 diff --git a/tests/UI/specs/UIIntegration_spec.js b/tests/UI/specs/UIIntegration_spec.js index 55729cce0a8..f63d3dc8142 100644 --- a/tests/UI/specs/UIIntegration_spec.js +++ b/tests/UI/specs/UIIntegration_spec.js @@ -27,6 +27,9 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? testEnvironment.save(); testEnvironment.callApi("SitesManager.setSiteAliasUrls", {idSite: 3, urls: []}, done); + + testEnvironment.pluginsToLoad = ['CustomDirPlugin']; + testEnvironment.save(); }); beforeEach(function () { @@ -83,6 +86,11 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? }, done); }); + it("should load the page of a plugin located in a custom directory", function (done) { + expect.screenshot("customdirplugin").to.be.captureSelector('.pageWrap', function (page) { + page.load("?module=CustomDirPlugin&action=index&idSite=1&period=day&date=yesterday"); + }, done); + }); // shortcuts help it("should show shortcut help", function (done) { expect.screenshot("shortcuts").to.be.captureSelector('.modal.open', function (page) { diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/.gitignore b/tests/resources/custompluginsdir/CustomDirPlugin/.gitignore new file mode 100644 index 00000000000..c8c9480010d --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/.gitignore @@ -0,0 +1 @@ +tests/System/processed/*xml \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/API.php b/tests/resources/custompluginsdir/CustomDirPlugin/API.php new file mode 100644 index 00000000000..08a0a957c55 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/API.php @@ -0,0 +1,22 @@ +renderTemplate('index', array( + 'answerToLife' => 42, + 'diTest' => StaticContainer::get('customDirPluginTest') + )); + } +} diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/CustomClass.php b/tests/resources/custompluginsdir/CustomDirPlugin/CustomClass.php new file mode 100644 index 00000000000..01d3fe9c494 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/CustomClass.php @@ -0,0 +1,15 @@ + 'getStylesheetFiles', + 'AssetManager.getJavaScriptFiles' => 'getJsFiles', + ); + } + + public function getStylesheetFiles(&$stylesheets) + { + $stylesheets[] = "plugins/CustomDirPlugin/stylesheets/test.less"; + } + + public function getJsFiles(&$jsFiles) + { + $jsFiles[] = "tests/resources/custompluginsdir/javascripts/test.js"; + } + + public function postLoad() + { + // we make sure auto loading works for these directories + return new CustomClass(); + } + + public function isTrackerPlugin() + { + return true; + } +} diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/SystemSettings.php b/tests/resources/custompluginsdir/CustomDirPlugin/SystemSettings.php new file mode 100644 index 00000000000..d219451fa93 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/SystemSettings.php @@ -0,0 +1,34 @@ +custom = $this->createMetricSetting(); + } + + private function createMetricSetting() + { + return $this->makeSetting('custom', $default = '', FieldConfig::TYPE_STRING, function (FieldConfig $field) { + $field->title = 'Custom setting'; + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + $field->description = 'Enter some custom text here'; + $field->validators[] = new NotEmpty(); + }); + } +} diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/config/config.php b/tests/resources/custompluginsdir/CustomDirPlugin/config/config.php new file mode 100644 index 00000000000..d546932d914 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/config/config.php @@ -0,0 +1,4 @@ + 'hello world!' +); \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/images/ok.png b/tests/resources/custompluginsdir/CustomDirPlugin/images/ok.png new file mode 100644 index 00000000000..148319419d5 Binary files /dev/null and b/tests/resources/custompluginsdir/CustomDirPlugin/images/ok.png differ diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/lang/en.json b/tests/resources/custompluginsdir/CustomDirPlugin/lang/en.json new file mode 100644 index 00000000000..20927d76ef6 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/lang/en.json @@ -0,0 +1,5 @@ +{ + "CustomDirPlugin": { + "CustomName": "Custom Name" + } +} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/plugin.json b/tests/resources/custompluginsdir/CustomDirPlugin/plugin.json new file mode 100644 index 00000000000..2d4b3c19a29 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/plugin.json @@ -0,0 +1,29 @@ +{ + "name": "CustomDirPlugin", + "description": "Custom Plugin located in different directory", + "version": "0.1.0", + "theme": false, + "require": { + "piwik": ">=3.8.1-stable,<4.0.0-b1" + }, + "authors": [ + { + "name": "Matomo", + "email": "", + "homepage": "https://matomo.org" + } + ], + "support": { + "email": "", + "issues": "", + "forum": "", + "irc": "", + "wiki": "", + "source": "", + "docs": "", + "rss": "" + }, + "homepage": "https://matomo.org", + "license": "GPL v3+", + "keywords": [] +} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/import.less b/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/import.less new file mode 100644 index 00000000000..707bfa8e944 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/import.less @@ -0,0 +1,3 @@ +.customPluginDirImportTest { + color: green; +} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/test.less b/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/test.less new file mode 100644 index 00000000000..d2885913bc0 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/stylesheets/test.less @@ -0,0 +1,15 @@ +.fooBarBazCustom { + font-weight: bold; + color: red; +} + +@import "import.less"; + +.customPluginDirBackgroundTestRelative { + background: url('../images/ok.png'); + min-height: 30px; +} +.customPluginDirBackgroundTestPluginRelative { + background: url('plugins/CustomDirPlugin/images/ok.png'); + min-height: 30px; +} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/templates/index.twig b/tests/resources/custompluginsdir/CustomDirPlugin/templates/index.twig new file mode 100644 index 00000000000..24bce30dca0 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/templates/index.twig @@ -0,0 +1,27 @@ +{% extends 'dashboard.twig' %} + +{% block content %} + This is our custom plugin! +
+ + The answer to life is {{ answerToLife }} + +
+

Translation test

+

This should be translated: {{ 'CustomDirPlugin_CustomName'|translate }}

+ +

image test

+

You should see an image showing a check here:

+ +

CSS import test

+

this text should appear green!

+ +

CSS background images test

+

This should have a background image relative

+ +

This should have a background image relative to plugins

+ +

Dependency Injection Test

+

You should now see hello world: {{ diTest }}

+ +{% endblock %} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/Fixtures/SimpleFixtureTrackFewVisits.php b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Fixtures/SimpleFixtureTrackFewVisits.php new file mode 100644 index 00000000000..f5d58969d09 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Fixtures/SimpleFixtureTrackFewVisits.php @@ -0,0 +1,35 @@ +setUpWebsite(); + } + + public function tearDown() + { + // empty + } + + private function setUpWebsite() + { + if (!self::siteCreated($this->idSite)) { + $idSite = self::createWebsite($this->dateTime, $ecommerce = 1); + $this->assertSame($this->idSite, $idSite); + } + } + +} \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/Integration/SystemSettingsTest.php b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Integration/SystemSettingsTest.php new file mode 100644 index 00000000000..a9dd935a1e3 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Integration/SystemSettingsTest.php @@ -0,0 +1,49 @@ +settings = new SystemSettings(); + } + + public function test_get_pluginName() + { + $this->assertSame('CustomDirPlugin', $this->settings->getPluginName()); + } + + public function test_get_default() + { + $this->assertSame('', $this->settings->custom->getValue()); + } + + public function test_set_value() + { + $this->settings->custom->setValue('%'); + $this->assertSame('%', $this->settings->custom->getValue()); + } + +} diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/APITest.php b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/APITest.php new file mode 100644 index 00000000000..11fd73a4f59 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/APITest.php @@ -0,0 +1,44 @@ +assertEquals(42, Request::processRequest('CustomDirPlugin.getCustomAnswerToLive')); + } + + public static function getOutputPrefix() + { + return ''; + } + + public static function getPathToTestDirectory() + { + return dirname(__FILE__); + } + +} + +APITest::$fixture = new SimpleFixtureTrackFewVisits(); \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___API.get_day.xml b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___API.get_day.xml new file mode 100644 index 00000000000..213b91b66a8 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___API.get_day.xml @@ -0,0 +1,44 @@ + + + 2 + 2 + 0 + 4 + 2 + 0 + 1264 + 0 + 0 + 0 + 0 + 0 + 0% + 0 + 0 + 1 + 1 + 2541 + 50% + 1 + 1 + 2541 + 50% + 0 + 0 + 0 + 0% + 0 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 1 + 1 + 0% + 2 + 632 + \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___Goals.getItemsSku_day.xml b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___Goals.getItemsSku_day.xml new file mode 100644 index 00000000000..ba820ed6498 --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/System/expected/test___Goals.getItemsSku_day.xml @@ -0,0 +1,12 @@ + + + + + 25641 + 33 + 1 + 777 + 33 + 0% + + \ No newline at end of file diff --git a/tests/resources/custompluginsdir/CustomDirPlugin/tests/Unit/CustomClassTest.php b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Unit/CustomClassTest.php new file mode 100644 index 00000000000..0018108c66c --- /dev/null +++ b/tests/resources/custompluginsdir/CustomDirPlugin/tests/Unit/CustomClassTest.php @@ -0,0 +1,35 @@ +assertTrue($customClass instanceof CustomClass); + } + +} diff --git a/tests/resources/custompluginsdir/javascripts/test.js b/tests/resources/custompluginsdir/javascripts/test.js new file mode 100644 index 00000000000..1b98ac75de1 --- /dev/null +++ b/tests/resources/custompluginsdir/javascripts/test.js @@ -0,0 +1 @@ +window.fooBarBazBarFoo = 'fooBarBaz'; \ No newline at end of file