forked from vergnet/site-accueil-insa
521 lines
16 KiB
PHP
521 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*
|
|
*/
|
|
namespace Piwik;
|
|
|
|
use Exception;
|
|
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\PluginUmdAssetFetcher;
|
|
use Piwik\AssetManager\UIAssetFetcher;
|
|
use Piwik\AssetManager\UIAssetMerger\JScriptUIAssetMerger;
|
|
use Piwik\AssetManager\UIAssetMerger\StylesheetUIAssetMerger;
|
|
use Piwik\Container\StaticContainer;
|
|
use Piwik\Plugin\Manager;
|
|
|
|
/**
|
|
* AssetManager is the class used to manage the inclusion of UI assets:
|
|
* JavaScript and CSS files.
|
|
*
|
|
* It performs the following actions:
|
|
* - Identifies required assets
|
|
* - Includes assets in the rendered HTML page
|
|
* - Manages asset merging and minifying
|
|
* - Manages server-side cache
|
|
*
|
|
* Whether assets are included individually or as merged files is defined by
|
|
* the global option 'disable_merged_assets'. See the documentation in the global
|
|
* config for more information.
|
|
*/
|
|
class AssetManager extends Singleton
|
|
{
|
|
const MERGED_CSS_FILE = "asset_manager_global_css.css";
|
|
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 JS_DEFER_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\" defer></script>\n";
|
|
const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
|
|
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";
|
|
const GET_JS_UMD_MODULE_ACTION = "index.php?module=Proxy&action=getUmdJs&chunk=";
|
|
|
|
/**
|
|
* @var UIAssetCacheBuster
|
|
*/
|
|
private $cacheBuster;
|
|
|
|
/**
|
|
* @var UIAssetFetcher
|
|
*/
|
|
private $minimalStylesheetFetcher;
|
|
|
|
/**
|
|
* @var Theme
|
|
*/
|
|
private $theme;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->cacheBuster = UIAssetCacheBuster::getInstance();
|
|
|
|
$this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array(), array(), $this->theme);
|
|
|
|
$theme = Manager::getInstance()->getThemeEnabled();
|
|
if (!empty($theme)) {
|
|
$this->theme = new Theme();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @return AssetManager
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
$assetManager = parent::getInstance();
|
|
|
|
/**
|
|
* Triggered when creating an instance of the asset manager. Lets you overwrite the
|
|
* asset manager behavior.
|
|
*
|
|
* @param AssetManager &$assetManager
|
|
*
|
|
* @ignore
|
|
* This event is not a public event since we don't want to make the asset manager itself public
|
|
* API
|
|
*/
|
|
Piwik::postEvent('AssetManager.makeNewAssetManagerObject', array(&$assetManager));
|
|
|
|
return $assetManager;
|
|
}
|
|
|
|
/**
|
|
* @param UIAssetCacheBuster $cacheBuster
|
|
*/
|
|
public function setCacheBuster($cacheBuster)
|
|
{
|
|
$this->cacheBuster = $cacheBuster;
|
|
}
|
|
|
|
/**
|
|
* @param UIAssetFetcher $minimalStylesheetFetcher
|
|
*/
|
|
public function setMinimalStylesheetFetcher($minimalStylesheetFetcher)
|
|
{
|
|
$this->minimalStylesheetFetcher = $minimalStylesheetFetcher;
|
|
}
|
|
|
|
/**
|
|
* @param Theme $theme
|
|
*/
|
|
public function setTheme($theme)
|
|
{
|
|
$this->theme = $theme;
|
|
}
|
|
|
|
/**
|
|
* Return CSS file inclusion directive(s) using the markup <link>
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCssInclusionDirective()
|
|
{
|
|
return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
|
|
}
|
|
|
|
/**
|
|
* Return JS file inclusion directive(s) using the markup <script>
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getJsInclusionDirective()
|
|
{
|
|
$result = "<script type=\"text/javascript\">\n" . StaticContainer::get('Piwik\Translation\Translator')->getJavascriptTranslations() . "\n</script>";
|
|
|
|
if ($this->isMergedAssetsDisabled()) {
|
|
$this->getMergedCoreJSAsset()->delete();
|
|
$this->getMergedNonCoreJSAsset()->delete();
|
|
|
|
$result .= $this->getIndividualCoreAndNonCoreJsIncludes();
|
|
} else {
|
|
$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);
|
|
|
|
$result .= $this->getPluginUmdChunks();
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
protected function getPluginUmdChunks()
|
|
{
|
|
$fetcher = $this->getPluginUmdJScriptFetcher();
|
|
|
|
$chunks = $fetcher->getChunkFiles();
|
|
|
|
$result = '';
|
|
foreach ($chunks as $chunk) {
|
|
$src = self::GET_JS_UMD_MODULE_ACTION . urlencode($chunk->getChunkName());
|
|
$result .= sprintf(self::JS_DEFER_IMPORT_DIRECTIVE, $src);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Return the base.less compiled to css
|
|
*
|
|
* @return UIAsset
|
|
*/
|
|
public function getCompiledBaseCss()
|
|
{
|
|
$mergedAsset = new InMemoryUIAsset();
|
|
|
|
$assetMerger = new StylesheetUIAssetMerger($mergedAsset, $this->minimalStylesheetFetcher, $this->cacheBuster);
|
|
|
|
$assetMerger->generateFile();
|
|
|
|
return $mergedAsset;
|
|
}
|
|
|
|
/**
|
|
* Return the css merged file absolute location.
|
|
* If there is none, the generation process will be triggered.
|
|
*
|
|
* @return UIAsset
|
|
*/
|
|
public function getMergedStylesheet()
|
|
{
|
|
$mergedAsset = $this->getMergedStylesheetAsset();
|
|
|
|
$assetFetcher = new StylesheetUIAssetFetcher(Manager::getInstance()->getLoadedPluginsName(), $this->theme);
|
|
|
|
$assetMerger = new StylesheetUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
|
|
|
|
$assetMerger->generateFile();
|
|
|
|
return $mergedAsset;
|
|
}
|
|
|
|
/**
|
|
* Return the core js merged file absolute location.
|
|
* If there is none, the generation process will be triggered.
|
|
*
|
|
* @return UIAsset
|
|
*/
|
|
public function getMergedCoreJavaScript()
|
|
{
|
|
return $this->getMergedJavascript($this->getCoreJScriptFetcher(), $this->getMergedCoreJSAsset());
|
|
}
|
|
|
|
/**
|
|
* Return the non core js merged file absolute location.
|
|
* If there is none, the generation process will be triggered.
|
|
*
|
|
* @return UIAsset
|
|
*/
|
|
public function getMergedNonCoreJavaScript()
|
|
{
|
|
return $this->getMergedJavascript($this->getNonCoreJScriptFetcher(), $this->getMergedNonCoreJSAsset());
|
|
}
|
|
|
|
/**
|
|
* Return a chunk JS merged file absolute location.
|
|
* If there is none, the generation process will be triggered.
|
|
*
|
|
* @param string $chunk The name of the chunk. Will either be a plugin name or an integer.
|
|
* @return UIAsset
|
|
*/
|
|
public function getMergedJavaScriptChunk($chunk)
|
|
{
|
|
$assetFetcher = $this->getPluginUmdJScriptFetcher($chunk);
|
|
$outputFile = $assetFetcher->getRequestedChunkOutputFile();
|
|
|
|
return $this->getMergedJavascript($assetFetcher, $this->getMergedUIAsset($outputFile));
|
|
}
|
|
|
|
/**
|
|
* @param boolean|"all" $core
|
|
* @return string[]
|
|
*/
|
|
public function getLoadedPlugins($core)
|
|
{
|
|
$loadedPlugins = array();
|
|
|
|
foreach (Manager::getInstance()->getPluginsLoadedAndActivated() as $plugin) {
|
|
$pluginName = $plugin->getPluginName();
|
|
$pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName);
|
|
|
|
if ($core === 'all' || ($pluginIsCore && $core) || (!$pluginIsCore && !$core)) {
|
|
$loadedPlugins[] = $pluginName;
|
|
}
|
|
}
|
|
|
|
return $loadedPlugins;
|
|
}
|
|
|
|
/**
|
|
* Remove previous merged assets
|
|
*/
|
|
public function removeMergedAssets($pluginName = false)
|
|
{
|
|
$assetsToRemove = array($this->getMergedStylesheetAsset());
|
|
|
|
if ($pluginName) {
|
|
if ($this->pluginContainsJScriptAssets($pluginName)) {
|
|
if (Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
|
|
$assetsToRemove[] = $this->getMergedCoreJSAsset();
|
|
} else {
|
|
$assetsToRemove[] = $this->getMergedNonCoreJSAsset();
|
|
}
|
|
|
|
$assetFetcher = $this->getPluginUmdJScriptFetcher();
|
|
foreach ($assetFetcher->getChunkFiles() as $chunk) {
|
|
$files = $chunk->getFiles();
|
|
|
|
$foundInChunk = false;
|
|
foreach ($files as $file) {
|
|
if (strpos($file, "/$pluginName.umd.") !== false) {
|
|
$foundInChunk = true;
|
|
}
|
|
}
|
|
|
|
if ($foundInChunk) {
|
|
$outputFile = $chunk->getOutputFile();
|
|
$asset = $this->getMergedUIAsset($outputFile);
|
|
if ($asset->exists()) {
|
|
$assetsToRemove[] = $asset;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$assetsToRemove[] = $this->getMergedCoreJSAsset();
|
|
$assetsToRemove[] = $this->getMergedNonCoreJSAsset();
|
|
|
|
$assetFetcher = $this->getPluginUmdJScriptFetcher();
|
|
foreach ($assetFetcher->getChunkFiles() as $chunk) {
|
|
$outputFile = $chunk->getOutputFile();
|
|
$asset = $this->getMergedUIAsset($outputFile);
|
|
if ($asset->exists()) {
|
|
$assetsToRemove[] = $asset;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->removeAssets($assetsToRemove);
|
|
}
|
|
|
|
/**
|
|
* Check if the merged file directory exists and is writable.
|
|
*
|
|
* @return string The directory location
|
|
* @throws Exception if directory is not writable.
|
|
*/
|
|
public function getAssetDirectory()
|
|
{
|
|
$mergedFileDirectory = StaticContainer::get('path.tmp') . '/assets';
|
|
|
|
if (!is_dir($mergedFileDirectory)) {
|
|
Filesystem::mkdir($mergedFileDirectory);
|
|
}
|
|
|
|
if (!is_writable($mergedFileDirectory)) {
|
|
throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
|
|
}
|
|
|
|
return $mergedFileDirectory;
|
|
}
|
|
|
|
/**
|
|
* Return the global option disable_merged_assets
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function isMergedAssetsDisabled()
|
|
{
|
|
if (Config::getInstance()->Development['disable_merged_assets'] == 1) {
|
|
return true;
|
|
}
|
|
|
|
if (isset($_GET['disable_merged_assets']) && $_GET['disable_merged_assets'] == 1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param UIAssetFetcher $assetFetcher
|
|
* @param UIAsset $mergedAsset
|
|
* @return UIAsset
|
|
*/
|
|
private function getMergedJavascript($assetFetcher, $mergedAsset)
|
|
{
|
|
$assetMerger = new JScriptUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
|
|
|
|
$assetMerger->generateFile();
|
|
|
|
return $mergedAsset;
|
|
}
|
|
|
|
/**
|
|
* Return individual JS file inclusion directive(s) using the markup <script>
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getIndividualCoreAndNonCoreJsIncludes()
|
|
{
|
|
return
|
|
$this->getIndividualJsIncludesFromAssetFetcher($this->getCoreJScriptFetcher()) .
|
|
$this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher()) .
|
|
$this->getIndividualJsIncludesFromAssetFetcher($this->getPluginUmdJScriptFetcher());
|
|
}
|
|
|
|
/**
|
|
* @param UIAssetFetcher $assetFetcher
|
|
* @return string
|
|
*/
|
|
protected function getIndividualJsIncludesFromAssetFetcher($assetFetcher)
|
|
{
|
|
$jsIncludeString = '';
|
|
|
|
$assets = $assetFetcher->getCatalog()->getAssets();
|
|
|
|
foreach ($assets as $jsFile) {
|
|
$jsFile->validateFile();
|
|
$jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile->getRelativeLocation());
|
|
}
|
|
|
|
return $jsIncludeString;
|
|
}
|
|
|
|
private function getCoreJScriptFetcher()
|
|
{
|
|
return new JScriptUIAssetFetcher($this->getLoadedPlugins(true), $this->theme);
|
|
}
|
|
|
|
protected function getNonCoreJScriptFetcher()
|
|
{
|
|
return new JScriptUIAssetFetcher($this->getLoadedPlugins(false), $this->theme);
|
|
}
|
|
|
|
protected function getPluginUmdJScriptFetcher($chunk = null)
|
|
{
|
|
return new PluginUmdAssetFetcher($this->getLoadedPlugins('all'), $this->theme, $chunk);
|
|
}
|
|
|
|
/**
|
|
* @param string $pluginName
|
|
* @return boolean
|
|
*/
|
|
private function pluginContainsJScriptAssets($pluginName)
|
|
{
|
|
$fetcher = new JScriptUIAssetFetcher(array($pluginName), $this->theme);
|
|
|
|
try {
|
|
$assets = $fetcher->getCatalog()->getAssets();
|
|
} catch (\Exception $e) {
|
|
// This can happen when a plugin is not valid (eg. Piwik 1.x format)
|
|
// When posting the event to the plugin, it returns an exception "Plugin has not been loaded"
|
|
return false;
|
|
}
|
|
|
|
$pluginManager = Manager::getInstance();
|
|
$plugin = null;
|
|
if ($pluginManager->isPluginLoaded($pluginName)) {
|
|
$plugin = $pluginManager->getLoadedPlugin($pluginName);
|
|
}
|
|
|
|
if ($plugin && $plugin->isTheme()) {
|
|
$theme = $pluginManager->getTheme($pluginName);
|
|
|
|
$javaScriptFiles = $theme->getJavaScriptFiles();
|
|
|
|
if (!empty($javaScriptFiles)) {
|
|
$assets = array_merge($assets, $javaScriptFiles);
|
|
}
|
|
}
|
|
|
|
return !empty($assets);
|
|
}
|
|
|
|
/**
|
|
* @param UIAsset[] $uiAssets
|
|
*/
|
|
public function removeAssets($uiAssets)
|
|
{
|
|
foreach ($uiAssets as $uiAsset) {
|
|
$uiAsset->delete();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return UIAsset
|
|
*/
|
|
public function getMergedStylesheetAsset()
|
|
{
|
|
return $this->getMergedUIAsset(self::MERGED_CSS_FILE);
|
|
}
|
|
|
|
/**
|
|
* @return UIAsset
|
|
*/
|
|
private function getMergedCoreJSAsset()
|
|
{
|
|
return $this->getMergedUIAsset(self::MERGED_CORE_JS_FILE);
|
|
}
|
|
|
|
/**
|
|
* @return UIAsset
|
|
*/
|
|
protected function getMergedNonCoreJSAsset()
|
|
{
|
|
return $this->getMergedUIAsset(self::MERGED_NON_CORE_JS_FILE);
|
|
}
|
|
|
|
/**
|
|
* @param string $fileName
|
|
* @return UIAsset
|
|
*/
|
|
private function getMergedUIAsset($fileName)
|
|
{
|
|
return new OnDiskUIAsset($this->getAssetDirectory(), $fileName);
|
|
}
|
|
|
|
public static function compileCustomStylesheets($files)
|
|
{
|
|
$assetManager = new AssetManager();
|
|
|
|
$fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
|
|
|
|
$assetManager->setMinimalStylesheetFetcher($fetcher);
|
|
|
|
return $assetManager->getCompiledBaseCss()->getContent();
|
|
}
|
|
|
|
public static function compileCustomJs($files)
|
|
{
|
|
$mergedAsset = new InMemoryUIAsset();
|
|
$fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
|
|
|
|
$cacheBuster = UIAssetCacheBuster::getInstance();
|
|
|
|
$assetMerger = new JScriptUIAssetMerger($mergedAsset, $fetcher, $cacheBuster);
|
|
$assetMerger->generateFile();
|
|
|
|
return $mergedAsset->getContent();
|
|
}
|
|
}
|