PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.3.1
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 1 year 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 1 year 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
573 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->addFilterPreventLinking();
123 $this->twig->addFilter(new TwigFilter('ucwords', 'ucwords'));
124 $this->twig->addFilter(new TwigFilter('lcfirst', 'lcfirst'));
125 $this->twig->addFilter(new TwigFilter('ucfirst', 'ucfirst'));
126 $this->twig->addFilter(new TwigFilter('preg_replace', function ($subject, $pattern, $replacement) {
127 return preg_replace($pattern, $replacement, $subject);
128 }));
129 $this->addFunctionExternalLink();
130 $this->addFunctionExternalRawLink();
131 $this->addFunctionIncludeAssets();
132 $this->addFunctionLinkTo();
133 $this->addFunctionSparkline();
134 $this->addFunctionPostEvent();
135 $this->addFunctionIsPluginLoaded();
136 $this->addFunctionGetJavascriptTranslations();
137 $this->twig->addTokenParser(new RenderTokenParser());
138 $this->addTestFalse();
139 $this->addTestTrue();
140 $this->addTestEmptyString();
141 $this->addTestIsNumeric();
142 $this->twig->addExtension(new EscapeFilter());
143 }
144 private function addTestFalse()
145 {
146 $test = new TwigTest('false', function ($value) {
147 return \false === $value;
148 });
149 $this->twig->addTest($test);
150 }
151 private function addTestTrue()
152 {
153 $test = new TwigTest('true', function ($value) {
154 return \true === $value;
155 });
156 $this->twig->addTest($test);
157 }
158 private function addTestEmptyString()
159 {
160 $test = new TwigTest('emptyString', function ($value) {
161 return '' === $value;
162 });
163 $this->twig->addTest($test);
164 }
165 protected function addFunctionGetJavascriptTranslations()
166 {
167 $getJavascriptTranslations = new TwigFunction('getJavascriptTranslations', array(StaticContainer::get('Piwik\\Translation\\Translator'), 'getJavascriptTranslations'));
168 $this->twig->addFunction($getJavascriptTranslations);
169 }
170 protected function addFunctionIsPluginLoaded()
171 {
172 $isPluginLoadedFunction = new TwigFunction('isPluginLoaded', function ($pluginName) {
173 return \Piwik\Plugin\Manager::getInstance()->isPluginLoaded($pluginName);
174 });
175 $this->twig->addFunction($isPluginLoadedFunction);
176 }
177 protected function addFunctionIncludeAssets()
178 {
179 $includeAssetsFunction = new TwigFunction('includeAssets', function ($params) {
180 if (!isset($params['type'])) {
181 throw new Exception("The function includeAssets needs a 'type' parameter.");
182 }
183 $assetType = strtolower($params['type']);
184 $deferJs = boolval($params['defer'] ?? \false);
185 switch ($assetType) {
186 case 'css':
187 return \Piwik\AssetManager::getInstance()->getCssInclusionDirective();
188 case 'js':
189 return \Piwik\AssetManager::getInstance()->getJsInclusionDirective($deferJs);
190 default:
191 throw new Exception("The twig function includeAssets 'type' parameter needs to be either 'css' or 'js'.");
192 }
193 });
194 $this->twig->addFunction($includeAssetsFunction);
195 }
196 protected function addFunctionPostEvent()
197 {
198 $postEventFunction = new TwigFunction('postEvent', function ($eventName) {
199 // get parameters to twig function
200 $params = func_get_args();
201 // remove the first value (event name)
202 array_shift($params);
203 // make the first value the string that will get output in the template
204 // plugins can modify this string
205 $str = '';
206 $params = array_merge(array(&$str), $params);
207 \Piwik\Piwik::postEvent($eventName, $params);
208 return $str;
209 }, array('is_safe' => array('html')));
210 $this->twig->addFunction($postEventFunction);
211 }
212 protected function addFunctionSparkline()
213 {
214 $sparklineFunction = new TwigFunction('sparkline', function ($src) {
215 $width = Sparkline::DEFAULT_WIDTH;
216 $height = Sparkline::DEFAULT_HEIGHT;
217 return sprintf(\Piwik\Twig::SPARKLINE_TEMPLATE, $src, $width, $height);
218 }, array('is_safe' => array('html')));
219 $this->twig->addFunction($sparklineFunction);
220 }
221 protected function addFunctionLinkTo()
222 {
223 $urlFunction = new TwigFunction('linkTo', function ($params) {
224 return 'index.php' . \Piwik\Url::getCurrentQueryStringWithParametersModified($params);
225 });
226 $this->twig->addFunction($urlFunction);
227 }
228 /**
229 * Build an external link for a URL
230 *
231 * Usage:
232 * externallink(url)
233 *
234 */
235 private function addFunctionExternalLink()
236 {
237 $externalLink = new TwigFunction('externallink', function ($url) {
238 // Add tracking parameters if a matomo.org link
239 $url = \Piwik\Url::addCampaignParametersToMatomoLink($url);
240 return "<a target='_blank' rel='noreferrer noopener' href='" . $url . "'>";
241 });
242 $this->twig->addFunction($externalLink);
243 }
244 private function addFunctionExternalRawLink()
245 {
246 $externalRawLink = new TwigFunction('externalrawlink', function ($url) {
247 // Add tracking parameters if a matomo.org link
248 return \Piwik\Url::addCampaignParametersToMatomoLink($url);
249 });
250 $this->twig->addFunction($externalRawLink);
251 }
252 /**
253 * @return FilesystemLoader
254 */
255 private function getDefaultThemeLoader()
256 {
257 $themeDir = Manager::getPluginDirectory(\Piwik\Plugin\Manager::DEFAULT_THEME) . '/templates/';
258 $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . \DIRECTORY_SEPARATOR);
259 return $themeLoader;
260 }
261 /**
262 * create template loader for a custom theme
263 * @param \Piwik\Plugin $theme
264 * @return FilesystemLoader|bool
265 */
266 protected function getCustomThemeLoader(\Piwik\Plugin $theme)
267 {
268 $pluginsDir = Manager::getPluginDirectory($theme->getPluginName());
269 $themeDir = $pluginsDir . '/templates/';
270 if (!file_exists($themeDir)) {
271 return \false;
272 }
273 $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . \DIRECTORY_SEPARATOR);
274 return $themeLoader;
275 }
276 public function getTwigEnvironment()
277 {
278 return $this->twig;
279 }
280 protected function addFilterNotification()
281 {
282 $twigEnv = $this->getTwigEnvironment();
283 $notificationFunction = new TwigFilter('notification', function ($message, $options) use($twigEnv) {
284 $template = '<div style="display:none" data-role="notification" ';
285 foreach ($options as $key => $value) {
286 if (ctype_alpha($key)) {
287 $template .= sprintf('data-%s="%s" ', $key, \Matomo\Dependencies\twig_escape_filter($twigEnv, $value, 'html_attr'));
288 }
289 }
290 $template .= '>';
291 if (!empty($options['raw'])) {
292 $template .= $message;
293 } else {
294 $template .= piwik_escape_filter($twigEnv, $message, 'html');
295 }
296 $template .= '</div>';
297 return $template;
298 }, array('is_safe' => array('html')));
299 $this->twig->addFilter($notificationFunction);
300 }
301 protected function addFilterSafeDecodeRaw()
302 {
303 $rawSafeDecoded = new TwigFilter('rawSafeDecoded', function ($string) {
304 if ($string === null) {
305 return '';
306 }
307 $string = str_replace('+', '%2B', $string);
308 $string = str_replace('&nbsp;', html_entity_decode('&nbsp;', \ENT_COMPAT | \ENT_HTML401, 'UTF-8'), $string);
309 $string = SafeDecodeLabel::decodeLabelSafe($string);
310 return $string;
311 }, array('is_safe' => array('all')));
312 $this->twig->addFilter($rawSafeDecoded);
313 }
314 protected function addFilterPrettyDate()
315 {
316 $prettyDate = new TwigFilter('prettyDate', function ($dateString, $period) {
317 return \Piwik\Period\Factory::build($period, $dateString)->getLocalizedShortString();
318 });
319 $this->twig->addFilter($prettyDate);
320 }
321 protected function addFilterPercentage()
322 {
323 $percentage = new TwigFilter('percentage', function ($string, $totalValue, $precision = 1) {
324 $formatter = \Piwik\NumberFormatter::getInstance();
325 return $formatter->formatPercent(\Piwik\Piwik::getPercentageSafe($string, $totalValue, $precision), $precision);
326 });
327 $this->twig->addFilter($percentage);
328 }
329 protected function addFilterPercent()
330 {
331 $percentage = new TwigFilter('percent', function ($string, $precision = 1) {
332 $formatter = \Piwik\NumberFormatter::getInstance();
333 return $formatter->formatPercent($string, $precision);
334 });
335 $this->twig->addFilter($percentage);
336 }
337 protected function addFilterPercentEvolution()
338 {
339 $percentage = new TwigFilter('percentEvolution', function ($string) {
340 $formatter = \Piwik\NumberFormatter::getInstance();
341 return $formatter->formatPercentEvolution($string);
342 });
343 $this->twig->addFilter($percentage);
344 }
345 private function getProfessionalServicesAdvertising()
346 {
347 return StaticContainer::get('Piwik\\ProfessionalServices\\Advertising');
348 }
349 protected function addFilterNumber()
350 {
351 $formatter = new TwigFilter('number', function ($string, $minFractionDigits = 0, $maxFractionDigits = 0) {
352 return piwik_format_number($string, $minFractionDigits, $maxFractionDigits);
353 });
354 $this->twig->addFilter($formatter);
355 }
356 protected function addFilterAnonymiseSystemInfo()
357 {
358 $formatter = new TwigFilter('anonymiseSystemInfo', function ($string) {
359 if ($string === null) {
360 return '';
361 }
362 if ($string === \false || $string === \true) {
363 return (int) $string;
364 }
365 $string = str_replace([PIWIK_DOCUMENT_ROOT, str_replace('/', '\\/', PIWIK_DOCUMENT_ROOT)], '$DOC_ROOT', $string);
366 $string = str_replace([PIWIK_USER_PATH, str_replace('/', '\\/', PIWIK_USER_PATH)], '$USER_PATH', $string);
367 $string = str_replace([PIWIK_INCLUDE_PATH, str_replace('/', '\\/', PIWIK_INCLUDE_PATH)], '$INCLUDE_PATH', $string);
368 // replace anything token like
369 $string = preg_replace('/[[:xdigit:]]{31,80}/', 'TOKEN_REPLACED', $string);
370 // just in case it was somehow show in a text
371 if (\Piwik\SettingsPiwik::isMatomoInstalled()) {
372 $string = str_replace(\Piwik\SettingsPiwik::getPiwikUrl(), '$MATOMO_URL', $string);
373 $string = str_replace(\Piwik\SettingsPiwik::getSalt(), '$MATOMO_SALT', $string);
374 }
375 return $string;
376 });
377 $this->twig->addFilter($formatter);
378 }
379 protected function addFilterNonce()
380 {
381 $nonce = new TwigFilter('nonce', array('Piwik\\Nonce', 'getNonce'));
382 $this->twig->addFilter($nonce);
383 }
384 private function addFilterMd5()
385 {
386 $md5 = new TwigFilter('md5', function ($value) {
387 return md5($value);
388 });
389 $this->twig->addFilter($md5);
390 }
391 private function addFilterOnlyDomain()
392 {
393 $domainOnly = new TwigFilter('domainOnly', function ($url) {
394 $parsed = parse_url($url);
395 return $parsed['scheme'] . '://' . $parsed['host'];
396 });
397 $this->twig->addFilter($domainOnly);
398 }
399 protected function addFilterTruncate()
400 {
401 $truncateFilter = new TwigFilter('truncate', function ($string, $size) {
402 return piwik_filter_truncate($string, $size);
403 });
404 $this->twig->addFilter($truncateFilter);
405 }
406 protected function addFilterMoney()
407 {
408 $moneyFilter = new TwigFilter('money', function ($amount) {
409 if (func_num_args() != 2) {
410 throw new Exception('the money modifier expects one parameter: the idSite.');
411 }
412 $idSite = func_get_args();
413 $idSite = $idSite[1];
414 return piwik_format_money($amount, $idSite);
415 });
416 $this->twig->addFilter($moneyFilter);
417 }
418 protected function addFilterSumTime()
419 {
420 $formatter = $this->formatter;
421 $sumtimeFilter = new TwigFilter('sumtime', function ($numberOfSeconds) use($formatter) {
422 return $formatter->getPrettyTimeFromSeconds($numberOfSeconds, \true);
423 });
424 $this->twig->addFilter($sumtimeFilter);
425 }
426 protected function addFilterUrlRewriteWithParameters()
427 {
428 $urlRewriteFilter = new TwigFilter('urlRewriteWithParameters', function ($parameters) {
429 $parameters['updated'] = null;
430 $url = \Piwik\Url::getCurrentQueryStringWithParametersModified($parameters);
431 return $url;
432 });
433 $this->twig->addFilter($urlRewriteFilter);
434 }
435 protected function addFilterTranslate()
436 {
437 $translateFilter = new TwigFilter('translate', function ($stringToken) {
438 if (func_num_args() <= 1) {
439 $aValues = array();
440 } else {
441 $aValues = func_get_args();
442 array_shift($aValues);
443 }
444 try {
445 $stringTranslated = \Piwik\Piwik::translate($stringToken, $aValues);
446 } catch (Exception $e) {
447 $stringTranslated = $stringToken;
448 }
449 return $stringTranslated;
450 });
451 $this->twig->addFilter($translateFilter);
452 }
453 protected function addFilterListings()
454 {
455 $andListing = new TwigFilter('andListing', function ($items) {
456 if (!is_array($items)) {
457 return $items;
458 // don't do anything if input data is incorrect
459 }
460 return StaticContainer::get(Translator::class)->createAndListing($items);
461 });
462 $this->twig->addFilter($andListing);
463 $orListing = new TwigFilter('orListing', function ($items) {
464 if (!is_array($items)) {
465 return $items;
466 // don't do anything if input data is incorrect
467 }
468 return StaticContainer::get(Translator::class)->createOrListing($items);
469 });
470 $this->twig->addFilter($orListing);
471 }
472 private function addPluginNamespaces(FilesystemLoader $loader)
473 {
474 $pluginManager = \Piwik\Plugin\Manager::getInstance();
475 $plugins = $pluginManager->getAllPluginsNames();
476 foreach ($plugins as $name) {
477 $pluginsDir = Manager::getPluginDirectory($name);
478 $path = sprintf("%s/templates/", $pluginsDir);
479 if (is_dir($path)) {
480 $loader->addPath(rtrim($path, '/'), $name);
481 }
482 }
483 }
484 /**
485 *
486 * Plugin-Templates can be overwritten by putting identically named templates in plugins/[theme]/templates/plugins/[plugin]/
487 *
488 */
489 private function addCustomPluginNamespaces(FilesystemLoader $loader, $pluginName)
490 {
491 $pluginManager = \Piwik\Plugin\Manager::getInstance();
492 $plugins = $pluginManager->getAllPluginsNames();
493 $pluginsDir = Manager::getPluginDirectory($pluginName);
494 foreach ($plugins as $name) {
495 $path = sprintf("%s/templates/plugins/%s/", $pluginsDir, $name);
496 if (is_dir($path)) {
497 $loader->addPath(rtrim($path, '/'), $name);
498 }
499 }
500 }
501 /**
502 * Prepend relative paths with absolute Piwik path
503 *
504 * @param string $value relative path (pass by reference)
505 * @param int $key (don't care)
506 * @param string $path Piwik root
507 */
508 public static function addPiwikPath(&$value, $key, $path)
509 {
510 if ($value[0] != '/' && $value[0] != \DIRECTORY_SEPARATOR) {
511 $value = $path . "/{$value}";
512 }
513 }
514 private function addFilterSafelink()
515 {
516 $safelink = new TwigFilter('safelink', function ($url) {
517 if (!\Piwik\UrlHelper::isLookLikeSafeUrl($url)) {
518 return '';
519 }
520 return $url;
521 });
522 $this->twig->addFilter($safelink);
523 }
524 /**
525 * Modify any links to matomo domains to add campaign tracking parameters
526 *
527 * Typical usage:
528 *
529 * Apply default campaign tracking parameters:
530 * {{ 'https://matomo.org/faq/123'|trackmatomolink }}
531 *
532 * Apply custom campaign tracking parameters:
533 * {{ 'https://matomo.org/faq/123'|trackmatomolink('SomeCampaign', 'SomeSource', 'SomeMedium') }}
534 *
535 */
536 private function addFilterTrackMatomoLink()
537 {
538 $trackLink = new TwigFilter('trackmatomolink', function ($url) {
539 $params = func_get_args();
540 array_shift($params);
541 $campaign = count($params) > 0 ? $params[0] : null;
542 $source = count($params) > 1 ? $params[1] : null;
543 $medium = count($params) > 2 ? $params[2] : null;
544 return \Piwik\Url::addCampaignParametersToMatomoLink($url, $campaign, $source, $medium);
545 });
546 $this->twig->addFilter($trackLink);
547 }
548 private function addFilterImplode()
549 {
550 $implode = new TwigFilter('implode', function ($value, $separator) {
551 return implode($separator, $value);
552 });
553 $this->twig->addFilter($implode);
554 }
555 private function addFilterPreventLinking()
556 {
557 $preventLinking = new TwigFilter('preventLinking', function ($string) {
558 while (preg_match('/\\w+\\.\\w+/i', $string, $matches)) {
559 $string = str_replace($matches[0], str_replace('.', '.<!-- -->', $matches[0]), $string);
560 }
561 return $string;
562 }, ['is_safe' => ['all']]);
563 $this->twig->addFilter($preventLinking);
564 }
565 private function addTestIsNumeric()
566 {
567 $test = new TwigTest('numeric', function ($value) {
568 return is_numeric($value);
569 });
570 $this->twig->addTest($test);
571 }
572 }
573