PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.15.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.15.2
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 3 years ago Application 4 years ago Archive 3 years ago ArchiveProcessor 2 years ago Archiver 5 years ago AssetManager 3 years ago Auth 3 years ago Category 5 years ago Changes 3 years ago CliMulti 4 years ago Columns 3 years ago Concurrency 3 years ago Config 4 years ago Container 4 years ago CronArchive 3 years ago DataAccess 2 years ago DataFiles 5 years ago DataTable 2 years ago Db 3 years ago DeviceDetector 3 years ago Email 5 years ago Exception 3 years ago Http 4 years ago Intl 4 years ago Mail 3 years ago Measurable 5 years ago Menu 3 years ago Metrics 4 years ago Notification 4 years ago Period 4 years ago Plugin 2 years ago ProfessionalServices 4 years ago Report 5 years ago ReportRenderer 2 years ago Scheduler 4 years ago Segment 3 years ago Session 4 years ago Settings 3 years ago Tracker 3 years ago Translation 4 years ago UpdateCheck 5 years ago Updater 3 years ago Updates 3 years ago Validators 4 years ago View 4 years ago ViewDataTable 3 years ago Visualization 4 years ago Widget 5 years ago .htaccess 2 years ago Access.php 4 years ago Archive.php 3 years ago ArchiveProcessor.php 2 years ago AssetManager.php 4 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 4 years ago Common.php 3 years ago Config.php 3 years ago Console.php 3 years ago Context.php 5 years ago Cookie.php 3 years ago CronArchive.php 3 years ago DataArray.php 4 years ago DataTable.php 2 years ago Date.php 3 years ago Db.php 4 years ago DbHelper.php 3 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 3 years ago ExceptionHandler.php 3 years ago FileIntegrity.php 3 years ago Filechecks.php 4 years ago Filesystem.php 3 years ago FrontController.php 3 years ago Http.php 3 years ago IP.php 4 years ago Log.php 4 years ago LogDeleter.php 4 years ago Mail.php 3 years ago Metrics.php 3 years ago NoAccessException.php 5 years ago Nonce.php 3 years ago Notification.php 5 years ago NumberFormatter.php 4 years ago Option.php 4 years ago Period.php 5 years ago Piwik.php 3 years ago Plugin.php 4 years ago Profiler.php 4 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 3 years ago QuickForm2.php 5 years ago RankingQuery.php 3 years ago ReportRenderer.php 4 years ago Segment.php 3 years ago Sequence.php 5 years ago Session.php 3 years ago SettingsPiwik.php 3 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 3 years ago SiteContentDetector.php 2 years ago SupportedBrowser.php 3 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 4 years ago Twig.php 4 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 4 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 2 years ago UrlHelper.php 3 years ago Version.php 2 years ago View.php 3 years ago bootstrap.php 3 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 3 years ago
AssetManager.php
522 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\AssetManager\UIAsset;
13 use Piwik\AssetManager\UIAsset\InMemoryUIAsset;
14 use Piwik\AssetManager\UIAsset\OnDiskUIAsset;
15 use Piwik\AssetManager\UIAssetCacheBuster;
16 use Piwik\AssetManager\UIAssetFetcher\JScriptUIAssetFetcher;
17 use Piwik\AssetManager\UIAssetFetcher\StaticUIAssetFetcher;
18 use Piwik\AssetManager\UIAssetFetcher\StylesheetUIAssetFetcher;
19 use Piwik\AssetManager\UIAssetFetcher\PluginUmdAssetFetcher;
20 use Piwik\AssetManager\UIAssetFetcher;
21 use Piwik\AssetManager\UIAssetMerger\JScriptUIAssetMerger;
22 use Piwik\AssetManager\UIAssetMerger\StylesheetUIAssetMerger;
23 use Piwik\Container\StaticContainer;
24 use Piwik\Plugin\Manager;
25
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 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
46 const CSS_IMPORT_DIRECTIVE = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n";
47 const JS_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
48 const JS_DEFER_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\" defer></script>\n";
49 const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
50 const GET_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getCoreJs";
51 const GET_NON_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getNonCoreJs";
52 const GET_JS_UMD_MODULE_ACTION = "index.php?module=Proxy&action=getUmdJs&chunk=";
53
54 /**
55 * @var UIAssetCacheBuster
56 */
57 private $cacheBuster;
58
59 /**
60 * @var UIAssetFetcher
61 */
62 private $minimalStylesheetFetcher;
63
64 /**
65 * @var Theme
66 */
67 private $theme;
68
69 public function __construct()
70 {
71 $this->cacheBuster = UIAssetCacheBuster::getInstance();
72
73 $this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array(), array(), $this->theme);
74
75 $theme = Manager::getInstance()->getThemeEnabled();
76 if (!empty($theme)) {
77 $this->theme = new Theme();
78 }
79 }
80
81 /**
82 * @inheritDoc
83 * @return AssetManager
84 */
85 public static function getInstance()
86 {
87 $assetManager = parent::getInstance();
88
89 /**
90 * Triggered when creating an instance of the asset manager. Lets you overwrite the
91 * asset manager behavior.
92 *
93 * @param AssetManager &$assetManager
94 *
95 * @ignore
96 * This event is not a public event since we don't want to make the asset manager itself public
97 * API
98 */
99 Piwik::postEvent('AssetManager.makeNewAssetManagerObject', array(&$assetManager));
100
101 return $assetManager;
102 }
103
104 /**
105 * @param UIAssetCacheBuster $cacheBuster
106 */
107 public function setCacheBuster($cacheBuster)
108 {
109 $this->cacheBuster = $cacheBuster;
110 }
111
112 /**
113 * @param UIAssetFetcher $minimalStylesheetFetcher
114 */
115 public function setMinimalStylesheetFetcher($minimalStylesheetFetcher)
116 {
117 $this->minimalStylesheetFetcher = $minimalStylesheetFetcher;
118 }
119
120 /**
121 * @param Theme $theme
122 */
123 public function setTheme($theme)
124 {
125 $this->theme = $theme;
126 }
127
128 /**
129 * Return CSS file inclusion directive(s) using the markup <link>
130 *
131 * @return string
132 */
133 public function getCssInclusionDirective()
134 {
135 return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
136 }
137
138 /**
139 * Return JS file inclusion directive(s) using the markup <script>
140 *
141 * @return string
142 */
143 public function getJsInclusionDirective()
144 {
145 $result = "<script type=\"text/javascript\">\n" . StaticContainer::get('Piwik\Translation\Translator')->getJavascriptTranslations() . "\n</script>";
146
147 if ($this->isMergedAssetsDisabled()) {
148 $this->getMergedCoreJSAsset()->delete();
149 $this->getMergedNonCoreJSAsset()->delete();
150
151 $result .= $this->getIndividualCoreAndNonCoreJsIncludes();
152 } else {
153 $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_CORE_JS_MODULE_ACTION);
154 $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_NON_CORE_JS_MODULE_ACTION);
155
156 $result .= $this->getPluginUmdChunks();
157 }
158
159 return $result;
160 }
161
162 protected function getPluginUmdChunks()
163 {
164 $fetcher = $this->getPluginUmdJScriptFetcher();
165
166 $chunks = $fetcher->getChunkFiles();
167
168 $result = '';
169 foreach ($chunks as $chunk) {
170 $src = self::GET_JS_UMD_MODULE_ACTION . urlencode($chunk->getChunkName());
171 $result .= sprintf(self::JS_DEFER_IMPORT_DIRECTIVE, $src);
172 }
173 return $result;
174 }
175
176 /**
177 * Return the base.less compiled to css
178 *
179 * @return UIAsset
180 */
181 public function getCompiledBaseCss()
182 {
183 $mergedAsset = new InMemoryUIAsset();
184
185 $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $this->minimalStylesheetFetcher, $this->cacheBuster);
186
187 $assetMerger->generateFile();
188
189 return $mergedAsset;
190 }
191
192 /**
193 * Return the css merged file absolute location.
194 * If there is none, the generation process will be triggered.
195 *
196 * @return UIAsset
197 */
198 public function getMergedStylesheet()
199 {
200 $mergedAsset = $this->getMergedStylesheetAsset();
201
202 $assetFetcher = new StylesheetUIAssetFetcher(Manager::getInstance()->getLoadedPluginsName(), $this->theme);
203
204 $assetMerger = new StylesheetUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
205
206 $assetMerger->generateFile();
207
208 return $mergedAsset;
209 }
210
211 /**
212 * Return the core js merged file absolute location.
213 * If there is none, the generation process will be triggered.
214 *
215 * @return UIAsset
216 */
217 public function getMergedCoreJavaScript()
218 {
219 return $this->getMergedJavascript($this->getCoreJScriptFetcher(), $this->getMergedCoreJSAsset());
220 }
221
222 /**
223 * Return the non core js merged file absolute location.
224 * If there is none, the generation process will be triggered.
225 *
226 * @return UIAsset
227 */
228 public function getMergedNonCoreJavaScript()
229 {
230 return $this->getMergedJavascript($this->getNonCoreJScriptFetcher(), $this->getMergedNonCoreJSAsset());
231 }
232
233 /**
234 * Return a chunk JS merged file absolute location.
235 * If there is none, the generation process will be triggered.
236 *
237 * @param string $chunk The name of the chunk. Will either be a plugin name or an integer.
238 * @return UIAsset
239 */
240 public function getMergedJavaScriptChunk($chunk)
241 {
242 $assetFetcher = $this->getPluginUmdJScriptFetcher($chunk);
243 $outputFile = $assetFetcher->getRequestedChunkOutputFile();
244
245 return $this->getMergedJavascript($assetFetcher, $this->getMergedUIAsset($outputFile));
246 }
247
248 /**
249 * @param boolean|"all" $core
250 * @return string[]
251 */
252 public function getLoadedPlugins($core)
253 {
254 $loadedPlugins = array();
255
256 foreach (Manager::getInstance()->getPluginsLoadedAndActivated() as $plugin) {
257 $pluginName = $plugin->getPluginName();
258 $pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName);
259
260 if ($core === 'all' || ($pluginIsCore && $core) || (!$pluginIsCore && !$core)) {
261 $loadedPlugins[] = $pluginName;
262 }
263 }
264
265 return $loadedPlugins;
266 }
267
268 /**
269 * Remove previous merged assets
270 */
271 public function removeMergedAssets($pluginName = false)
272 {
273 $assetsToRemove = array($this->getMergedStylesheetAsset());
274
275 if ($pluginName) {
276 if ($this->pluginContainsJScriptAssets($pluginName)) {
277 if (Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
278 $assetsToRemove[] = $this->getMergedCoreJSAsset();
279 } else {
280 $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
281 }
282
283 $assetFetcher = $this->getPluginUmdJScriptFetcher();
284 foreach ($assetFetcher->getChunkFiles() as $chunk) {
285 $files = $chunk->getFiles();
286
287 $foundInChunk = false;
288 foreach ($files as $file) {
289 if (strpos($file, "/$pluginName.umd.") !== false) {
290 $foundInChunk = true;
291 }
292 }
293
294 if ($foundInChunk) {
295 $outputFile = $chunk->getOutputFile();
296 $asset = $this->getMergedUIAsset($outputFile);
297 if ($asset->exists()) {
298 $assetsToRemove[] = $asset;
299 }
300 break;
301 }
302 }
303 }
304 } else {
305 $assetsToRemove[] = $this->getMergedCoreJSAsset();
306 $assetsToRemove[] = $this->getMergedNonCoreJSAsset();
307
308 $assetFetcher = $this->getPluginUmdJScriptFetcher();
309 foreach ($assetFetcher->getChunkFiles() as $chunk) {
310 $outputFile = $chunk->getOutputFile();
311 $asset = $this->getMergedUIAsset($outputFile);
312 if ($asset->exists()) {
313 $assetsToRemove[] = $asset;
314 }
315 }
316 }
317
318 $this->removeAssets($assetsToRemove);
319 }
320
321 /**
322 * Check if the merged file directory exists and is writable.
323 *
324 * @return string The directory location
325 * @throws Exception if directory is not writable.
326 */
327 public function getAssetDirectory()
328 {
329 $mergedFileDirectory = StaticContainer::get('path.tmp') . '/assets';
330
331 if (!is_dir($mergedFileDirectory)) {
332 Filesystem::mkdir($mergedFileDirectory);
333 }
334
335 if (!is_writable($mergedFileDirectory)) {
336 throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
337 }
338
339 return $mergedFileDirectory;
340 }
341
342 /**
343 * Return the global option disable_merged_assets
344 *
345 * @return boolean
346 */
347 public function isMergedAssetsDisabled()
348 {
349 if (Config::getInstance()->Development['disable_merged_assets'] == 1) {
350 return true;
351 }
352
353 if (isset($_GET['disable_merged_assets']) && $_GET['disable_merged_assets'] == 1) {
354 return true;
355 }
356
357 return false;
358 }
359
360 /**
361 * @param UIAssetFetcher $assetFetcher
362 * @param UIAsset $mergedAsset
363 * @return UIAsset
364 */
365 private function getMergedJavascript($assetFetcher, $mergedAsset)
366 {
367 $assetMerger = new JScriptUIAssetMerger($mergedAsset, $assetFetcher, $this->cacheBuster);
368
369 $assetMerger->generateFile();
370
371 return $mergedAsset;
372 }
373
374 /**
375 * Return individual JS file inclusion directive(s) using the markup <script>
376 *
377 * @return string
378 */
379 protected function getIndividualCoreAndNonCoreJsIncludes()
380 {
381 return
382 $this->getIndividualJsIncludesFromAssetFetcher($this->getCoreJScriptFetcher()) .
383 $this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher()) .
384 $this->getIndividualJsIncludesFromAssetFetcher($this->getPluginUmdJScriptFetcher());
385 }
386
387 /**
388 * @param UIAssetFetcher $assetFetcher
389 * @return string
390 */
391 protected function getIndividualJsIncludesFromAssetFetcher($assetFetcher)
392 {
393 $jsIncludeString = '';
394
395 $assets = $assetFetcher->getCatalog()->getAssets();
396
397 foreach ($assets as $jsFile) {
398 $jsFile->validateFile();
399 $jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile->getRelativeLocation());
400 }
401
402 return $jsIncludeString;
403 }
404
405 private function getCoreJScriptFetcher()
406 {
407 return new JScriptUIAssetFetcher($this->getLoadedPlugins(true), $this->theme);
408 }
409
410 protected function getNonCoreJScriptFetcher()
411 {
412 return new JScriptUIAssetFetcher($this->getLoadedPlugins(false), $this->theme);
413 }
414
415 protected function getPluginUmdJScriptFetcher($chunk = null)
416 {
417 return new PluginUmdAssetFetcher($this->getLoadedPlugins('all'), $this->theme, $chunk);
418 }
419
420 /**
421 * @param string $pluginName
422 * @return boolean
423 */
424 private function pluginContainsJScriptAssets($pluginName)
425 {
426 $fetcher = new JScriptUIAssetFetcher(array($pluginName), $this->theme);
427
428 try {
429 $assets = $fetcher->getCatalog()->getAssets();
430 } catch (\Exception $e) {
431 // This can happen when a plugin is not valid (eg. Piwik 1.x format)
432 // When posting the event to the plugin, it returns an exception "Plugin has not been loaded"
433 return false;
434 }
435
436 $pluginManager = Manager::getInstance();
437 $plugin = null;
438 if ($pluginManager->isPluginLoaded($pluginName)) {
439 $plugin = $pluginManager->getLoadedPlugin($pluginName);
440 }
441
442 if ($plugin && $plugin->isTheme()) {
443 $theme = $pluginManager->getTheme($pluginName);
444
445 $javaScriptFiles = $theme->getJavaScriptFiles();
446
447 if (!empty($javaScriptFiles)) {
448 $assets = array_merge($assets, $javaScriptFiles);
449 }
450 }
451
452 return !empty($assets);
453 }
454
455 /**
456 * @param UIAsset[] $uiAssets
457 */
458 public function removeAssets($uiAssets)
459 {
460 foreach ($uiAssets as $uiAsset) {
461 $uiAsset->delete();
462 }
463 }
464
465 /**
466 * @return UIAsset
467 */
468 public function getMergedStylesheetAsset()
469 {
470 return $this->getMergedUIAsset(self::MERGED_CSS_FILE);
471 }
472
473 /**
474 * @return UIAsset
475 */
476 private function getMergedCoreJSAsset()
477 {
478 return $this->getMergedUIAsset(self::MERGED_CORE_JS_FILE);
479 }
480
481 /**
482 * @return UIAsset
483 */
484 protected function getMergedNonCoreJSAsset()
485 {
486 return $this->getMergedUIAsset(self::MERGED_NON_CORE_JS_FILE);
487 }
488
489 /**
490 * @param string $fileName
491 * @return UIAsset
492 */
493 private function getMergedUIAsset($fileName)
494 {
495 return new OnDiskUIAsset($this->getAssetDirectory(), $fileName);
496 }
497
498 public static function compileCustomStylesheets($files)
499 {
500 $assetManager = new AssetManager();
501
502 $fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
503
504 $assetManager->setMinimalStylesheetFetcher($fetcher);
505
506 return $assetManager->getCompiledBaseCss()->getContent();
507 }
508
509 public static function compileCustomJs($files)
510 {
511 $mergedAsset = new InMemoryUIAsset();
512 $fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null);
513
514 $cacheBuster = UIAssetCacheBuster::getInstance();
515
516 $assetMerger = new JScriptUIAssetMerger($mergedAsset, $fetcher, $cacheBuster);
517 $assetMerger->generateFile();
518
519 return $mergedAsset->getContent();
520 }
521 }
522