Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJulien Moumné <julien@piwik.org>2013-12-17 20:52:36 +0400
committerJulien Moumné <julien@piwik.org>2013-12-17 20:52:36 +0400
commit6624e27e575d6056da7881bc05e9972c50ec1128 (patch)
tree06bec63f550d16ccc8a428d40a79604496ce0985 /core
parent5d44202fb79a9b2aa70b00ec88fd316f83a440b0 (diff)
fixes #4373, #1640
Diffstat (limited to 'core')
-rw-r--r--core/AssetManager.php710
-rw-r--r--core/AssetManager/UIAsset.php63
-rw-r--r--core/AssetManager/UIAsset/InMemoryUIAsset.php65
-rw-r--r--core/AssetManager/UIAsset/OnDiskUIAsset.php115
-rw-r--r--core/AssetManager/UIAssetCacheBuster.php47
-rw-r--r--core/AssetManager/UIAssetCatalog.php83
-rw-r--r--core/AssetManager/UIAssetCatalogSorter.php61
-rw-r--r--core/AssetManager/UIAssetFetcher.php121
-rw-r--r--core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php85
-rw-r--r--core/AssetManager/UIAssetFetcher/StaticUIAssetFetcher.php39
-rw-r--r--core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php70
-rw-r--r--core/AssetManager/UIAssetMerger.php212
-rw-r--r--core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php91
-rw-r--r--core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php142
-rw-r--r--core/AssetManager/UIAssetMinifier.php67
-rw-r--r--core/Db/BatchInsert.php2
-rw-r--r--core/Filesystem.php4
-rw-r--r--core/Plugin.php2
-rw-r--r--core/Plugin/Manager.php31
-rw-r--r--core/ProxyHttp.php3
-rw-r--r--core/Theme.php26
-rw-r--r--core/Twig.php4
-rw-r--r--core/View.php3
23 files changed, 1520 insertions, 526 deletions
diff --git a/core/AssetManager.php b/core/AssetManager.php
index ec850de0db..1104ed42d8 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -11,14 +11,19 @@
namespace Piwik;
use Exception;
-use JSMin;
-use lessc;
+use Piwik\AssetManager\UIAsset;
+use Piwik\AssetManager\UIAsset\InMemoryUIAsset;
+use Piwik\AssetManager\UIAsset\OnDiskUIAsset;
+use Piwik\AssetManager\UIAssetCacheBuster;
+use Piwik\AssetManager\UIAssetFetcher\JScriptUIAssetFetcher;
+use Piwik\AssetManager\UIAssetFetcher\StaticUIAssetFetcher;
+use Piwik\AssetManager\UIAssetFetcher\StylesheetUIAssetFetcher;
+use Piwik\AssetManager\UIAssetFetcher;
+use Piwik\AssetManager\UIAssetMerger\JScriptUIAssetMerger;
+use Piwik\AssetManager\UIAssetMerger\StylesheetUIAssetMerger;
+use Piwik\Plugin\Manager;
use Piwik\Translate;
-
-/**
- * @see libs/jsmin/jsmin.php
- */
-require_once PIWIK_INCLUDE_PATH . '/libs/jsmin/jsmin.php';
+use Piwik\Config as PiwikConfig;
/**
* AssetManager is the class used to manage the inclusion of UI assets:
@@ -36,678 +41,361 @@ require_once PIWIK_INCLUDE_PATH . '/libs/jsmin/jsmin.php';
* When set to 0, files will be included within a pair of files: 1 JavaScript
* and 1 css file.
*
+ * @method static \Piwik\AssetManager getInstance()
* @package Piwik
*/
-class AssetManager
+class AssetManager extends Singleton
{
const MERGED_CSS_FILE = "asset_manager_global_css.css";
- const MERGED_JS_FILE = "asset_manager_global_js.js";
- const STYLESHEET_IMPORT_EVENT = "AssetManager.getStylesheetFiles";
- const JAVASCRIPT_IMPORT_EVENT = "AssetManager.getJavaScriptFiles";
- const MERGED_FILE_DIR = "tmp/assets/";
- const COMPRESSED_FILE_LOCATION = "/tmp/assets/";
+ const MERGED_CORE_JS_FILE = "asset_manager_core_js.js";
+ const MERGED_NON_CORE_JS_FILE = "asset_manager_non_core_js.js";
const CSS_IMPORT_DIRECTIVE = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n";
const JS_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
- const GET_JS_MODULE_ACTION = "index.php?module=Proxy&action=getJs";
- const MINIFIED_JS_RATIO = 100;
+ const GET_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getCoreJs";
+ const GET_NON_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getNonCoreJs";
/**
- * @param $file
- * @param $less
- * @internal param $mergedContent
- * @return string
+ * @var UIAssetCacheBuster
*/
- protected static function getCssContentFromFile($file, $less)
- {
- self::validateCssFile($file);
-
- $fileLocation = self::getAbsoluteLocation($file);
- $less->addImportDir(dirname($fileLocation));
-
- $content = file_get_contents($fileLocation);
- $content = self::rewriteCssPathsDirectives($file, $content);
-
- return $content;
- }
+ private $cacheBuster;
/**
- * Returns CSS file inclusion directive(s) using the markup <link>
- *
- * @return string
+ * @var UIAssetFetcher
*/
- public static function getCssAssets()
- {
- return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
- }
+ private $minimalStylesheetFetcher;
/**
- * Returns JS file inclusion directive(s) using the markup <script>
- *
- * @return string
+ * @var Theme
*/
- public static function getJsAssets()
- {
- $result = "<script type=\"text/javascript\">\n" . Translate::getJavascriptTranslations() . "\n</script>";
+ private $theme;
- if (self::isMergedAssetsDisabled()) {
- // Individual includes mode
- self::removeMergedAsset(self::MERGED_JS_FILE);
- $result .= self::getIndividualJsIncludes();
- } else {
- $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_JS_MODULE_ACTION);
- }
+ function __construct()
+ {
+ $this->cacheBuster = UIAssetCacheBuster::getInstance();
+ $this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array('plugins/Zeitgeist/stylesheets/base.less'), array(), $this->theme);
- return $result;
+ if(Manager::getInstance()->getThemeEnabled() != null)
+ $this->theme = new Theme();
}
/**
- * Assets are cached in the browser and Piwik server returns 304 after initial download.
- * when the Cache buster string changes, the assets will be re-generated
- *
- * @return string
+ * @param UIAssetCacheBuster $cacheBuster
*/
- public static function generateAssetsCacheBuster()
+ public function setCacheBuster($cacheBuster)
{
- $currentGitHash = @file_get_contents(PIWIK_INCLUDE_PATH . '/.git/refs/heads/master');
- $pluginList = md5(implode(",", \Piwik\Plugin\Manager::getInstance()->getLoadedPluginsName()));
- $cacheBuster = md5(SettingsPiwik::getSalt() . $pluginList . PHP_VERSION . Version::VERSION . trim($currentGitHash));
- return $cacheBuster;
+ $this->cacheBuster = $cacheBuster;
}
/**
- * Generate the merged css file.
- *
- * @throws Exception if a file can not be opened in write mode
+ * @param UIAssetFetcher $minimalStylesheetFetcher
*/
- private static function prepareMergedCssFile()
+ public function setMinimalStylesheetFetcher($minimalStylesheetFetcher)
{
- $mergedCssAlreadyGenerated = self::isGenerated(self::MERGED_CSS_FILE);
- $isDevelopingPiwik = self::isMergedAssetsDisabled();
-
- if ($mergedCssAlreadyGenerated && !$isDevelopingPiwik) {
- return;
- }
-
- $files = self::getStylesheetFiles();
- $less = self::makeLess();
-
- // Loop through each css file
- $mergedContent = "";
- foreach ($files as $file) {
- $mergedContent .= self::getCssContentFromFile($file, $less, $mergedContent);
- }
-
- $fileHash = md5($mergedContent);
- $firstLineCompileHash = "/* compile_me_once=$fileHash */";
-
- // Disable Merged Assets ==> Check on each request if file needs re-compiling
- if ($mergedCssAlreadyGenerated
- && !$isDevelopingPiwik
- ) {
- $mergedFile = self::MERGED_CSS_FILE;
- $cacheIsValid = self::isFirstLineMatching($mergedFile, $firstLineCompileHash);
- if($cacheIsValid) {
- return;
- }
- // Some CSS file in the merge, has changed since last merged asset was generated
- // Note: we do not detect changes in @import'ed LESS files
- }
-
- $mergedContent = $less->compile($mergedContent);
-
- /**
- * Triggered after all less stylesheets are compiled to CSS, minified and merged into
- * one file, but before the generated CSS is written to disk.
- *
- * This event can be used to modify merged CSS.
- *
- * @param string &$mergedContent The merged and minified CSS.
- */
- Piwik::postEvent('AssetManager.filterMergedStylesheets', array(&$mergedContent));
-
- $theme = new Theme;
- $mergedContent = $theme->rewriteAssetsPathToTheme($mergedContent);
-
- $mergedContent =
- $firstLineCompileHash . "\n"
- . "/* Piwik CSS file is compiled with Less. You may be interested in writing a custom Theme for Piwik! */\n"
- . $mergedContent;
-
- self::writeAssetToFile($mergedContent, self::MERGED_CSS_FILE);
+ $this->minimalStylesheetFetcher = $minimalStylesheetFetcher;
}
- protected static function makeLess()
+ /**
+ * @param Theme $theme
+ */
+ public function setTheme($theme)
{
- if (!class_exists("lessc")) {
- throw new Exception("Less was added to composer during 2.0. ==> Execute this command to update composer packages: \$ php composer.phar install");
- }
- $less = new lessc;
- return $less;
+ $this->theme = $theme;
}
/**
- * Returns the base.less compiled to css
+ * Return CSS file inclusion directive(s) using the markup <link>
*
* @return string
*/
- public static function getCompiledBaseCss()
+ public function getCssInclusionDirective()
{
- $file = '/plugins/Zeitgeist/stylesheets/base.less';
- $less = self::makeLess();
- $lessContent = self::getCssContentFromFile($file, $less);
- $css = $less->compile($lessContent);
- return $css;
+ return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
}
- /*
- * Rewrite css url directives
- * - rewrites relative paths
- * - rewrite windows directory separator \\ to /
+ /**
+ * Return JS file inclusion directive(s) using the markup <script>
+ *
+ * @return string
*/
- protected static function rewriteCssPathsDirectives($relativePath, $content)
+ public function getJsInclusionDirective()
{
- static $rootDirectoryLength = null;
- if (is_null($rootDirectoryLength)) {
- $rootDirectoryLength = self::countDirectoriesInPathToRoot();
- }
+ $result = "<script type=\"text/javascript\">\n" . Translate::getJavascriptTranslations() . "\n</script>";
- $baseDirectory = dirname($relativePath);
- $content = preg_replace_callback(
- "/(url\(['\"]?)([^'\")]*)/",
- function ($matches) use ($rootDirectoryLength, $baseDirectory) {
- $absolutePath = substr(realpath(PIWIK_DOCUMENT_ROOT . "/$baseDirectory/" . $matches[2]), $rootDirectoryLength);
- $rewritten = str_replace('\\', '/', $absolutePath);
+ if ($this->isMergedAssetsDisabled()) {
- if (is_file($rewritten)) { // only rewrite the URL if transforming it points to a valid file
- return $matches[1] . $rewritten;
- } else {
- return $matches[1] . $matches[2];
- }
- },
- $content
- );
- return $content;
- }
+ $this->getMergedCoreJSAsset()->delete();
+ $this->getMergedNonCoreJSAsset()->delete();
- protected static function countDirectoriesInPathToRoot()
- {
- $rootDirectory = realpath(PIWIK_DOCUMENT_ROOT);
- if ($rootDirectory != '/' && substr_compare($rootDirectory, '/', -1)) {
- $rootDirectory .= '/';
- }
- $rootDirectoryLen = strlen($rootDirectory);
- return $rootDirectoryLen;
- }
+ $result .= $this->getIndividualJsIncludes();
- private static function writeAssetToFile($mergedContent, $name)
- {
- // Remove the previous file
- self::removeMergedAsset($name);
-
- // Tries to open the new file
- $newFilePath = self::getAbsoluteMergedFileLocation($name);
- $newFile = @fopen($newFilePath, "w");
+ } else {
- if (!$newFile) {
- throw new Exception ("The file : " . $newFile . " can not be opened in write mode.");
+ $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_CORE_JS_MODULE_ACTION);
+ $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_NON_CORE_JS_MODULE_ACTION);
}
- // Write the content in the new file
- fwrite($newFile, $mergedContent);
- fclose($newFile);
+ return $result;
}
/**
- * Returns individual CSS file inclusion directive(s) using the markup <link>
+ * Return the base.less compiled to css
*
- * @return string
+ * @return UIAsset
*/
- private static function getIndividualCssIncludes()
+ public function getCompiledBaseCss()
{
- $cssIncludeString = '';
-
- $stylesheets = self::getStylesheetFiles();
+ $mergedAsset = new InMemoryUIAsset();
- foreach ($stylesheets as $cssFile) {
+ $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $this->minimalStylesheetFetcher, $this->cacheBuster);
- self::validateCssFile($cssFile);
- $cssIncludeString = $cssIncludeString . sprintf(self::CSS_IMPORT_DIRECTIVE, $cssFile);
- }
+ $assetMerger->generateFile();
- return $cssIncludeString;
+ return $mergedAsset;
}
/**
- * Returns required CSS files
+ * Return the css merged file absolute location.
+ * If there is none, the generation process will be triggered.
*
- * @return Array
+ * @return UIAsset
*/
- private static function getStylesheetFiles()
+ public function getMergedStylesheet()
{
- $stylesheets = array();
-
- /**
- * Triggered when gathering the list of all stylesheets (CSS and LESS) needed by
- * Piwik and its plugins.
- *
- * Plugins that have stylesheets should use this event to make those stylesheets
- * load.
- *
- * Stylesheets should be placed within a **stylesheets** subdirectory in your plugin's
- * root directory.
- *
- * _Note: While you are developing your plugin you should enable the config setting
- * `[Debug] disable_merged_assets` so your stylesheets will be reloaded immediately
- * after a change._
- *
- * **Example**
- *
- * public function getStylesheetFiles(&$stylesheets)
- * {
- * $stylesheets[] = "plugins/MyPlugin/stylesheets/myfile.less";
- * $stylesheets[] = "plugins/MyPlugin/stylesheets/myotherfile.css";
- * }
- *
- * @param string[] &$stylesheets The list of stylesheet paths.
- */
- Piwik::postEvent(self::STYLESHEET_IMPORT_EVENT, array(&$stylesheets));
-
- $stylesheets = self::sortCssFiles($stylesheets);
-
- $theme = new Theme;
- $themeStylesheet = $theme->getStylesheet();
- if($themeStylesheet) {
- $stylesheets[] = $themeStylesheet;
- }
+ $mergedAsset = $this->getMergedStylesheetAsset();
- return $stylesheets;
- }
+ $assetFetcher = new StylesheetUIAssetFetcher(Manager::getInstance()->getLoadedPluginsName(), $this->theme);
- /**
- * Ensure CSS stylesheets are loaded in a particular order regardless of the order that plugins are loaded.
- *
- * @param array $stylesheets Array of CSS stylesheet files
- * @return array
- */
- private static function sortCssFiles($stylesheets)
- {
- $priorityCssOrdered = array(
- 'libs/',
- 'plugins/CoreHome/stylesheets/color_manager.css', // must be before other Piwik stylesheets
- 'plugins/Zeitgeist/stylesheets/base.less',
- 'plugins/Zeitgeist/stylesheets/',
- 'plugins/',
- 'plugins/Dashboard/stylesheets/dashboard.less',
- 'tests/',
- );
-
- return self::prioritySort($priorityCssOrdered, $stylesheets);
+ $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
+
+ $assetMerger->generateFile();
+
+ return $mergedAsset;
}
/**
- * Check the validity of the css file
+ * Return the core js merged file absolute location.
+ * If there is none, the generation process will be triggered.
*
- * @param string $cssFile CSS file name
- * @return boolean
- * @throws Exception if a file can not be opened in write mode
+ * @return UIAsset
*/
- private static function validateCssFile($cssFile)
+ public function getMergedCoreJavaScript()
{
- if (!self::assetIsReadable($cssFile)) {
- throw new Exception("The css asset with 'href' = " . $cssFile . " is not readable");
- }
+ return $this->getMergedJavascript($this->getCoreJScriptFetcher(), $this->getMergedCoreJSAsset());
}
/**
- * Generate the merged js file.
+ * Return the non core js merged file absolute location.
+ * If there is none, the generation process will be triggered.
*
- * @throws Exception if a file can not be opened in write mode
+ * @return UIAsset
*/
- private static function generateMergedJsFile()
+ public function getMergedNonCoreJavaScript()
{
- $mergedContent = self::getFirstLineOfMergedJs();
-
- $files = self::getJsFiles();
- foreach ($files as $file) {
- self::validateJsFile($file);
- $fileLocation = self::getAbsoluteLocation($file);
- $content = file_get_contents($fileLocation);
- if (!self::isMinifiedJs($content)) {
- $content = JSMin::minify($content);
- }
- $mergedContent = $mergedContent . PHP_EOL . $content;
- }
- $mergedContent = str_replace("\n", "\r\n", $mergedContent);
-
- /**
- * Triggered after all the JavaScript files Piwik uses are minified and merged into a
- * single file, but before the merged JavaScript is written to disk.
- *
- * Plugins can use this event to modify merged JavaScript or do something else
- * with it.
- *
- * @param string &$mergedContent The minified and merged JavaScript.
- */
- Piwik::postEvent('AssetManager.filterMergedJavaScripts', array(&$mergedContent));
-
- $theme = new Theme;
- $mergedContent = $theme->rewriteAssetsPathToTheme($mergedContent);
-
- self::writeAssetToFile($mergedContent, self::MERGED_JS_FILE);
+ return $this->getMergedJavascript($this->getNonCoreJScriptFetcher(), $this->getMergedNonCoreJSAsset());
}
/**
- * Returns individual JS file inclusion directive(s) using the markup <script>
- *
- * @return string
+ * @param boolean $core
+ * @return string[]
*/
- private static function getIndividualJsIncludes()
+ public function getLoadedPlugins($core)
{
- $jsIncludeString = '';
+ $loadedPlugins = array();
- $jsFiles = self::getJsFiles();
- foreach ($jsFiles as $jsFile) {
- self::validateJsFile($jsFile);
- $jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile);
+ foreach(Manager::getInstance()->getLoadedPluginsName() as $pluginName) {
+
+ $pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName);
+
+ if(($pluginIsCore && $core) || (!$pluginIsCore && !$core))
+ $loadedPlugins[] = $pluginName;
}
- return $jsIncludeString;
+
+ return $loadedPlugins;
}
/**
- * Returns required JS files
- *
- * @return Array
+ * Remove previous merged assets
*/
- private static function getJsFiles()
+ public function removeMergedAssets($pluginName = false)
{
- $jsFiles = array();
-
- /**
- * Triggered when gathering the list of all JavaScript files needed by Piwik
- * and its plugins.
- *
- * Plugins that have their own JavaScript should use this event to make those
- * files load in the browser.
- *
- * JavaScript files should be placed within a **javascripts** subdirectory in your
- * plugin's root directory.
- *
- * _Note: While you are developing your plugin you should enable the config setting
- * `[Debug] disable_merged_assets` so JavaScript files will be reloaded immediately
- * after every change._
- *
- * **Example**
- *
- * public function getJsFiles(&$jsFiles)
- * {
- * $jsFiles[] = "plugins/MyPlugin/javascripts/myfile.js";
- * $jsFiles[] = "plugins/MyPlugin/javascripts/anotherone.js";
- * }
- *
- * @param string[] $jsFiles The JavaScript files to load.
- */
- Piwik::postEvent(self::JAVASCRIPT_IMPORT_EVENT, array(&$jsFiles));
-
- $theme = new Theme;
- $jsInThemes = $theme->getJavaScriptFiles();
- if(!empty($jsInThemes)) {
- $jsFiles = array_merge($jsFiles, $jsInThemes);
+ $assetsToRemove = array($this->getMergedStylesheetAsset());
+
+ if($pluginName) {
+
+ if($this->pluginContainsJScriptAssets($pluginName)) {
+
+ PiwikConfig::getInstance()->init();
+ if(Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
+
+ $assetsToRemove[] = $this->getMergedCoreJSAsset();
+
+ } else {
+
+ $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
+ }
+ }
+
+ } else {
+
+ $assetsToRemove[] = $this->getMergedCoreJSAsset();
+ $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
}
- $jsFiles = self::sortJsFiles($jsFiles);
- return $jsFiles;
+ $this->removeAssets($assetsToRemove);
}
/**
- * Ensure core JS (jQuery etc.) are loaded in a particular order regardless of the order that plugins are loaded.
+ * Check if the merged file directory exists and is writable.
*
- * @param array $jsFiles Arry of JavaScript files
- * @return array
+ * @return string The directory location
+ * @throws Exception if directory is not writable.
*/
- private static function sortJsFiles($jsFiles)
+ public function getAssetDirectory()
{
- $priorityJsOrdered = array(
- 'libs/jquery/jquery.js',
- 'libs/jquery/jquery-ui.js',
- 'libs/jquery/jquery.browser.js',
- 'libs/',
- 'plugins/Zeitgeist/javascripts/piwikHelper.js',
- 'plugins/Zeitgeist/javascripts/',
- 'plugins/CoreHome/javascripts/broadcast.js',
- 'plugins/',
- 'tests/',
- );
-
- return self::prioritySort($priorityJsOrdered, $jsFiles);
- }
+ $mergedFileDirectory = PIWIK_USER_PATH . "/tmp/assets";
+ $mergedFileDirectory = SettingsPiwik::rewriteTmpPathWithHostname($mergedFileDirectory);
- /**
- * Check the validity of the js file
- *
- * @param string $jsFile JavaScript file name
- * @return boolean
- * @throws Exception if js file is not valid
- */
- private static function validateJsFile($jsFile)
- {
- if (!self::assetIsReadable($jsFile)) {
- throw new Exception("The js asset with 'src' = " . $jsFile . " is not readable");
+ if (!is_dir($mergedFileDirectory)) {
+ Filesystem::mkdir($mergedFileDirectory);
}
- }
- /**
- * Returns the global option disable_merged_assets
- *
- * @return string
- */
- public static function isMergedAssetsDisabled()
- {
- return Config::getInstance()->Debug['disable_merged_assets'];
+ if (!is_writable($mergedFileDirectory)) {
+ throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
+ }
+
+ return $mergedFileDirectory;
}
/**
- * Returns the css merged file absolute location.
- * If there is none, the generation process will be triggered.
+ * Return the global option disable_merged_assets
*
- * @return string The absolute location of the css merged file
+ * @return boolean
*/
- public static function getMergedCssFileLocation()
+ public function isMergedAssetsDisabled()
{
- self::prepareMergedCssFile();
- return self::getAbsoluteMergedFileLocation(self::MERGED_CSS_FILE);
+ return Config::getInstance()->Debug['disable_merged_assets'];
}
/**
- * Returns the js merged file absolute location.
- * If there is none, the generation process will be triggered.
- *
- * @return string The absolute location of the js merged file
+ * @param UIAssetFetcher $assetFetcher
+ * @param UIAsset $mergedAsset
+ * @return UIAsset
*/
- public static function getMergedJsFileLocation()
+ private function getMergedJavascript($assetFetcher, $mergedAsset)
{
- $isGenerated = self::isGenerated(self::MERGED_JS_FILE);
+ $assetMerger = new JScriptUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
- // Make sure the merged JS is re-generated if there are new commits
- if($isGenerated) {
- $expectedFirstLine = self::getFirstLineOfMergedJs();
- $isGenerated = self::isFirstLineMatching(self::MERGED_JS_FILE, $expectedFirstLine);
- }
- if (!$isGenerated) {
- self::generateMergedJsFile();
- }
+ $assetMerger->generateFile();
- return self::getAbsoluteMergedFileLocation(self::MERGED_JS_FILE);
+ return $mergedAsset;
}
/**
- * Check if the provided merged file is generated
+ * Return individual JS file inclusion directive(s) using the markup <script>
*
- * @param string $filename filename of the merged asset
- * @return boolean true is file exists and is readable, false otherwise
+ * @return string
*/
- private static function isGenerated($filename)
+ private function getIndividualJsIncludes()
{
- return is_readable(self::getAbsoluteMergedFileLocation($filename));
+ return
+ $this->getIndividualJsIncludesFromAssetFetcher($this->getCoreJScriptFetcher()) .
+ $this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher());
}
/**
- * Removes the previous merged file if it exists.
- * Also tries to remove compressed version of the merged file.
- *
- * @param string $filename filename of the merged asset
- * @see ProxyStaticFile::serveStaticFile(serveFile
- * @throws Exception if the file couldn't be deleted
+ * @param UIAssetFetcher $assetFetcher
+ * @return string
*/
- private static function removeMergedAsset($filename)
+ private function getIndividualJsIncludesFromAssetFetcher($assetFetcher)
{
- $isGenerated = self::isGenerated($filename);
-
- if ($isGenerated) {
- if (!unlink(self::getAbsoluteMergedFileLocation($filename))) {
- throw new Exception("Unable to delete merged file : " . $filename . ". Please delete the file and refresh");
- }
+ $jsIncludeString = '';
- // Tries to remove compressed version of the merged file.
- // See ProxyHttp::serverStaticFile() for more info on static file compression
- $compressedFileLocation = PIWIK_USER_PATH . self::COMPRESSED_FILE_LOCATION . $filename;
- $compressedFileLocation = SettingsPiwik::rewriteTmpPathWithHostname($compressedFileLocation);
+ foreach ($assetFetcher->getCatalog()->getAssets() as $jsFile) {
- @unlink($compressedFileLocation . ".deflate");
- @unlink($compressedFileLocation . ".gz");
+ $jsFile->validateFile();
+ $jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile->getRelativeLocation());
}
+
+ return $jsIncludeString;
}
- /**
- * Remove previous merged assets
- */
- public static function removeMergedAssets()
+ private function getCoreJScriptFetcher()
{
- self::removeMergedAsset(self::MERGED_CSS_FILE);
- self::removeMergedAsset(self::MERGED_JS_FILE);
+ return new JScriptUIAssetFetcher($this->getLoadedPlugins(true), $this->theme);
}
- /**
- * Check if asset is readable
- *
- * @param string $relativePath Relative path to file
- * @return boolean
- */
- private static function assetIsReadable($relativePath)
+ private function getNonCoreJScriptFetcher()
{
- return is_readable(self::getAbsoluteLocation($relativePath));
+ return new JScriptUIAssetFetcher($this->getLoadedPlugins(false), $this->theme);
}
/**
- * Check if the merged file directory exists and is writable.
- *
- * @return string The directory location
- * @throws Exception if directory is not writable.
+ * @param string $pluginName
+ * @return boolean
*/
- private static function getMergedFileDirectory()
+ private function pluginContainsJScriptAssets($pluginName)
{
- $mergedFileDirectory = PIWIK_USER_PATH . '/' . self::MERGED_FILE_DIR;
- $mergedFileDirectory = SettingsPiwik::rewriteTmpPathWithHostname($mergedFileDirectory);
+ $fetcher = new JScriptUIAssetFetcher(array($pluginName), $this->theme);
- if (!is_dir($mergedFileDirectory)) {
- Filesystem::mkdir($mergedFileDirectory);
- }
+ $assets = $fetcher->getCatalog()->getAssets();
- if (!is_writable($mergedFileDirectory)) {
- throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
- }
+ $plugin = Manager::getInstance()->getLoadedPlugin($pluginName);
- return $mergedFileDirectory;
- }
+ if($plugin->isTheme()) {
- /**
- * Builds the absolute location of the requested merged file
- *
- * @param string $mergedFile Name of the merge file
- * @return string absolute location of the merged file
- */
- private static function getAbsoluteMergedFileLocation($mergedFile)
- {
- return self::getMergedFileDirectory() . $mergedFile;
+ $theme = Manager::getInstance()->getTheme($pluginName);
+
+ $javaScriptFiles = $theme->getJavaScriptFiles();
+
+ if(!empty($javaScriptFiles))
+ $assets = array_merge($assets, $javaScriptFiles);
+ }
+
+ return !empty($assets);
}
/**
- * Returns the full path of an asset file
- *
- * @param string $relativePath Relative path to file
- * @return string
+ * @param UIAsset[] $uiAssets
*/
- private static function getAbsoluteLocation($relativePath)
+ private function removeAssets($uiAssets)
{
- // served by web server directly, so must be a public path
- return PIWIK_DOCUMENT_ROOT . "/" . $relativePath;
+ foreach($uiAssets as $uiAsset) {
+ $uiAsset->delete();
+ }
}
/**
- * Indicates if the provided JavaScript content has already been minified or not.
- * The heuristic is based on a custom ratio : (size of file) / (number of lines).
- * The threshold (100) has been found empirically on existing files :
- * - the ratio never exceeds 50 for non-minified content and
- * - it never goes under 150 for minified content.
- *
- * @param string $content Contents of the JavaScript file
- * @return boolean
+ * @return UIAsset
*/
- private static function isMinifiedJs($content)
+ private function getMergedStylesheetAsset()
{
- $lineCount = substr_count($content, "\n");
- if ($lineCount == 0) {
- return true;
- }
-
- $contentSize = strlen($content);
-
- $ratio = $contentSize / $lineCount;
-
- return $ratio > self::MINIFIED_JS_RATIO;
+ return $this->getMergedUIAsset(self::MERGED_CSS_FILE);
}
/**
- * Sort files according to priority order. Duplicates are also removed.
- *
- * @param array $priorityOrder Ordered array of paths (first to last) serving as buckets
- * @param array $files Unsorted array of files
- * @return array
+ * @return UIAsset
*/
- public static function prioritySort($priorityOrder, $files)
+ private function getMergedCoreJSAsset()
{
- $newFiles = array();
- foreach ($priorityOrder as $filePattern) {
- $newFiles = array_merge($newFiles, preg_grep('~^' . $filePattern . '~', $files));
- }
- return array_keys(array_flip($newFiles));
+ return $this->getMergedUIAsset(self::MERGED_CORE_JS_FILE);
}
/**
- * @param $mergedFile
- * @param $firstLineCompileHash
- * @return bool
+ * @return UIAsset
*/
- private static function isFirstLineMatching($mergedFile, $firstLineCompileHash)
+ private function getMergedNonCoreJSAsset()
{
- $pathMerged = self::getAbsoluteMergedFileLocation($mergedFile);
- $f = fopen($pathMerged, 'r');
- $firstLine = fgets($f);
- fclose($f);
- if (!empty($firstLine)
- && trim($firstLine) == trim($firstLineCompileHash)
- ) {
- return true;
- }
- return false;
+ return $this->getMergedUIAsset(self::MERGED_NON_CORE_JS_FILE);
}
/**
- * @return string
+ * @param string $fileName
+ * @return UIAsset
*/
- private static function getFirstLineOfMergedJs()
+ private function getMergedUIAsset($fileName)
{
- return "/* Piwik Javascript - cb=" . self::generateAssetsCacheBuster() . "*/\n";
+ return new OnDiskUIAsset($this->getAssetDirectory(), $fileName);
}
} \ No newline at end of file
diff --git a/core/AssetManager/UIAsset.php b/core/AssetManager/UIAsset.php
new file mode 100644
index 0000000000..00ba05cf74
--- /dev/null
+++ b/core/AssetManager/UIAsset.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+use Exception;
+
+abstract class UIAsset
+{
+ abstract public function validateFile();
+
+ /**
+ * @return string
+ */
+ abstract public function getAbsoluteLocation();
+
+ /**
+ * @return string
+ */
+ abstract public function getRelativeLocation();
+
+ /**
+ * @return string
+ */
+ abstract public function getBaseDirectory();
+
+ /**
+ * Removes the previous file if it exists.
+ * Also tries to remove compressed version of the file.
+ *
+ * @see ProxyStaticFile::serveStaticFile(serveFile
+ * @throws Exception if the file couldn't be deleted
+ */
+ abstract public function delete();
+
+ /**
+ * @param string $content
+ * @throws \Exception
+ */
+ abstract public function writeContent($content);
+
+ /**
+ * @return string
+ */
+ abstract public function getContent();
+
+ /**
+ * @return boolean
+ */
+ abstract public function exists();
+
+ /**
+ * @return int
+ */
+ abstract public function getModificationDate();
+}
diff --git a/core/AssetManager/UIAsset/InMemoryUIAsset.php b/core/AssetManager/UIAsset/InMemoryUIAsset.php
new file mode 100644
index 0000000000..3b5e8488f5
--- /dev/null
+++ b/core/AssetManager/UIAsset/InMemoryUIAsset.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAsset;
+
+use Exception;
+use Piwik\AssetManager\UIAsset;
+
+class InMemoryUIAsset extends UIAsset
+{
+ private $content;
+
+ public function validateFile()
+ {
+ return;
+ }
+
+ public function getAbsoluteLocation()
+ {
+ throw new Exception('invalid operation');
+ }
+
+ public function getRelativeLocation()
+ {
+ throw new Exception('invalid operation');
+ }
+
+ public function getBaseDirectory()
+ {
+ throw new Exception('invalid operation');
+ }
+
+ public function delete()
+ {
+ $this->content = null;
+ }
+
+ public function exists()
+ {
+ return false;
+ }
+
+
+ public function writeContent($content)
+ {
+ $this->content = $content;
+ }
+
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ public function getModificationDate()
+ {
+ throw new Exception('invalid operation');
+ }
+}
diff --git a/core/AssetManager/UIAsset/OnDiskUIAsset.php b/core/AssetManager/UIAsset/OnDiskUIAsset.php
new file mode 100644
index 0000000000..bdda1119c2
--- /dev/null
+++ b/core/AssetManager/UIAsset/OnDiskUIAsset.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAsset;
+
+use Exception;
+use Piwik\AssetManager\UIAsset;
+
+class OnDiskUIAsset extends UIAsset
+{
+ /**
+ * @var string
+ */
+ private $baseDirectory;
+
+ /**
+ * @var string
+ */
+ private $relativeLocation;
+
+ /**
+ * @param string $baseDirectory
+ * @param string $fileLocation
+ */
+ function __construct($baseDirectory, $fileLocation)
+ {
+ $this->baseDirectory = $baseDirectory;
+ $this->relativeLocation = $fileLocation;
+ }
+
+ public function getAbsoluteLocation()
+ {
+ return $this->baseDirectory . '/' . $this->relativeLocation;
+ }
+
+ public function getRelativeLocation()
+ {
+ return $this->relativeLocation;
+ }
+
+ public function getBaseDirectory()
+ {
+ return $this->baseDirectory;
+ }
+
+ public function validateFile()
+ {
+ if (!$this->assetIsReadable())
+ throw new Exception("The ui asset with 'href' = " . $this->getAbsoluteLocation() . " is not readable");
+ }
+
+ public function delete()
+ {
+ if ($this->exists()) {
+
+ if (!unlink($this->getAbsoluteLocation()))
+ throw new Exception("Unable to delete merged file : " . $this->getAbsoluteLocation() . ". Please delete the file and refresh");
+
+ // try to remove compressed version of the merged file.
+ @unlink($this->getAbsoluteLocation() . ".deflate");
+ @unlink($this->getAbsoluteLocation() . ".gz");
+ }
+ }
+
+ /**
+ * @param string $content
+ * @throws \Exception
+ */
+ public function writeContent($content)
+ {
+ $this->delete();
+
+ $newFile = @fopen($this->getAbsoluteLocation(), "w");
+
+ if (!$newFile)
+ throw new Exception ("The file : " . $newFile . " can not be opened in write mode.");
+
+ fwrite($newFile, $content);
+
+ fclose($newFile);
+ }
+
+ /**
+ * @return string
+ */
+ public function getContent()
+ {
+ return file_get_contents($this->getAbsoluteLocation());
+ }
+
+ public function exists()
+ {
+ return $this->assetIsReadable();
+ }
+
+ /**
+ * @return boolean
+ */
+ private function assetIsReadable()
+ {
+ return is_readable($this->getAbsoluteLocation());
+ }
+
+ public function getModificationDate()
+ {
+ return filemtime($this->getAbsoluteLocation());
+ }
+}
diff --git a/core/AssetManager/UIAssetCacheBuster.php b/core/AssetManager/UIAssetCacheBuster.php
new file mode 100644
index 0000000000..a8359442b3
--- /dev/null
+++ b/core/AssetManager/UIAssetCacheBuster.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @method static \Piwik\AssetManager\UIAssetCacheBuster getInstance()
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+use Piwik\Plugin\Manager;
+use Piwik\SettingsPiwik;
+use Piwik\Singleton;
+use Piwik\Version;
+
+class UIAssetCacheBuster extends Singleton
+{
+ /**
+ * Cache buster based on
+ * - Piwik version
+ * - Loaded plugins
+ * - Super user salt
+ * - Latest
+ *
+ * @param string[] $pluginNames
+ * @return string
+ */
+ public function piwikVersionBasedCacheBuster($pluginNames = false)
+ {
+ $currentGitHash = @file_get_contents(PIWIK_INCLUDE_PATH . '/.git/refs/heads/master');
+ $pluginList = md5(implode(",", !$pluginNames ? Manager::getInstance()->getLoadedPluginsName() : $pluginNames));
+ $cacheBuster = md5(SettingsPiwik::getSalt() . $pluginList . PHP_VERSION . Version::VERSION . trim($currentGitHash));
+ return $cacheBuster;
+ }
+
+ /**
+ * @param string $content
+ * @return string
+ */
+ public function md5BasedCacheBuster($content)
+ {
+ return md5($content);
+ }
+}
diff --git a/core/AssetManager/UIAssetCatalog.php b/core/AssetManager/UIAssetCatalog.php
new file mode 100644
index 0000000000..dc25553c8e
--- /dev/null
+++ b/core/AssetManager/UIAssetCatalog.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+class UIAssetCatalog
+{
+ /**
+ * @var UIAsset[]
+ */
+ private $uiAssets = array();
+
+ /**
+ * @var UIAssetCatalogSorter
+ */
+ private $catalogSorter;
+
+ /**
+ * @var string
+ */
+ private $concatenatedAssets;
+
+ /**
+ * @param UIAssetCatalogSorter $catalogSorter
+ */
+ function __construct($catalogSorter)
+ {
+ $this->catalogSorter = $catalogSorter;
+ }
+
+ /**
+ * @param UIAsset $uiAsset
+ */
+ public function addUIAsset($uiAsset)
+ {
+ if(!$this->assetAlreadyInCatalog($uiAsset)) {
+
+ $this->uiAssets[] = $uiAsset;
+ $this->resetConcatenatedAssets();
+ }
+ }
+
+ /**
+ * @return UIAsset[]
+ */
+ public function getAssets()
+ {
+ return $this->uiAssets;
+ }
+
+ /**
+ * @return UIAssetCatalog
+ */
+ public function getSortedCatalog()
+ {
+ return $this->catalogSorter->sortUIAssetCatalog($this);
+ }
+
+ private function resetConcatenatedAssets()
+ {
+ $this->concatenatedAssets = null;
+ }
+
+ /**
+ * @param UIAsset $uiAsset
+ * @return boolean
+ */
+ private function assetAlreadyInCatalog($uiAsset)
+ {
+ foreach($this->uiAssets as $existingAsset)
+ if($uiAsset->getAbsoluteLocation() == $existingAsset->getAbsoluteLocation())
+ return true;
+
+ return false;
+ }
+}
diff --git a/core/AssetManager/UIAssetCatalogSorter.php b/core/AssetManager/UIAssetCatalogSorter.php
new file mode 100644
index 0000000000..1f93a398e1
--- /dev/null
+++ b/core/AssetManager/UIAssetCatalogSorter.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+class UIAssetCatalogSorter
+{
+ /**
+ * @var string[]
+ */
+ private $priorityOrder;
+
+ /**
+ * @param string[] $priorityOrder
+ */
+ function __construct($priorityOrder)
+ {
+ $this->priorityOrder = $priorityOrder;
+ }
+
+ /**
+ * @param UIAssetCatalog $uiAssetCatalog
+ * @return UIAssetCatalog
+ */
+ public function sortUIAssetCatalog($uiAssetCatalog)
+ {
+ $sortedCatalog = new UIAssetCatalog($this);
+ foreach ($this->priorityOrder as $filePattern) {
+
+ $assetsMatchingPattern = array_filter($uiAssetCatalog->getAssets(), function($uiAsset) use ($filePattern) {
+ return preg_match('~^' . $filePattern . '~', $uiAsset->getRelativeLocation());
+ });
+
+ foreach($assetsMatchingPattern as $assetMatchingPattern) {
+ $sortedCatalog->addUIAsset($assetMatchingPattern);
+ }
+ }
+
+ $this->addUnmatchedAssets($uiAssetCatalog, $sortedCatalog);
+
+ return $sortedCatalog;
+ }
+
+ /**
+ * @param UIAssetCatalog $uiAssetCatalog
+ * @param UIAssetCatalog $sortedCatalog
+ */
+ private function addUnmatchedAssets($uiAssetCatalog, $sortedCatalog)
+ {
+ foreach ($uiAssetCatalog->getAssets() as $uiAsset) {
+ $sortedCatalog->addUIAsset($uiAsset);
+ }
+ }
+}
diff --git a/core/AssetManager/UIAssetFetcher.php b/core/AssetManager/UIAssetFetcher.php
new file mode 100644
index 0000000000..21185014f7
--- /dev/null
+++ b/core/AssetManager/UIAssetFetcher.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+use Piwik\AssetManager\UIAsset\OnDiskUIAsset;
+use Piwik\Theme;
+
+abstract class UIAssetFetcher
+{
+ /**
+ * @var UIAssetCatalog
+ */
+ protected $catalog;
+
+ /**
+ * @var string[]
+ */
+ protected $fileLocations = array();
+
+ /**
+ * @var string[]
+ */
+ protected $plugins;
+
+ /**
+ * @var Theme
+ */
+ private $theme;
+
+ /**
+ * @param string[] $plugins
+ * @param Theme $theme
+ */
+ function __construct($plugins, $theme)
+ {
+ $this->plugins = $plugins;
+ $this->theme = $theme;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getPlugins()
+ {
+ return $this->plugins;
+ }
+
+ /**
+ * $return UIAssetCatalog
+ */
+ public function getCatalog()
+ {
+ if($this->catalog == null)
+ $this->createCatalog();
+
+ return $this->catalog;
+ }
+
+ abstract protected function retrieveFileLocations();
+
+ /**
+ * @return string[]
+ */
+ abstract protected function getPriorityOrder();
+
+ private function createCatalog()
+ {
+ $this->retrieveFileLocations();
+
+ $this->initCatalog();
+
+ $this->populateCatalog();
+
+ $this->sortCatalog();
+ }
+
+ private function initCatalog()
+ {
+ $catalogSorter = new UIAssetCatalogSorter($this->getPriorityOrder());
+ $this->catalog = new UIAssetCatalog($catalogSorter);
+ }
+
+ private function populateCatalog()
+ {
+ foreach ($this->fileLocations as $fileLocation) {
+
+ $newUIAsset = new OnDiskUIAsset($this->getBaseDirectory(), $fileLocation);
+ $this->catalog->addUIAsset($newUIAsset);
+ }
+ }
+
+ private function sortCatalog()
+ {
+ $this->catalog = $this->catalog->getSortedCatalog();
+ }
+
+ /**
+ * @return string
+ */
+ private function getBaseDirectory()
+ {
+ // served by web server directly, so must be a public path
+ return PIWIK_USER_PATH;
+ }
+
+ /**
+ * @return Theme
+ */
+ public function getTheme()
+ {
+ return $this->theme;
+ }
+}
diff --git a/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php
new file mode 100644
index 0000000000..3382fdabc6
--- /dev/null
+++ b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAssetFetcher;
+
+use Piwik\AssetManager\UIAssetFetcher;
+use Piwik\Piwik;
+use string;
+
+class JScriptUIAssetFetcher extends UIAssetFetcher
+{
+
+ protected function retrieveFileLocations()
+ {
+
+ if(!empty($this->plugins)) {
+
+ /**
+ * Triggered when gathering the list of all JavaScript files needed by Piwik
+ * and its plugins.
+ *
+ * Plugins that have their own JavaScript should use this event to make those
+ * files load in the browser.
+ *
+ * JavaScript files should be placed within a **javascripts** subdirectory in your
+ * plugin's root directory.
+ *
+ * _Note: While you are developing your plugin you should enable the config setting
+ * `[Debug] disable_merged_assets` so JavaScript files will be reloaded immediately
+ * after every change._
+ *
+ * **Example**
+ *
+ * public function getJsFiles(&$jsFiles)
+ * {
+ * $jsFiles[] = "plugins/MyPlugin/javascripts/myfile.js";
+ * $jsFiles[] = "plugins/MyPlugin/javascripts/anotherone.js";
+ * }
+ *
+ * @param string[] $jsFiles The JavaScript files to load.
+ */
+ Piwik::postEvent('AssetManager.getJavaScriptFiles', array(&$this->fileLocations), null, $this->plugins);
+ }
+
+ $this->addThemeFiles();
+ }
+
+ protected function addThemeFiles()
+ {
+ if(in_array($this->getTheme()->getThemeName(), $this->plugins)) {
+
+ $jsInThemes = $this->getTheme()->getJavaScriptFiles();
+
+ if(!empty($jsInThemes)) {
+
+ foreach($jsInThemes as $jsFile) {
+
+ $this->fileLocations[] = $jsFile;
+ }
+ }
+ }
+ }
+
+ protected function getPriorityOrder()
+ {
+ return array(
+ 'libs/jquery/jquery.js',
+ 'libs/jquery/jquery-ui.js',
+ 'libs/jquery/jquery.browser.js',
+ 'libs/',
+ 'plugins/Zeitgeist/javascripts/piwikHelper.js',
+ 'plugins/Zeitgeist/javascripts/',
+ 'plugins/CoreHome/javascripts/broadcast.js',
+ 'plugins/',
+ 'tests/',
+ );
+ }
+}
diff --git a/core/AssetManager/UIAssetFetcher/StaticUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/StaticUIAssetFetcher.php
new file mode 100644
index 0000000000..981eac8204
--- /dev/null
+++ b/core/AssetManager/UIAssetFetcher/StaticUIAssetFetcher.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAssetFetcher;
+
+use Piwik\AssetManager\UIAssetFetcher;
+
+class StaticUIAssetFetcher extends UIAssetFetcher
+{
+ /**
+ * @var string[]
+ */
+ private $priorityOrder;
+
+ function __construct($fileLocations, $priorityOrder, $theme)
+ {
+ parent::__construct(array(), $theme);
+
+ $this->fileLocations = $fileLocations;
+ $this->priorityOrder = $priorityOrder;
+ }
+
+ protected function retrieveFileLocations()
+ {
+
+ }
+
+ protected function getPriorityOrder()
+ {
+ return $this->priorityOrder;
+ }
+}
diff --git a/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php
new file mode 100644
index 0000000000..e5e6e7be0f
--- /dev/null
+++ b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAssetFetcher;
+
+use Piwik\AssetManager\UIAssetFetcher;
+use Piwik\Piwik;
+
+class StylesheetUIAssetFetcher extends UIAssetFetcher
+{
+ protected function getPriorityOrder()
+ {
+ return array(
+ 'libs/',
+ 'plugins/CoreHome/stylesheets/color_manager.css', // must be before other Piwik stylesheets
+ 'plugins/Zeitgeist/stylesheets/base.less',
+ 'plugins/Zeitgeist/stylesheets/',
+ 'plugins/',
+ 'plugins/Dashboard/stylesheets/dashboard.less',
+ 'tests/',
+ );
+ }
+
+ protected function retrieveFileLocations()
+ {
+ /**
+ * Triggered when gathering the list of all stylesheets (CSS and LESS) needed by
+ * Piwik and its plugins.
+ *
+ * Plugins that have stylesheets should use this event to make those stylesheets
+ * load.
+ *
+ * Stylesheets should be placed within a **stylesheets** subdirectory in your plugin's
+ * root directory.
+ *
+ * _Note: While you are developing your plugin you should enable the config setting
+ * `[Debug] disable_merged_assets` so your stylesheets will be reloaded immediately
+ * after a change._
+ *
+ * **Example**
+ *
+ * public function getStylesheetFiles(&$stylesheets)
+ * {
+ * $stylesheets[] = "plugins/MyPlugin/stylesheets/myfile.less";
+ * $stylesheets[] = "plugins/MyPlugin/stylesheets/myotherfile.css";
+ * }
+ *
+ * @param string[] &$stylesheets The list of stylesheet paths.
+ */
+ Piwik::postEvent('AssetManager.getStylesheetFiles', array(&$this->fileLocations));
+
+ $this->addThemeFiles();
+ }
+
+ protected function addThemeFiles()
+ {
+ $themeStylesheet = $this->getTheme()->getStylesheet();
+
+ if($themeStylesheet) {
+ $this->fileLocations[] = $themeStylesheet;
+ }
+ }
+}
diff --git a/core/AssetManager/UIAssetMerger.php b/core/AssetManager/UIAssetMerger.php
new file mode 100644
index 0000000000..8e1ededd47
--- /dev/null
+++ b/core/AssetManager/UIAssetMerger.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+use Piwik\AssetManager\PiwikLessCompiler;
+use Piwik\AssetManager\UIAsset\StylesheetUIAsset;
+use Piwik\AssetManager;
+
+abstract class UIAssetMerger
+{
+ /**
+ * @var UIAssetFetcher
+ */
+ private $assetFetcher;
+
+ /**
+ * @var UIAsset
+ */
+ private $mergedAsset;
+
+ /**
+ * @var string
+ */
+ private $mergedContent;
+
+ /**
+ * @var UIAssetCacheBuster
+ */
+ protected $cacheBuster;
+
+ /**
+ * @param UIAsset $mergedAsset
+ * @param UIAssetFetcher $assetFetcher
+ * @param UIAssetCacheBuster $cacheBuster
+ */
+ function __construct($mergedAsset, $assetFetcher, $cacheBuster)
+ {
+ $this->mergedAsset = $mergedAsset;
+ $this->assetFetcher = $assetFetcher;
+ $this->cacheBuster = $cacheBuster;
+ }
+
+ public function generateFile()
+ {
+ if(!$this->shouldGenerate())
+ return;
+
+ $this->mergedContent = $this->getMergedAssets();
+
+ $this->postEvent($this->mergedContent);
+
+ $this->adjustPaths();
+
+ $this->addPreamble();
+
+ $this->writeContentToFile();
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getMergedAssets();
+
+ /**
+ * @return string
+ */
+ abstract protected function generateCacheBuster();
+
+ /**
+ * @return string
+ */
+ abstract protected function getPreamble();
+
+ /**
+ * @return string
+ */
+ abstract protected function getFileSeparator();
+
+ /**
+ * @param UIAsset $uiAsset
+ * @return string
+ */
+ abstract protected function processFileContent($uiAsset);
+
+ /**
+ * @param string $mergedContent
+ */
+ abstract protected function postEvent(&$mergedContent);
+
+ protected function getConcatenatedAssets()
+ {
+ if(empty($this->mergedContent))
+ $this->concatenateAssets();
+
+ return $this->mergedContent;
+ }
+
+ private function concatenateAssets()
+ {
+ $mergedContent = '';
+
+ foreach ($this->getAssetCatalog()->getAssets() as $uiAsset) {
+
+ $uiAsset->validateFile();
+ $content = $this->processFileContent($uiAsset);
+
+ $mergedContent .= $this->getFileSeparator() . $content;
+ }
+
+ $this->mergedContent = $mergedContent;
+ }
+
+ /**
+ * @return string[]
+ */
+ protected function getPlugins()
+ {
+ return $this->assetFetcher->getPlugins();
+ }
+
+ /**
+ * @return UIAssetCatalog
+ */
+ protected function getAssetCatalog()
+ {
+ return $this->assetFetcher->getCatalog();
+ }
+
+ /**
+ * @return boolean
+ */
+ private function shouldGenerate()
+ {
+ if(!$this->mergedAsset->exists())
+ return true;
+
+ if($this->shouldCompareExistingVersion()) {
+
+ return !$this->isFileUpToDate();
+ }
+
+ return false;
+ }
+
+ /**
+ * @return boolean
+ */
+ private function isFileUpToDate()
+ {
+ $f = fopen($this->mergedAsset->getAbsoluteLocation(), 'r');
+ $firstLine = fgets($f);
+ fclose($f);
+
+ if (!empty($firstLine) && trim($firstLine) == trim($this->getCacheBusterValue())) {
+ return true;
+ }
+
+ // Some CSS file in the merge, has changed since last merged asset was generated
+ // Note: we do not detect changes in @import'ed LESS files
+ return false;
+ }
+
+ /**
+ * @return boolean
+ */
+ private function isMergedAssetsDisabled()
+ {
+ return AssetManager::getInstance()->isMergedAssetsDisabled();
+ }
+
+ private function adjustPaths()
+ {
+ $this->mergedContent = $this->assetFetcher->getTheme()->rewriteAssetsPathToTheme($this->mergedContent);
+ }
+
+ private function writeContentToFile()
+ {
+ $this->mergedAsset->writeContent($this->mergedContent);
+ }
+
+ /**
+ * @return string
+ */
+ protected function getCacheBusterValue()
+ {
+ if(empty($this->cacheBusterValue))
+ $this->cacheBusterValue = $this->generateCacheBuster();
+
+ return $this->cacheBusterValue;
+ }
+
+ private function addPreamble()
+ {
+ $this->mergedContent = $this->getPreamble() . $this->mergedContent;
+ }
+
+ /**
+ * @return boolean
+ */
+ private function shouldCompareExistingVersion()
+ {
+ return $this->isMergedAssetsDisabled();
+ }
+}
diff --git a/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php
new file mode 100644
index 0000000000..f501280ab1
--- /dev/null
+++ b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAssetMerger;
+
+use Piwik\AssetManager\UIAsset;
+use Piwik\AssetManager\UIAssetCacheBuster;
+use Piwik\AssetManager\UIAssetFetcher\JScriptUIAssetFetcher;
+use Piwik\AssetManager\UIAssetMerger;
+use Piwik\AssetManager;
+use Piwik\AssetManager\UIAssetMinifier;
+use Piwik\Piwik;
+
+class JScriptUIAssetMerger extends UIAssetMerger
+{
+ /**
+ * @var UIAssetMinifier
+ */
+ private $assetMinifier;
+
+ /**
+ * @param UIAsset $mergedAsset
+ * @param JScriptUIAssetFetcher $assetFetcher
+ * @param UIAssetCacheBuster $cacheBuster
+ */
+ function __construct($mergedAsset, $assetFetcher, $cacheBuster)
+ {
+ parent::__construct($mergedAsset, $assetFetcher, $cacheBuster);
+
+ $this->assetMinifier = UIAssetMinifier::getInstance();
+ }
+
+ protected function getMergedAssets()
+ {
+ $concatenatedAssets = $this->getConcatenatedAssets();
+
+ return str_replace("\n", "\r\n", $concatenatedAssets);
+ }
+
+ protected function generateCacheBuster()
+ {
+ $cacheBuster = $this->cacheBuster->piwikVersionBasedCacheBuster($this->getPlugins());
+ return "/* Piwik Javascript - cb=" . $cacheBuster . "*/\r\n";
+ }
+
+ protected function getPreamble()
+ {
+ return $this->getCacheBusterValue();
+ }
+
+ protected function postEvent(&$mergedContent)
+ {
+ $plugins = $this->getPlugins();
+
+ if(!empty($plugins)) {
+
+ /**
+ * Triggered after all the JavaScript files Piwik uses are minified and merged into a
+ * single file, but before the merged JavaScript is written to disk.
+ *
+ * Plugins can use this event to modify merged JavaScript or do something else
+ * with it.
+ *
+ * @param string $mergedContent The minified and merged JavaScript.
+ */
+ Piwik::postEvent('AssetManager.filterMergedJavaScripts', array(&$mergedContent), null, $plugins);
+ }
+ }
+
+ public function getFileSeparator()
+ {
+ return PHP_EOL;
+ }
+
+ protected function processFileContent($uiAsset)
+ {
+ $content = $uiAsset->getContent();
+
+ if (!$this->assetMinifier->isMinifiedJs($content))
+ $content = $this->assetMinifier->minifyJs($content);
+
+ return $content;
+ }
+}
diff --git a/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php
new file mode 100644
index 0000000000..ae02127448
--- /dev/null
+++ b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\AssetManager\UIAssetMerger;
+
+use Exception;
+use Piwik\AssetManager\UIAsset;
+use Piwik\AssetManager\UIAssetMerger;
+use Piwik\Piwik;
+use lessc;
+
+class StylesheetUIAssetMerger extends UIAssetMerger
+{
+ /**
+ * @var lessc
+ */
+ private $lessCompiler;
+
+ function __construct($mergedAsset, $assetFetcher, $cacheBuster)
+ {
+ parent::__construct($mergedAsset, $assetFetcher, $cacheBuster);
+
+ $this->lessCompiler = self::getLessCompiler();
+ }
+
+ protected function getMergedAssets()
+ {
+ foreach($this->getAssetCatalog()->getAssets() as $uiAsset) {
+ $this->lessCompiler->addImportDir(dirname($uiAsset->getAbsoluteLocation()));
+ }
+
+ return $this->lessCompiler->compile($this->getConcatenatedAssets());
+ }
+
+ /**
+ * @return lessc
+ * @throws Exception
+ */
+ private static function getLessCompiler()
+ {
+ if (!class_exists("lessc")) {
+ throw new Exception("Less was added to composer during 2.0. ==> Execute this command to update composer packages: \$ php composer.phar install");
+ }
+ $less = new lessc();
+ return $less;
+ }
+
+ protected function generateCacheBuster()
+ {
+ $fileHash = $this->cacheBuster->md5BasedCacheBuster($this->getConcatenatedAssets());
+ return "/* compile_me_once=$fileHash */";
+ }
+
+ protected function getPreamble()
+ {
+ return $this->getCacheBusterValue() . "\n"
+ . "/* Piwik CSS file is compiled with Less. You may be interested in writing a custom Theme for Piwik! */\n";
+ }
+
+ protected function postEvent(&$mergedContent)
+ {
+ /**
+ * Triggered after all less stylesheets are compiled to CSS, minified and merged into
+ * one file, but before the generated CSS is written to disk.
+ *
+ * This event can be used to modify merged CSS.
+ *
+ * @param string $mergedContent The merged and minified CSS.
+ */
+ Piwik::postEvent('AssetManager.filterMergedStylesheets', array(&$mergedContent));
+ }
+
+ public function getFileSeparator()
+ {
+ return '';
+ }
+
+ protected function processFileContent($uiAsset)
+ {
+ return $this->rewriteCssPathsDirectives($uiAsset);
+ }
+
+ /**
+ * Rewrite css url directives
+ * - rewrites paths defined relatively to their css/less definition file
+ * - rewrite windows directory separator \\ to /
+ *
+ * @param UIAsset $uiAsset
+ * @return string
+ */
+ private function rewriteCssPathsDirectives($uiAsset)
+ {
+ static $rootDirectoryLength = null;
+ if (is_null($rootDirectoryLength)) {
+ $rootDirectoryLength = self::countDirectoriesInPathToRoot($uiAsset);
+ }
+
+ $baseDirectory = dirname($uiAsset->getRelativeLocation());
+ $content = preg_replace_callback(
+ "/(url\(['\"]?)([^'\")]*)/",
+ function ($matches) use ($rootDirectoryLength, $baseDirectory) {
+
+ $absolutePath = realpath(PIWIK_USER_PATH . "/$baseDirectory/" . $matches[2]);
+
+ if($absolutePath) {
+
+ $relativePath = substr($absolutePath, $rootDirectoryLength);
+
+ $relativePath = str_replace('\\', '/', $relativePath);
+
+ return $matches[1] . $relativePath;
+
+ } else {
+ return $matches[1] . $matches[2];
+ }
+ },
+ $uiAsset->getContent()
+ );
+ return $content;
+ }
+
+ /**
+ * @param UIAsset $uiAsset
+ * @return int
+ */
+ protected function countDirectoriesInPathToRoot($uiAsset)
+ {
+ $rootDirectory = realpath($uiAsset->getBaseDirectory());
+ if ($rootDirectory != '/' && substr_compare($rootDirectory, '/', -1)) {
+ $rootDirectory .= '/';
+ }
+ $rootDirectoryLen = strlen($rootDirectory);
+ return $rootDirectoryLen;
+ }
+}
diff --git a/core/AssetManager/UIAssetMinifier.php b/core/AssetManager/UIAssetMinifier.php
new file mode 100644
index 0000000000..fe29f1ec61
--- /dev/null
+++ b/core/AssetManager/UIAssetMinifier.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @method static \Piwik\AssetManager\UIAssetMinifier getInstance()
+ * @package Piwik
+ */
+namespace Piwik\AssetManager;
+
+use Exception;
+use Piwik\Singleton;
+use JShrink\Minifier;
+
+class UIAssetMinifier extends Singleton
+{
+ const MINIFIED_JS_RATIO = 100;
+
+ protected function __construct()
+ {
+ self::validateDependency();
+ parent::__construct();
+ }
+
+
+ /**
+ * Indicates if the provided JavaScript content has already been minified or not.
+ * The heuristic is based on a custom ratio : (size of file) / (number of lines).
+ * The threshold (100) has been found empirically on existing files :
+ * - the ratio never exceeds 50 for non-minified content and
+ * - it never goes under 150 for minified content.
+ *
+ * @param string $content Contents of the JavaScript file
+ * @return boolean
+ */
+ public function isMinifiedJs($content)
+ {
+ $lineCount = substr_count($content, "\n");
+ if ($lineCount == 0) {
+ return true;
+ }
+
+ $contentSize = strlen($content);
+
+ $ratio = $contentSize / $lineCount;
+
+ return $ratio > self::MINIFIED_JS_RATIO;
+ }
+
+ /**
+ * @param string $content
+ * @return string
+ */
+ public function minifyJs($content)
+ {
+ return Minifier::minify($content);
+ }
+
+ private static function validateDependency()
+ {
+ if (!class_exists("JShrink\Minifier"))
+ throw new Exception("JShrink dependency is managed using Composer.");
+ }
+}
diff --git a/core/Db/BatchInsert.php b/core/Db/BatchInsert.php
index 1a9563be95..89a003490f 100644
--- a/core/Db/BatchInsert.php
+++ b/core/Db/BatchInsert.php
@@ -61,7 +61,7 @@ class BatchInsert
*/
public static function tableInsertBatch($tableName, $fields, $values, $throwException = false)
{
- $filePath = PIWIK_USER_PATH . '/' . AssetManager::MERGED_FILE_DIR . $tableName . '-' . Common::generateUniqId() . '.csv';
+ $filePath = PIWIK_USER_PATH . '/tmp/assets/' . $tableName . '-' . Common::generateUniqId() . '.csv';
$filePath = SettingsPiwik::rewriteTmpPathWithHostname($filePath);
if (Db::get()->hasBulkLoader()) {
diff --git a/core/Filesystem.php b/core/Filesystem.php
index aea8f5e1c8..37ea89e999 100644
--- a/core/Filesystem.php
+++ b/core/Filesystem.php
@@ -24,9 +24,9 @@ class Filesystem
* Called on Core install, update, plugin enable/disable
* Will clear all cache that could be affected by the change in configuration being made
*/
- public static function deleteAllCacheOnUpdate()
+ public static function deleteAllCacheOnUpdate($pluginName = false)
{
- AssetManager::removeMergedAssets();
+ AssetManager::getInstance()->removeMergedAssets($pluginName);
View::clearCompiledTemplates();
Cache::deleteTrackerCache();
}
diff --git a/core/Plugin.php b/core/Plugin.php
index 53fe0f8559..d2ce8d11dd 100644
--- a/core/Plugin.php
+++ b/core/Plugin.php
@@ -262,7 +262,7 @@ class Plugin
*
* @return bool
*/
- final public function isTheme()
+ public function isTheme()
{
$info = $this->getInformation();
return !empty($info['theme']) && (bool)$info['theme'];
diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php
index 40ca5977ca..d067333b26 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -20,6 +20,7 @@ use Piwik\Plugin;
use Piwik\Singleton;
use Piwik\Translate;
use Piwik\Updater;
+use Piwik\Theme;
require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php';
@@ -219,7 +220,7 @@ class Manager extends Singleton
PiwikConfig::getInstance()->forceSave();
\Piwik\Settings\Manager::cleanupPluginSettings($pluginName);
- Filesystem::deleteAllCacheOnUpdate();
+ $this->clearCache($pluginName);
self::deletePluginFromFilesystem($pluginName);
if ($this->isPluginInFilesystem($pluginName)) {
@@ -228,6 +229,14 @@ class Manager extends Singleton
return true;
}
+ /**
+ * @param string $pluginName
+ */
+ private function clearCache($pluginName)
+ {
+ Filesystem::deleteAllCacheOnUpdate($pluginName);
+ }
+
public static function deletePluginFromFilesystem($plugin)
{
Filesystem::unlinkRecursive(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin, $deleteRootToo = true);
@@ -255,7 +264,7 @@ class Manager extends Singleton
$this->removePluginFromTrackerConfig($pluginName);
PiwikConfig::getInstance()->forceSave();
- Filesystem::deleteAllCacheOnUpdate();
+ $this->clearCache($pluginName);
return $plugins;
}
@@ -329,7 +338,7 @@ class Manager extends Singleton
$this->updatePluginsConfig($plugins);
PiwikConfig::getInstance()->forceSave();
- Filesystem::deleteAllCacheOnUpdate();
+ $this->clearCache($pluginName);
$this->pluginsToLoad[] = $pluginName;
}
@@ -369,6 +378,22 @@ class Manager extends Singleton
return $theme;
}
+ /**
+ * @param string $themeName
+ * @throws \Exception
+ * @return Theme
+ */
+ public function getTheme($themeName)
+ {
+ $plugins = $this->getLoadedPlugins();
+
+ foreach ($plugins as $plugin)
+ if ($plugin->isTheme() && $plugin->getPluginName() == $themeName)
+ return new Theme($plugin);
+
+ throw new \Exception('Theme not found : ' . $themeName);
+ }
+
public function getNumberOfActivatedPlugins()
{
$counter = 0;
diff --git a/core/ProxyHttp.php b/core/ProxyHttp.php
index b35d749342..692e8219bb 100644
--- a/core/ProxyHttp.php
+++ b/core/ProxyHttp.php
@@ -91,8 +91,7 @@ class ProxyHttp
// optional compression
$compressed = false;
$encoding = '';
- $compressedFileLocation = PIWIK_USER_PATH . AssetManager::COMPRESSED_FILE_LOCATION . basename($file);
- $compressedFileLocation = SettingsPiwik::rewriteTmpPathWithHostname($compressedFileLocation);
+ $compressedFileLocation = AssetManager::getInstance()->getAssetDirectory() . '/' . basename($file);
$phpOutputCompressionEnabled = ProxyHttp::isPhpOutputCompressed();
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && !$phpOutputCompressionEnabled) {
diff --git a/core/Theme.php b/core/Theme.php
index 699783ae46..785d37382f 100644
--- a/core/Theme.php
+++ b/core/Theme.php
@@ -10,6 +10,8 @@
*/
namespace Piwik;
+use Piwik\Plugin\Manager;
+
/**
* This class contains logic to make Themes work beautifully.
*
@@ -23,10 +25,21 @@ class Theme
/** @var \Piwik\Plugin */
private $theme;
- public function __construct()
+ /**
+ * @var Plugin $plugin
+ */
+ public function __construct($plugin = false)
+ {
+ $this->createThemeFromPlugin($plugin ? $plugin : Manager::getInstance()->getThemeEnabled());
+ }
+
+ /**
+ * @param Plugin $plugin
+ */
+ private function createThemeFromPlugin($plugin)
{
- $this->theme = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled();
- $this->themeName = $this->theme->getPluginName();
+ $this->theme = $plugin;
+ $this->themeName = $plugin->getPluginName();
}
public function getStylesheet()
@@ -123,4 +136,11 @@ class Theme
return $source;
}
+ /**
+ * @return string
+ */
+ public function getThemeName()
+ {
+ return $this->themeName;
+ }
} \ No newline at end of file
diff --git a/core/Twig.php b/core/Twig.php
index 69a6501d90..3f15148985 100644
--- a/core/Twig.php
+++ b/core/Twig.php
@@ -104,9 +104,9 @@ class Twig
$assetType = strtolower($params['type']);
switch ($assetType) {
case 'css':
- return AssetManager::getCssAssets();
+ return AssetManager::getInstance()->getCssInclusionDirective();
case 'js':
- return AssetManager::getJsAssets();
+ return AssetManager::getInstance()->getJsInclusionDirective();
default:
throw new Exception("The twig function includeAssets 'type' parameter needs to be either 'css' or 'js'.");
}
diff --git a/core/View.php b/core/View.php
index 105434e00a..9a9e105da0 100644
--- a/core/View.php
+++ b/core/View.php
@@ -11,6 +11,7 @@
namespace Piwik;
use Exception;
+use Piwik\AssetManager\UIAssetCacheBuster;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\Plugins\UsersManager\API as APIUsersManager;
use Piwik\View\ViewInterface;
@@ -259,7 +260,7 @@ class View implements ViewInterface
protected function applyFilter_cacheBuster($output)
{
- $cacheBuster = AssetManager::generateAssetsCacheBuster();
+ $cacheBuster = UIAssetCacheBuster::getInstance()->piwikVersionBasedCacheBuster();
$tag = 'cb=' . $cacheBuster;
$pattern = array(