PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.3
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / AssetManager.php
matomo / app / core Last commit date
API 2 years ago Access 2 years ago Application 2 years ago Archive 2 years ago ArchiveProcessor 2 years ago Archiver 2 years ago AssetManager 2 years ago Auth 2 years ago Category 2 years ago Changes 2 years ago CliMulti 2 years ago Columns 2 years ago Concurrency 2 years ago Config 2 years ago Container 2 years ago CronArchive 2 years ago DataAccess 2 years ago DataFiles 2 years ago DataTable 2 years ago Db 2 years ago DeviceDetector 2 years ago Email 2 years ago Exception 2 years ago Http 2 years ago Intl 2 years ago Log 2 years ago Mail 2 years ago Measurable 2 years ago Menu 2 years ago Metrics 2 years ago Notification 2 years ago Period 2 years ago Plugin 2 years ago ProfessionalServices 2 years ago Report 2 years ago ReportRenderer 2 years ago Scheduler 2 years ago Segment 2 years ago Session 2 years ago Settings 2 years ago Tracker 2 years ago Translation 2 years ago Twig 2 years ago UpdateCheck 2 years ago Updater 2 years ago Updates 2 years ago Validators 2 years ago View 2 years ago ViewDataTable 2 years ago Visualization 2 years ago Widget 2 years ago .htaccess 2 years ago Access.php 2 years ago Archive.php 2 years ago ArchiveProcessor.php 2 years ago AssetManager.php 2 years ago Auth.php 2 years ago AuthResult.php 2 years ago BaseFactory.php 2 years ago Cache.php 2 years ago CacheId.php 2 years ago CliMulti.php 2 years ago Common.php 2 years ago Config.php 2 years ago Console.php 2 years ago Context.php 2 years ago Cookie.php 2 years ago CronArchive.php 2 years ago DI.php 2 years ago DataArray.php 2 years ago DataTable.php 2 years ago Date.php 2 years ago Db.php 2 years ago DbHelper.php 2 years ago Development.php 2 years ago ErrorHandler.php 2 years ago EventDispatcher.php 2 years ago ExceptionHandler.php 2 years ago FileIntegrity.php 2 years ago Filechecks.php 2 years ago Filesystem.php 2 years ago FrontController.php 2 years ago Http.php 2 years ago IP.php 2 years ago Log.php 2 years ago LogDeleter.php 2 years ago Mail.php 2 years ago Metrics.php 2 years ago NoAccessException.php 2 years ago Nonce.php 2 years ago Notification.php 2 years ago NumberFormatter.php 2 years ago Option.php 2 years ago Period.php 2 years ago Piwik.php 2 years ago Plugin.php 2 years ago Profiler.php 2 years ago ProxyHeaders.php 2 years ago ProxyHttp.php 2 years ago QuickForm2.php 2 years ago RankingQuery.php 2 years ago ReportRenderer.php 2 years ago Request.php 2 years ago Segment.php 2 years ago Sequence.php 2 years ago Session.php 2 years ago SettingsPiwik.php 2 years ago SettingsServer.php 2 years ago Singleton.php 2 years ago Site.php 2 years ago SiteContentDetector.php 2 years ago SupportedBrowser.php 2 years ago TCPDF.php 2 years ago Theme.php 2 years ago Timer.php 2 years ago Tracker.php 2 years ago Twig.php 2 years ago Unzip.php 2 years ago UpdateCheck.php 2 years ago Updater.php 2 years ago UpdaterErrorException.php 2 years ago Updates.php 2 years ago Url.php 2 years ago UrlHelper.php 2 years ago Version.php 2 years ago View.php 2 years ago bootstrap.php 2 years ago dispatch.php 2 years ago testMinimumPhpVersion.php 2 years ago
AssetManager.php
436 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 *
9 */
10 namespace Piwik;
11
12 use Exception;
13 use Piwik\AssetManager\UIAsset;
14 use Piwik\AssetManager\UIAsset\InMemoryUIAsset;
15 use Piwik\AssetManager\UIAsset\OnDiskUIAsset;
16 use Piwik\AssetManager\UIAssetCacheBuster;
17 use Piwik\AssetManager\UIAssetFetcher\JScriptUIAssetFetcher;
18 use Piwik\AssetManager\UIAssetFetcher\StaticUIAssetFetcher;
19 use Piwik\AssetManager\UIAssetFetcher\StylesheetUIAssetFetcher;
20 use Piwik\AssetManager\UIAssetFetcher\PluginUmdAssetFetcher;
21 use Piwik\AssetManager\UIAssetFetcher;
22 use Piwik\AssetManager\UIAssetMerger\JScriptUIAssetMerger;
23 use Piwik\AssetManager\UIAssetMerger\StylesheetUIAssetMerger;
24 use Piwik\Container\StaticContainer;
25 use Piwik\Plugin\Manager;
26 /**
27 * AssetManager is the class used to manage the inclusion of UI assets:
28 * JavaScript and CSS files.
29 *
30 * It performs the following actions:
31 * - Identifies required assets
32 * - Includes assets in the rendered HTML page
33 * - Manages asset merging and minifying
34 * - Manages server-side cache
35 *
36 * Whether assets are included individually or as merged files is defined by
37 * the global option 'disable_merged_assets'. See the documentation in the global
38 * config for more information.
39 */
40 class AssetManager extends \Piwik\Singleton
41 {
42 const MERGED_CSS_FILE = "asset_manager_global_css.css";
43 const MERGED_CORE_JS_FILE = "asset_manager_core_js.js";
44 const MERGED_NON_CORE_JS_FILE = "asset_manager_non_core_js.js";
45 const CSS_IMPORT_DIRECTIVE = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n";
46 const JS_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
47 const JS_DEFER_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\" defer></script>\n";
48 const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
49 const GET_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getCoreJs";
50 const GET_NON_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getNonCoreJs";
51 const GET_JS_UMD_MODULE_ACTION = "index.php?module=Proxy&action=getUmdJs&chunk=";
52 /**
53 * @var UIAssetCacheBuster
54 */
55 private $cacheBuster;
56 /**
57 * @var UIAssetFetcher
58 */
59 private $minimalStylesheetFetcher;
60 /**
61 * @var Theme
62 */
63 private $theme;
64 public function __construct()
65 {
66 $this->cacheBuster = UIAssetCacheBuster::getInstance();
67 $this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array(), array(), $this->theme);
68 $theme = Manager::getInstance()->getThemeEnabled();
69 if (!empty($theme)) {
70 $this->theme = new \Piwik\Theme();
71 }
72 }
73 /**
74 * @inheritDoc
75 * @return AssetManager
76 */
77 public static function getInstance()
78 {
79 $assetManager = parent::getInstance();
80 /**
81 * Triggered when creating an instance of the asset manager. Lets you overwrite the
82 * asset manager behavior.
83 *
84 * @param AssetManager &$assetManager
85 *
86 * @ignore
87 * This event is not a public event since we don't want to make the asset manager itself public
88 * API
89 */
90 \Piwik\Piwik::postEvent('AssetManager.makeNewAssetManagerObject', array(&$assetManager));
91 return $assetManager;
92 }
93 /**
94 * @param UIAssetCacheBuster $cacheBuster
95 */
96 public function setCacheBuster($cacheBuster)
97 {
98 $this->cacheBuster = $cacheBuster;
99 }
100 /**
101 * @param UIAssetFetcher $minimalStylesheetFetcher
102 */
103 public function setMinimalStylesheetFetcher($minimalStylesheetFetcher)
104 {
105 $this->minimalStylesheetFetcher = $minimalStylesheetFetcher;
106 }
107 /**
108 * @param Theme $theme
109 */
110 public function setTheme($theme)
111 {
112 $this->theme = $theme;
113 }
114 /**
115 * Return CSS file inclusion directive(s) using the markup <link>
116 *
117 * @return string
118 */
119 public function getCssInclusionDirective()
120 {
121 return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
122 }
123 /**
124 * Return JS file inclusion directive(s) using the markup <script>
125 *
126 * @param bool $deferJS
127 * @return string
128 */
129 public function getJsInclusionDirective(bool $deferJS = false) : string
130 {
131 $result = "<script type=\"text/javascript\">\n" . StaticContainer::get('Piwik\\Translation\\Translator')->getJavascriptTranslations() . "\n</script>";
132 if ($this->isMergedAssetsDisabled()) {
133 $this->getMergedCoreJSAsset()->delete();
134 $this->getMergedNonCoreJSAsset()->delete();
135 $result .= $this->getIndividualCoreAndNonCoreJsIncludes();
136 } else {
137 $result .= sprintf($deferJS ? self::JS_DEFER_IMPORT_DIRECTIVE : self::JS_IMPORT_DIRECTIVE, self::GET_CORE_JS_MODULE_ACTION);
138 $result .= sprintf($deferJS ? self::JS_DEFER_IMPORT_DIRECTIVE : self::JS_IMPORT_DIRECTIVE, self::GET_NON_CORE_JS_MODULE_ACTION);
139 $result .= $this->getPluginUmdChunks();
140 }
141 return $result;
142 }
143 protected function getPluginUmdChunks()
144 {
145 $fetcher = $this->getPluginUmdJScriptFetcher();
146 $chunks = $fetcher->getChunkFiles();
147 $result = '';
148 foreach ($chunks as $chunk) {
149 $src = self::GET_JS_UMD_MODULE_ACTION . urlencode($chunk->getChunkName());
150 $result .= sprintf(self::JS_DEFER_IMPORT_DIRECTIVE, $src);
151 }
152 return $result;
153 }
154 /**
155 * Return the base.less compiled to css
156 *
157 * @return UIAsset
158 */
159 public function getCompiledBaseCss()
160 {
161 $mergedAsset = new InMemoryUIAsset();
162 $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $this->minimalStylesheetFetcher, $this->cacheBuster);
163 $assetMerger->generateFile();
164 return $mergedAsset;
165 }
166 /**
167 * Return the css merged file absolute location.
168 * If there is none, the generation process will be triggered.
169 *
170 * @return UIAsset
171 */
172 public function getMergedStylesheet()
173 {
174 $mergedAsset = $this->getMergedStylesheetAsset();
175 $assetFetcher = new StylesheetUIAssetFetcher(Manager::getInstance()->getLoadedPluginsName(), $this->theme);
176 $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
177 $assetMerger->generateFile();
178 return $mergedAsset;
179 }
180 /**
181 * Return the core js merged file absolute location.
182 * If there is none, the generation process will be triggered.
183 *
184 * @return UIAsset
185 */
186 public function getMergedCoreJavaScript()
187 {
188 return $this->getMergedJavascript($this->getCoreJScriptFetcher(), $this->getMergedCoreJSAsset());
189 }
190 /**
191 * Return the non core js merged file absolute location.
192 * If there is none, the generation process will be triggered.
193 *
194 * @return UIAsset
195 */
196 public function getMergedNonCoreJavaScript()
197 {
198 return $this->getMergedJavascript($this->getNonCoreJScriptFetcher(), $this->getMergedNonCoreJSAsset());
199 }
200 /**
201 * Return a chunk JS merged file absolute location.
202 * If there is none, the generation process will be triggered.
203 *
204 * @param string $chunk The name of the chunk. Will either be a plugin name or an integer.
205 * @return UIAsset
206 */
207 public function getMergedJavaScriptChunk($chunk)
208 {
209 $assetFetcher = $this->getPluginUmdJScriptFetcher($chunk);
210 $outputFile = $assetFetcher->getRequestedChunkOutputFile();
211 return $this->getMergedJavascript($assetFetcher, $this->getMergedUIAsset($outputFile));
212 }
213 /**
214 * @param boolean|"all" $core
215 * @return string[]
216 */
217 public function getLoadedPlugins($core)
218 {
219 $loadedPlugins = array();
220 foreach (Manager::getInstance()->getPluginsLoadedAndActivated() as $plugin) {
221 $pluginName = $plugin->getPluginName();
222 $pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName);
223 if ($core === 'all' || $pluginIsCore && $core || !$pluginIsCore && !$core) {
224 $loadedPlugins[] = $pluginName;
225 }
226 }
227 return $loadedPlugins;
228 }
229 /**
230 * Remove previous merged assets
231 */
232 public function removeMergedAssets($pluginName = false)
233 {
234 $assetsToRemove = array($this->getMergedStylesheetAsset());
235 if ($pluginName) {
236 if ($this->pluginContainsJScriptAssets($pluginName)) {
237 if (Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
238 $assetsToRemove[] = $this->getMergedCoreJSAsset();
239 } else {
240 $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
241 }
242 $assetFetcher = $this->getPluginUmdJScriptFetcher();
243 foreach ($assetFetcher->getChunkFiles() as $chunk) {
244 $files = $chunk->getFiles();
245 $foundInChunk = false;
246 foreach ($files as $file) {
247 if (strpos($file, "/{$pluginName}.umd.") !== false) {
248 $foundInChunk = true;
249 }
250 }
251 if ($foundInChunk) {
252 $outputFile = $chunk->getOutputFile();
253 $asset = $this->getMergedUIAsset($outputFile);
254 if ($asset->exists()) {
255 $assetsToRemove[] = $asset;
256 }
257 break;
258 }
259 }
260 }
261 } else {
262 $assetsToRemove[] = $this->getMergedCoreJSAsset();
263 $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
264 $assetFetcher = $this->getPluginUmdJScriptFetcher();
265 foreach ($assetFetcher->getChunkFiles() as $chunk) {
266 $outputFile = $chunk->getOutputFile();
267 $asset = $this->getMergedUIAsset($outputFile);
268 if ($asset->exists()) {
269 $assetsToRemove[] = $asset;
270 }
271 }
272 }
273 $this->removeAssets($assetsToRemove);
274 }
275 /**
276 * Check if the merged file directory exists and is writable.
277 *
278 * @return string The directory location
279 * @throws Exception if directory is not writable.
280 */
281 public function getAssetDirectory()
282 {
283 $mergedFileDirectory = StaticContainer::get('path.tmp') . '/assets';
284 if (!is_dir($mergedFileDirectory)) {
285 \Piwik\Filesystem::mkdir($mergedFileDirectory);
286 }
287 if (!is_writable($mergedFileDirectory)) {
288 throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
289 }
290 return $mergedFileDirectory;
291 }
292 /**
293 * Return the global option disable_merged_assets
294 *
295 * @return boolean
296 */
297 public function isMergedAssetsDisabled()
298 {
299 if (\Piwik\Config::getInstance()->Development['disable_merged_assets'] == 1) {
300 return true;
301 }
302 if (isset($_GET['disable_merged_assets']) && $_GET['disable_merged_assets'] == 1) {
303 return true;
304 }
305 return false;
306 }
307 /**
308 * @param UIAssetFetcher $assetFetcher
309 * @param UIAsset $mergedAsset
310 * @return UIAsset
311 */
312 private function getMergedJavascript($assetFetcher, $mergedAsset)
313 {
314 $assetMerger = new JScriptUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
315 $assetMerger->generateFile();
316 return $mergedAsset;
317 }
318 /**
319 * Return individual JS file inclusion directive(s) using the markup <script>
320 *
321 * @return string
322 */
323 protected function getIndividualCoreAndNonCoreJsIncludes() : string
324 {
325 return $this->getIndividualJsIncludesFromAssetFetcher($this->getCoreJScriptFetcher()) . $this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher()) . $this->getIndividualJsIncludesFromAssetFetcher($this->getPluginUmdJScriptFetcher());
326 }
327 /**
328 * @param UIAssetFetcher $assetFetcher
329 * @return string
330 */
331 protected function getIndividualJsIncludesFromAssetFetcher($assetFetcher) : string
332 {
333 $jsIncludeString = '';
334 $assets = $assetFetcher->getCatalog()->getAssets();
335 foreach ($assets as $jsFile) {
336 $jsFile->validateFile();
337 $jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile->getRelativeLocation());
338 }
339 return $jsIncludeString;
340 }
341 private function getCoreJScriptFetcher()
342 {
343 return new JScriptUIAssetFetcher($this->getLoadedPlugins(true), $this->theme);
344 }
345 protected function getNonCoreJScriptFetcher()
346 {
347 return new JScriptUIAssetFetcher($this->getLoadedPlugins(false), $this->theme);
348 }
349 protected function getPluginUmdJScriptFetcher($chunk = null)
350 {
351 return new PluginUmdAssetFetcher($this->getLoadedPlugins('all'), $this->theme, $chunk);
352 }
353 /**
354 * @param string $pluginName
355 * @return boolean
356 */
357 private function pluginContainsJScriptAssets($pluginName)
358 {
359 $fetcher = new JScriptUIAssetFetcher(array($pluginName), $this->theme);
360 try {
361 $assets = $fetcher->getCatalog()->getAssets();
362 } catch (\Exception $e) {
363 // This can happen when a plugin is not valid (eg. Piwik 1.x format)
364 // When posting the event to the plugin, it returns an exception "Plugin has not been loaded"
365 return false;
366 }
367 $pluginManager = Manager::getInstance();
368 $plugin = null;
369 if ($pluginManager->isPluginLoaded($pluginName)) {
370 $plugin = $pluginManager->getLoadedPlugin($pluginName);
371 }
372 if ($plugin && $plugin->isTheme()) {
373 $theme = $pluginManager->getTheme($pluginName);
374 $javaScriptFiles = $theme->getJavaScriptFiles();
375 if (!empty($javaScriptFiles)) {
376 $assets = array_merge($assets, $javaScriptFiles);
377 }
378 }
379 return !empty($assets);
380 }
381 /**
382 * @param UIAsset[] $uiAssets
383 */
384 public function removeAssets($uiAssets)
385 {
386 foreach ($uiAssets as $uiAsset) {
387 $uiAsset->delete();
388 }
389 }
390 /**
391 * @return UIAsset
392 */
393 public function getMergedStylesheetAsset()
394 {
395 return $this->getMergedUIAsset(self::MERGED_CSS_FILE);
396 }
397 /**
398 * @return UIAsset
399 */
400 private function getMergedCoreJSAsset()
401 {
402 return $this->getMergedUIAsset(self::MERGED_CORE_JS_FILE);
403 }
404 /**
405 * @return UIAsset
406 */
407 protected function getMergedNonCoreJSAsset()
408 {
409 return $this->getMergedUIAsset(self::MERGED_NON_CORE_JS_FILE);
410 }
411 /**
412 * @param string $fileName
413 * @return UIAsset
414 */
415 private function getMergedUIAsset($fileName)
416 {
417 return new OnDiskUIAsset($this->getAssetDirectory(), $fileName);
418 }
419 public static function compileCustomStylesheets($files)
420 {
421 $assetManager = new \Piwik\AssetManager();
422 $fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
423 $assetManager->setMinimalStylesheetFetcher($fetcher);
424 return $assetManager->getCompiledBaseCss()->getContent();
425 }
426 public static function compileCustomJs($files)
427 {
428 $mergedAsset = new InMemoryUIAsset();
429 $fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
430 $cacheBuster = UIAssetCacheBuster::getInstance();
431 $assetMerger = new JScriptUIAssetMerger($mergedAsset, $fetcher, $cacheBuster);
432 $assetMerger->generateFile();
433 return $mergedAsset->getContent();
434 }
435 }
436