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 / Twig.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
Twig.php
563 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\Container\StaticContainer;
14 use Piwik\DataTable\Filter\SafeDecodeLabel;
15 use Piwik\Metrics\Formatter;
16 use Piwik\Plugin\Manager;
17 use Piwik\Tracker\GoalManager;
18 use Piwik\Translation\Translator;
19 use Piwik\Twig\Extension\EscapeFilter;
20 use Piwik\View\RenderTokenParser;
21 use Piwik\Visualization\Sparkline;
22 use Matomo\Dependencies\Twig\Environment;
23 use Matomo\Dependencies\Twig\Extension\DebugExtension;
24 use Matomo\Dependencies\Twig\Loader\ChainLoader;
25 use Matomo\Dependencies\Twig\Loader\FilesystemLoader;
26 use Matomo\Dependencies\Twig\TwigFilter;
27 use Matomo\Dependencies\Twig\TwigFunction;
28 use Matomo\Dependencies\Twig\TwigTest;
29 function piwik_filter_truncate($string, $size)
30 {
31 if (mb_strlen(html_entity_decode($string)) <= $size) {
32 return $string;
33 } else {
34 preg_match('/^(&(?:[a-z\\d]+|#\\d+|#x[a-f\\d]+);|.){' . $size . '}/i', $string, $shortenString);
35 return reset($shortenString) . "...";
36 }
37 }
38 function piwik_format_number($string, $minFractionDigits, $maxFractionDigits)
39 {
40 $formatter = \Piwik\NumberFormatter::getInstance();
41 return $formatter->format($string, $minFractionDigits, $maxFractionDigits);
42 }
43 function piwik_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
44 {
45 $string = \Matomo\Dependencies\twig_escape_filter($env, $string, $strategy, $charset, $autoescape);
46 switch ($strategy) {
47 case 'url':
48 $encoded = rawurlencode('{');
49 return str_replace('{{', $encoded . $encoded, $string);
50 default:
51 return $string;
52 }
53 }
54 function piwik_format_money($amount, $idSite)
55 {
56 $currencySymbol = \Piwik\Site::getCurrencySymbolFor($idSite);
57 $numberFormatter = \Piwik\NumberFormatter::getInstance();
58 return $numberFormatter->formatCurrency($amount, $currencySymbol, GoalManager::REVENUE_PRECISION);
59 }
60 /**
61 * Twig class
62 *
63 */
64 class Twig
65 {
66 const SPARKLINE_TEMPLATE = '<img loading="lazy" alt="" data-src="%s" width="%d" height="%d" />
67 <script type="text/javascript">$(function() { piwik.initSparklines(); });</script>';
68 /**
69 * @var Environment
70 */
71 private $twig;
72 private $formatter;
73 public function __construct()
74 {
75 $loader = $this->getDefaultThemeLoader();
76 $this->addPluginNamespaces($loader);
77 //get current theme
78 $manager = \Piwik\Plugin\Manager::getInstance();
79 $theme = $manager->getThemeEnabled();
80 $loaders = array();
81 $this->formatter = new Formatter();
82 //create loader for custom theme to overwrite twig templates
83 if ($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) {
84 $customLoader = $this->getCustomThemeLoader($theme);
85 if ($customLoader) {
86 //make it possible to overwrite plugin templates
87 $this->addCustomPluginNamespaces($customLoader, $theme->getPluginName());
88 $loaders[] = $customLoader;
89 }
90 }
91 $loaders[] = $loader;
92 $chainLoader = new ChainLoader($loaders);
93 // Create new Twig Environment and set cache dir
94 $cache = StaticContainer::get('twig.cache');
95 $this->twig = new Environment($chainLoader, array(
96 'debug' => true,
97 // to use {{ dump(var) }} in twig templates
98 'strict_variables' => true,
99 // throw an exception if variables are invalid
100 'cache' => $cache,
101 ));
102 $this->twig->addExtension(new DebugExtension());
103 $this->addFilterTranslate();
104 $this->addFilterListings();
105 $this->addFilterUrlRewriteWithParameters();
106 $this->addFilterSumTime();
107 $this->addFilterMoney();
108 $this->addFilterTruncate();
109 $this->addFilterNotification();
110 $this->addFilterPercent();
111 $this->addFilterPercentage();
112 $this->addFilterPercentEvolution();
113 $this->addFilterPrettyDate();
114 $this->addFilterSafeDecodeRaw();
115 $this->addFilterNumber();
116 $this->addFilterAnonymiseSystemInfo();
117 $this->addFilterNonce();
118 $this->addFilterMd5();
119 $this->addFilterOnlyDomain();
120 $this->addFilterSafelink();
121 $this->addFilterTrackMatomoLink();
122 $this->addFilterImplode();
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 addTestIsNumeric()
556 {
557 $test = new TwigTest('numeric', function ($value) {
558 return is_numeric($value);
559 });
560 $this->twig->addTest($test);
561 }
562 }
563