PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.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 / Twig.php
matomo / app / core Last commit date
API 1 year ago Access 1 year ago Application 1 year ago Archive 1 year ago ArchiveProcessor 1 year ago Archiver 2 years ago AssetManager 1 year ago Auth 1 year ago Category 2 years ago Changes 1 year ago CliMulti 1 year ago Columns 1 year ago Concurrency 1 year ago Config 1 year ago Container 1 year ago CronArchive 1 year ago DataAccess 1 year ago DataFiles 2 years ago DataTable 1 year ago Db 1 year ago DeviceDetector 1 year ago Email 2 years ago Exception 1 year ago Http 1 year ago Intl 1 year ago Log 2 years ago Mail 1 year ago Measurable 1 year ago Menu 1 year ago Metrics 1 year ago Notification 1 year ago Period 1 year ago Plugin 1 year ago ProfessionalServices 1 year ago Report 1 year ago ReportRenderer 1 year ago Scheduler 1 year ago Segment 1 year ago Session 1 year ago Settings 1 year ago Tracker 1 year ago Translation 1 year ago Twig 1 year ago UpdateCheck 1 year ago Updater 1 year ago Updates 1 year ago Validators 1 year ago View 1 year ago ViewDataTable 1 year ago Visualization 1 year ago Widget 1 year ago .htaccess 2 years ago Access.php 1 year ago Archive.php 1 year ago ArchiveProcessor.php 1 year ago AssetManager.php 1 year ago Auth.php 2 years ago AuthResult.php 2 years ago BaseFactory.php 2 years ago Cache.php 2 years ago CacheId.php 1 year ago CliMulti.php 1 year ago Common.php 1 year ago Config.php 1 year ago Console.php 1 year ago Context.php 2 years ago Cookie.php 1 year ago CronArchive.php 1 year ago DI.php 1 year ago DataArray.php 1 year ago DataTable.php 1 year ago Date.php 1 year ago Db.php 1 year ago DbHelper.php 1 year ago Development.php 1 year ago ErrorHandler.php 1 year ago EventDispatcher.php 1 year ago ExceptionHandler.php 1 year ago FileIntegrity.php 1 year ago Filechecks.php 1 year ago Filesystem.php 1 year ago FrontController.php 1 year ago Http.php 1 year ago IP.php 1 year ago Log.php 2 years ago LogDeleter.php 1 year ago Mail.php 1 year ago Metrics.php 1 year ago NoAccessException.php 2 years ago Nonce.php 1 year ago Notification.php 1 year ago NumberFormatter.php 1 year ago Option.php 1 year ago Period.php 1 year ago Piwik.php 1 year ago Plugin.php 1 year ago Process.php 1 year ago Profiler.php 1 year ago ProxyHeaders.php 2 years ago ProxyHttp.php 1 year ago QuickForm2.php 1 year ago RankingQuery.php 1 year ago ReportRenderer.php 1 year ago Request.php 1 year ago Segment.php 1 year ago Sequence.php 2 years ago Session.php 1 year ago SettingsPiwik.php 1 year ago SettingsServer.php 1 year ago Singleton.php 2 years ago Site.php 1 year ago SiteContentDetector.php 1 year ago SupportedBrowser.php 2 years ago TCPDF.php 1 year ago Theme.php 1 year ago Timer.php 2 years ago Tracker.php 1 year ago Twig.php 1 year ago Unzip.php 1 year ago UpdateCheck.php 1 year ago Updater.php 1 year ago UpdaterErrorException.php 2 years ago Updates.php 1 year ago Url.php 1 year ago UrlHelper.php 1 year ago Version.php 1 year ago View.php 1 year ago bootstrap.php 1 year ago dispatch.php 2 years ago testMinimumPhpVersion.php 2 years ago
Twig.php
562 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\Container\StaticContainer;
13 use Piwik\DataTable\Filter\SafeDecodeLabel;
14 use Piwik\Metrics\Formatter;
15 use Piwik\Plugin\Manager;
16 use Piwik\Tracker\GoalManager;
17 use Piwik\Translation\Translator;
18 use Piwik\Twig\Extension\EscapeFilter;
19 use Piwik\View\RenderTokenParser;
20 use Piwik\Visualization\Sparkline;
21 use Matomo\Dependencies\Twig\Environment;
22 use Matomo\Dependencies\Twig\Extension\DebugExtension;
23 use Matomo\Dependencies\Twig\Loader\ChainLoader;
24 use Matomo\Dependencies\Twig\Loader\FilesystemLoader;
25 use Matomo\Dependencies\Twig\TwigFilter;
26 use Matomo\Dependencies\Twig\TwigFunction;
27 use Matomo\Dependencies\Twig\TwigTest;
28 function piwik_filter_truncate($string, $size)
29 {
30 if (mb_strlen(html_entity_decode($string)) <= $size) {
31 return $string;
32 } else {
33 preg_match('/^(&(?:[a-z\\d]+|#\\d+|#x[a-f\\d]+);|.){' . $size . '}/i', $string, $shortenString);
34 return reset($shortenString) . "...";
35 }
36 }
37 function piwik_format_number($string, $minFractionDigits, $maxFractionDigits)
38 {
39 $formatter = \Piwik\NumberFormatter::getInstance();
40 return $formatter->format($string, $minFractionDigits, $maxFractionDigits);
41 }
42 function piwik_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = \false)
43 {
44 $string = \Matomo\Dependencies\twig_escape_filter($env, $string, $strategy, $charset, $autoescape);
45 switch ($strategy) {
46 case 'url':
47 $encoded = rawurlencode('{');
48 return str_replace('{{', $encoded . $encoded, $string);
49 default:
50 return $string;
51 }
52 }
53 function piwik_format_money($amount, $idSite)
54 {
55 $currencySymbol = \Piwik\Site::getCurrencySymbolFor($idSite);
56 $numberFormatter = \Piwik\NumberFormatter::getInstance();
57 return $numberFormatter->formatCurrency($amount, $currencySymbol, GoalManager::REVENUE_PRECISION);
58 }
59 /**
60 * Twig class
61 *
62 */
63 class Twig
64 {
65 public const SPARKLINE_TEMPLATE = '<img loading="lazy" alt="" data-src="%s" width="%d" height="%d" />
66 <script type="text/javascript">$(function() { piwik.initSparklines(); });</script>';
67 /**
68 * @var Environment
69 */
70 private $twig;
71 private $formatter;
72 public function __construct()
73 {
74 $loader = $this->getDefaultThemeLoader();
75 $this->addPluginNamespaces($loader);
76 //get current theme
77 $manager = \Piwik\Plugin\Manager::getInstance();
78 $theme = $manager->getThemeEnabled();
79 $loaders = array();
80 $this->formatter = new Formatter();
81 //create loader for custom theme to overwrite twig templates
82 if ($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) {
83 $customLoader = $this->getCustomThemeLoader($theme);
84 if ($customLoader) {
85 //make it possible to overwrite plugin templates
86 $this->addCustomPluginNamespaces($customLoader, $theme->getPluginName());
87 $loaders[] = $customLoader;
88 }
89 }
90 $loaders[] = $loader;
91 $chainLoader = new ChainLoader($loaders);
92 // Create new Twig Environment and set cache dir
93 $cache = StaticContainer::get('twig.cache');
94 $this->twig = new Environment($chainLoader, array(
95 'debug' => \true,
96 // to use {{ dump(var) }} in twig templates
97 'strict_variables' => \true,
98 // throw an exception if variables are invalid
99 'cache' => $cache,
100 ));
101 $this->twig->addExtension(new DebugExtension());
102 $this->addFilterTranslate();
103 $this->addFilterListings();
104 $this->addFilterUrlRewriteWithParameters();
105 $this->addFilterSumTime();
106 $this->addFilterMoney();
107 $this->addFilterTruncate();
108 $this->addFilterNotification();
109 $this->addFilterPercent();
110 $this->addFilterPercentage();
111 $this->addFilterPercentEvolution();
112 $this->addFilterPrettyDate();
113 $this->addFilterSafeDecodeRaw();
114 $this->addFilterNumber();
115 $this->addFilterAnonymiseSystemInfo();
116 $this->addFilterNonce();
117 $this->addFilterMd5();
118 $this->addFilterOnlyDomain();
119 $this->addFilterSafelink();
120 $this->addFilterTrackMatomoLink();
121 $this->addFilterImplode();
122 $this->twig->addFilter(new TwigFilter('ucwords', 'ucwords'));
123 $this->twig->addFilter(new TwigFilter('lcfirst', 'lcfirst'));
124 $this->twig->addFilter(new TwigFilter('ucfirst', 'ucfirst'));
125 $this->twig->addFilter(new TwigFilter('preg_replace', function ($subject, $pattern, $replacement) {
126 return preg_replace($pattern, $replacement, $subject);
127 }));
128 $this->addFunctionExternalLink();
129 $this->addFunctionExternalRawLink();
130 $this->addFunctionIncludeAssets();
131 $this->addFunctionLinkTo();
132 $this->addFunctionSparkline();
133 $this->addFunctionPostEvent();
134 $this->addFunctionIsPluginLoaded();
135 $this->addFunctionGetJavascriptTranslations();
136 $this->twig->addTokenParser(new RenderTokenParser());
137 $this->addTestFalse();
138 $this->addTestTrue();
139 $this->addTestEmptyString();
140 $this->addTestIsNumeric();
141 $this->twig->addExtension(new EscapeFilter());
142 }
143 private function addTestFalse()
144 {
145 $test = new TwigTest('false', function ($value) {
146 return \false === $value;
147 });
148 $this->twig->addTest($test);
149 }
150 private function addTestTrue()
151 {
152 $test = new TwigTest('true', function ($value) {
153 return \true === $value;
154 });
155 $this->twig->addTest($test);
156 }
157 private function addTestEmptyString()
158 {
159 $test = new TwigTest('emptyString', function ($value) {
160 return '' === $value;
161 });
162 $this->twig->addTest($test);
163 }
164 protected function addFunctionGetJavascriptTranslations()
165 {
166 $getJavascriptTranslations = new TwigFunction('getJavascriptTranslations', array(StaticContainer::get('Piwik\\Translation\\Translator'), 'getJavascriptTranslations'));
167 $this->twig->addFunction($getJavascriptTranslations);
168 }
169 protected function addFunctionIsPluginLoaded()
170 {
171 $isPluginLoadedFunction = new TwigFunction('isPluginLoaded', function ($pluginName) {
172 return \Piwik\Plugin\Manager::getInstance()->isPluginLoaded($pluginName);
173 });
174 $this->twig->addFunction($isPluginLoadedFunction);
175 }
176 protected function addFunctionIncludeAssets()
177 {
178 $includeAssetsFunction = new TwigFunction('includeAssets', function ($params) {
179 if (!isset($params['type'])) {
180 throw new Exception("The function includeAssets needs a 'type' parameter.");
181 }
182 $assetType = strtolower($params['type']);
183 $deferJs = boolval($params['defer'] ?? \false);
184 switch ($assetType) {
185 case 'css':
186 return \Piwik\AssetManager::getInstance()->getCssInclusionDirective();
187 case 'js':
188 return \Piwik\AssetManager::getInstance()->getJsInclusionDirective($deferJs);
189 default:
190 throw new Exception("The twig function includeAssets 'type' parameter needs to be either 'css' or 'js'.");
191 }
192 });
193 $this->twig->addFunction($includeAssetsFunction);
194 }
195 protected function addFunctionPostEvent()
196 {
197 $postEventFunction = new TwigFunction('postEvent', function ($eventName) {
198 // get parameters to twig function
199 $params = func_get_args();
200 // remove the first value (event name)
201 array_shift($params);
202 // make the first value the string that will get output in the template
203 // plugins can modify this string
204 $str = '';
205 $params = array_merge(array(&$str), $params);
206 \Piwik\Piwik::postEvent($eventName, $params);
207 return $str;
208 }, array('is_safe' => array('html')));
209 $this->twig->addFunction($postEventFunction);
210 }
211 protected function addFunctionSparkline()
212 {
213 $sparklineFunction = new TwigFunction('sparkline', function ($src) {
214 $width = Sparkline::DEFAULT_WIDTH;
215 $height = Sparkline::DEFAULT_HEIGHT;
216 return sprintf(\Piwik\Twig::SPARKLINE_TEMPLATE, $src, $width, $height);
217 }, array('is_safe' => array('html')));
218 $this->twig->addFunction($sparklineFunction);
219 }
220 protected function addFunctionLinkTo()
221 {
222 $urlFunction = new TwigFunction('linkTo', function ($params) {
223 return 'index.php' . \Piwik\Url::getCurrentQueryStringWithParametersModified($params);
224 });
225 $this->twig->addFunction($urlFunction);
226 }
227 /**
228 * Build an external link for a URL
229 *
230 * Usage:
231 * externallink(url)
232 *
233 */
234 private function addFunctionExternalLink()
235 {
236 $externalLink = new TwigFunction('externallink', function ($url) {
237 // Add tracking parameters if a matomo.org link
238 $url = \Piwik\Url::addCampaignParametersToMatomoLink($url);
239 return "<a target='_blank' rel='noreferrer noopener' href='" . $url . "'>";
240 });
241 $this->twig->addFunction($externalLink);
242 }
243 private function addFunctionExternalRawLink()
244 {
245 $externalRawLink = new TwigFunction('externalrawlink', function ($url) {
246 // Add tracking parameters if a matomo.org link
247 return \Piwik\Url::addCampaignParametersToMatomoLink($url);
248 });
249 $this->twig->addFunction($externalRawLink);
250 }
251 /**
252 * @return FilesystemLoader
253 */
254 private function getDefaultThemeLoader()
255 {
256 $themeDir = Manager::getPluginDirectory(\Piwik\Plugin\Manager::DEFAULT_THEME) . '/templates/';
257 $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . \DIRECTORY_SEPARATOR);
258 return $themeLoader;
259 }
260 /**
261 * create template loader for a custom theme
262 * @param \Piwik\Plugin $theme
263 * @return FilesystemLoader|bool
264 */
265 protected function getCustomThemeLoader(\Piwik\Plugin $theme)
266 {
267 $pluginsDir = Manager::getPluginDirectory($theme->getPluginName());
268 $themeDir = $pluginsDir . '/templates/';
269 if (!file_exists($themeDir)) {
270 return \false;
271 }
272 $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . \DIRECTORY_SEPARATOR);
273 return $themeLoader;
274 }
275 public function getTwigEnvironment()
276 {
277 return $this->twig;
278 }
279 protected function addFilterNotification()
280 {
281 $twigEnv = $this->getTwigEnvironment();
282 $notificationFunction = new TwigFilter('notification', function ($message, $options) use($twigEnv) {
283 $template = '<div style="display:none" data-role="notification" ';
284 foreach ($options as $key => $value) {
285 if (ctype_alpha($key)) {
286 $template .= sprintf('data-%s="%s" ', $key, \Matomo\Dependencies\twig_escape_filter($twigEnv, $value, 'html_attr'));
287 }
288 }
289 $template .= '>';
290 if (!empty($options['raw'])) {
291 $template .= $message;
292 } else {
293 $template .= piwik_escape_filter($twigEnv, $message, 'html');
294 }
295 $template .= '</div>';
296 return $template;
297 }, array('is_safe' => array('html')));
298 $this->twig->addFilter($notificationFunction);
299 }
300 protected function addFilterSafeDecodeRaw()
301 {
302 $rawSafeDecoded = new TwigFilter('rawSafeDecoded', function ($string) {
303 if ($string === null) {
304 return '';
305 }
306 $string = str_replace('+', '%2B', $string);
307 $string = str_replace('&nbsp;', html_entity_decode('&nbsp;', \ENT_COMPAT | \ENT_HTML401, 'UTF-8'), $string);
308 $string = SafeDecodeLabel::decodeLabelSafe($string);
309 return $string;
310 }, array('is_safe' => array('all')));
311 $this->twig->addFilter($rawSafeDecoded);
312 }
313 protected function addFilterPrettyDate()
314 {
315 $prettyDate = new TwigFilter('prettyDate', function ($dateString, $period) {
316 return \Piwik\Period\Factory::build($period, $dateString)->getLocalizedShortString();
317 });
318 $this->twig->addFilter($prettyDate);
319 }
320 protected function addFilterPercentage()
321 {
322 $percentage = new TwigFilter('percentage', function ($string, $totalValue, $precision = 1) {
323 $formatter = \Piwik\NumberFormatter::getInstance();
324 return $formatter->formatPercent(\Piwik\Piwik::getPercentageSafe($string, $totalValue, $precision), $precision);
325 });
326 $this->twig->addFilter($percentage);
327 }
328 protected function addFilterPercent()
329 {
330 $percentage = new TwigFilter('percent', function ($string, $precision = 1) {
331 $formatter = \Piwik\NumberFormatter::getInstance();
332 return $formatter->formatPercent($string, $precision);
333 });
334 $this->twig->addFilter($percentage);
335 }
336 protected function addFilterPercentEvolution()
337 {
338 $percentage = new TwigFilter('percentEvolution', function ($string) {
339 $formatter = \Piwik\NumberFormatter::getInstance();
340 return $formatter->formatPercentEvolution($string);
341 });
342 $this->twig->addFilter($percentage);
343 }
344 private function getProfessionalServicesAdvertising()
345 {
346 return StaticContainer::get('Piwik\\ProfessionalServices\\Advertising');
347 }
348 protected function addFilterNumber()
349 {
350 $formatter = new TwigFilter('number', function ($string, $minFractionDigits = 0, $maxFractionDigits = 0) {
351 return piwik_format_number($string, $minFractionDigits, $maxFractionDigits);
352 });
353 $this->twig->addFilter($formatter);
354 }
355 protected function addFilterAnonymiseSystemInfo()
356 {
357 $formatter = new TwigFilter('anonymiseSystemInfo', function ($string) {
358 if ($string === null) {
359 return '';
360 }
361 if ($string === \false || $string === \true) {
362 return (int) $string;
363 }
364 $string = str_replace([PIWIK_DOCUMENT_ROOT, str_replace('/', '\\/', PIWIK_DOCUMENT_ROOT)], '$DOC_ROOT', $string);
365 $string = str_replace([PIWIK_USER_PATH, str_replace('/', '\\/', PIWIK_USER_PATH)], '$USER_PATH', $string);
366 $string = str_replace([PIWIK_INCLUDE_PATH, str_replace('/', '\\/', PIWIK_INCLUDE_PATH)], '$INCLUDE_PATH', $string);
367 // replace anything token like
368 $string = preg_replace('/[[:xdigit:]]{31,80}/', 'TOKEN_REPLACED', $string);
369 // just in case it was somehow show in a text
370 if (\Piwik\SettingsPiwik::isMatomoInstalled()) {
371 $string = str_replace(\Piwik\SettingsPiwik::getPiwikUrl(), '$MATOMO_URL', $string);
372 $string = str_replace(\Piwik\SettingsPiwik::getSalt(), '$MATOMO_SALT', $string);
373 }
374 return $string;
375 });
376 $this->twig->addFilter($formatter);
377 }
378 protected function addFilterNonce()
379 {
380 $nonce = new TwigFilter('nonce', array('Piwik\\Nonce', 'getNonce'));
381 $this->twig->addFilter($nonce);
382 }
383 private function addFilterMd5()
384 {
385 $md5 = new TwigFilter('md5', function ($value) {
386 return md5($value);
387 });
388 $this->twig->addFilter($md5);
389 }
390 private function addFilterOnlyDomain()
391 {
392 $domainOnly = new TwigFilter('domainOnly', function ($url) {
393 $parsed = parse_url($url);
394 return $parsed['scheme'] . '://' . $parsed['host'];
395 });
396 $this->twig->addFilter($domainOnly);
397 }
398 protected function addFilterTruncate()
399 {
400 $truncateFilter = new TwigFilter('truncate', function ($string, $size) {
401 return piwik_filter_truncate($string, $size);
402 });
403 $this->twig->addFilter($truncateFilter);
404 }
405 protected function addFilterMoney()
406 {
407 $moneyFilter = new TwigFilter('money', function ($amount) {
408 if (func_num_args() != 2) {
409 throw new Exception('the money modifier expects one parameter: the idSite.');
410 }
411 $idSite = func_get_args();
412 $idSite = $idSite[1];
413 return piwik_format_money($amount, $idSite);
414 });
415 $this->twig->addFilter($moneyFilter);
416 }
417 protected function addFilterSumTime()
418 {
419 $formatter = $this->formatter;
420 $sumtimeFilter = new TwigFilter('sumtime', function ($numberOfSeconds) use($formatter) {
421 return $formatter->getPrettyTimeFromSeconds($numberOfSeconds, \true);
422 });
423 $this->twig->addFilter($sumtimeFilter);
424 }
425 protected function addFilterUrlRewriteWithParameters()
426 {
427 $urlRewriteFilter = new TwigFilter('urlRewriteWithParameters', function ($parameters) {
428 $parameters['updated'] = null;
429 $url = \Piwik\Url::getCurrentQueryStringWithParametersModified($parameters);
430 return $url;
431 });
432 $this->twig->addFilter($urlRewriteFilter);
433 }
434 protected function addFilterTranslate()
435 {
436 $translateFilter = new TwigFilter('translate', function ($stringToken) {
437 if (func_num_args() <= 1) {
438 $aValues = array();
439 } else {
440 $aValues = func_get_args();
441 array_shift($aValues);
442 }
443 try {
444 $stringTranslated = \Piwik\Piwik::translate($stringToken, $aValues);
445 } catch (Exception $e) {
446 $stringTranslated = $stringToken;
447 }
448 return $stringTranslated;
449 });
450 $this->twig->addFilter($translateFilter);
451 }
452 protected function addFilterListings()
453 {
454 $andListing = new TwigFilter('andListing', function ($items) {
455 if (!is_array($items)) {
456 return $items;
457 // don't do anything if input data is incorrect
458 }
459 return StaticContainer::get(Translator::class)->createAndListing($items);
460 });
461 $this->twig->addFilter($andListing);
462 $orListing = new TwigFilter('orListing', function ($items) {
463 if (!is_array($items)) {
464 return $items;
465 // don't do anything if input data is incorrect
466 }
467 return StaticContainer::get(Translator::class)->createOrListing($items);
468 });
469 $this->twig->addFilter($orListing);
470 }
471 private function addPluginNamespaces(FilesystemLoader $loader)
472 {
473 $pluginManager = \Piwik\Plugin\Manager::getInstance();
474 $plugins = $pluginManager->getAllPluginsNames();
475 foreach ($plugins as $name) {
476 $pluginsDir = Manager::getPluginDirectory($name);
477 $path = sprintf("%s/templates/", $pluginsDir);
478 if (is_dir($path)) {
479 $loader->addPath(rtrim($path, '/'), $name);
480 }
481 }
482 }
483 /**
484 *
485 * Plugin-Templates can be overwritten by putting identically named templates in plugins/[theme]/templates/plugins/[plugin]/
486 *
487 */
488 private function addCustomPluginNamespaces(FilesystemLoader $loader, $pluginName)
489 {
490 $pluginManager = \Piwik\Plugin\Manager::getInstance();
491 $plugins = $pluginManager->getAllPluginsNames();
492 $pluginsDir = Manager::getPluginDirectory($pluginName);
493 foreach ($plugins as $name) {
494 $path = sprintf("%s/templates/plugins/%s/", $pluginsDir, $name);
495 if (is_dir($path)) {
496 $loader->addPath(rtrim($path, '/'), $name);
497 }
498 }
499 }
500 /**
501 * Prepend relative paths with absolute Piwik path
502 *
503 * @param string $value relative path (pass by reference)
504 * @param int $key (don't care)
505 * @param string $path Piwik root
506 */
507 public static function addPiwikPath(&$value, $key, $path)
508 {
509 if ($value[0] != '/' && $value[0] != \DIRECTORY_SEPARATOR) {
510 $value = $path . "/{$value}";
511 }
512 }
513 private function addFilterSafelink()
514 {
515 $safelink = new TwigFilter('safelink', function ($url) {
516 if (!\Piwik\UrlHelper::isLookLikeSafeUrl($url)) {
517 return '';
518 }
519 return $url;
520 });
521 $this->twig->addFilter($safelink);
522 }
523 /**
524 * Modify any links to matomo domains to add campaign tracking parameters
525 *
526 * Typical usage:
527 *
528 * Apply default campaign tracking parameters:
529 * {{ 'https://matomo.org/faq/123'|trackmatomolink }}
530 *
531 * Apply custom campaign tracking parameters:
532 * {{ 'https://matomo.org/faq/123'|trackmatomolink('SomeCampaign', 'SomeSource', 'SomeMedium') }}
533 *
534 */
535 private function addFilterTrackMatomoLink()
536 {
537 $trackLink = new TwigFilter('trackmatomolink', function ($url) {
538 $params = func_get_args();
539 array_shift($params);
540 $campaign = count($params) > 0 ? $params[0] : null;
541 $source = count($params) > 1 ? $params[1] : null;
542 $medium = count($params) > 2 ? $params[2] : null;
543 return \Piwik\Url::addCampaignParametersToMatomoLink($url, $campaign, $source, $medium);
544 });
545 $this->twig->addFilter($trackLink);
546 }
547 private function addFilterImplode()
548 {
549 $implode = new TwigFilter('implode', function ($value, $separator) {
550 return implode($separator, $value);
551 });
552 $this->twig->addFilter($implode);
553 }
554 private function addTestIsNumeric()
555 {
556 $test = new TwigTest('numeric', function ($value) {
557 return is_numeric($value);
558 });
559 $this->twig->addTest($test);
560 }
561 }
562