PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.1.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.1.0
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
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