";
});
$this->twig->addFunction($externalLink);
}
private function addFunctionExternalRawLink()
{
$externalRawLink = new TwigFunction('externalrawlink', function ($url) {
// Add tracking parameters if a matomo.org link
return Url::addCampaignParametersToMatomoLink($url);
});
$this->twig->addFunction($externalRawLink);
}
/**
* @return FilesystemLoader
*/
private function getDefaultThemeLoader()
{
$themeDir = Manager::getPluginDirectory(\Piwik\Plugin\Manager::DEFAULT_THEME) . '/templates/';
$themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . DIRECTORY_SEPARATOR);
return $themeLoader;
}
/**
* create template loader for a custom theme
* @param \Piwik\Plugin $theme
* @return FilesystemLoader|bool
*/
protected function getCustomThemeLoader(Plugin $theme)
{
$pluginsDir = Manager::getPluginDirectory($theme->getPluginName());
$themeDir = $pluginsDir . '/templates/';
if (!file_exists($themeDir)) {
return false;
}
$themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT . DIRECTORY_SEPARATOR);
return $themeLoader;
}
public function getTwigEnvironment()
{
return $this->twig;
}
protected function addFilterNotification()
{
$twigEnv = $this->getTwigEnvironment();
$notificationFunction = new TwigFilter('notification', function ($message, $options) use ($twigEnv) {
$template = ' $value) {
if (ctype_alpha($key)) {
$template .= sprintf('data-%s="%s" ', $key, twig_escape_filter($twigEnv, $value, 'html_attr'));
}
}
$template .= '>';
if (!empty($options['raw'])) {
$template .= $message;
} else {
$template .= piwik_escape_filter($twigEnv, $message, 'html');
}
$template .= '
';
return $template;
}, array('is_safe' => array('html')));
$this->twig->addFilter($notificationFunction);
}
protected function addFilterSafeDecodeRaw()
{
$rawSafeDecoded = new TwigFilter('rawSafeDecoded', function ($string) {
if ($string === null) {
return '';
}
$string = str_replace('+', '%2B', $string);
$string = str_replace(' ', html_entity_decode(' ', ENT_COMPAT | ENT_HTML401, 'UTF-8'), $string);
$string = SafeDecodeLabel::decodeLabelSafe($string);
return $string;
}, array('is_safe' => array('all')));
$this->twig->addFilter($rawSafeDecoded);
}
protected function addFilterPrettyDate()
{
$prettyDate = new TwigFilter('prettyDate', function ($dateString, $period) {
return Period\Factory::build($period, $dateString)->getLocalizedShortString();
});
$this->twig->addFilter($prettyDate);
}
protected function addFilterPercentage()
{
$percentage = new TwigFilter('percentage', function ($string, $totalValue, $precision = 1) {
$formatter = NumberFormatter::getInstance();
return $formatter->formatPercent(Piwik::getPercentageSafe($string, $totalValue, $precision), $precision);
});
$this->twig->addFilter($percentage);
}
protected function addFilterPercent()
{
$percentage = new TwigFilter('percent', function ($string, $precision = 1) {
$formatter = NumberFormatter::getInstance();
return $formatter->formatPercent($string, $precision);
});
$this->twig->addFilter($percentage);
}
protected function addFilterPercentEvolution()
{
$percentage = new TwigFilter('percentEvolution', function ($string) {
$formatter = NumberFormatter::getInstance();
return $formatter->formatPercentEvolution($string);
});
$this->twig->addFilter($percentage);
}
private function getProfessionalServicesAdvertising()
{
return StaticContainer::get('Piwik\ProfessionalServices\Advertising');
}
protected function addFilterNumber()
{
$formatter = new TwigFilter('number', function ($string, $minFractionDigits = 0, $maxFractionDigits = 0) {
return piwik_format_number($string, $minFractionDigits, $maxFractionDigits);
});
$this->twig->addFilter($formatter);
}
protected function addFilterAnonymiseSystemInfo()
{
$formatter = new TwigFilter('anonymiseSystemInfo', function ($string) {
if ($string === null) {
return '';
}
if ($string === false || $string === true) {
return (int) $string;
}
$string = str_replace([PIWIK_DOCUMENT_ROOT, str_replace('/', '\/', PIWIK_DOCUMENT_ROOT)], '$DOC_ROOT', $string);
$string = str_replace([PIWIK_USER_PATH, str_replace('/', '\/', PIWIK_USER_PATH) ], '$USER_PATH', $string);
$string = str_replace([PIWIK_INCLUDE_PATH, str_replace('/', '\/', PIWIK_INCLUDE_PATH) ], '$INCLUDE_PATH', $string);
// replace anything token like
$string = preg_replace('/[[:xdigit:]]{31,80}/', 'TOKEN_REPLACED', $string);
// just in case it was somehow show in a text
if (SettingsPiwik::isMatomoInstalled()) {
$string = str_replace(SettingsPiwik::getPiwikUrl(), '$MATOMO_URL', $string);
$string = str_replace(SettingsPiwik::getSalt(), '$MATOMO_SALT', $string);
}
return $string;
});
$this->twig->addFilter($formatter);
}
protected function addFilterNonce()
{
$nonce = new TwigFilter('nonce', array('Piwik\\Nonce', 'getNonce'));
$this->twig->addFilter($nonce);
}
private function addFilterMd5()
{
$md5 = new TwigFilter('md5', function ($value) {
return md5($value);
});
$this->twig->addFilter($md5);
}
private function addFilterOnlyDomain()
{
$domainOnly = new TwigFilter('domainOnly', function ($url) {
$parsed = parse_url($url);
return $parsed['scheme'] . '://' . $parsed['host'];
});
$this->twig->addFilter($domainOnly);
}
protected function addFilterTruncate()
{
$truncateFilter = new TwigFilter('truncate', function ($string, $size) {
return piwik_filter_truncate($string, $size);
});
$this->twig->addFilter($truncateFilter);
}
protected function addFilterMoney()
{
$moneyFilter = new TwigFilter('money', function ($amount) {
if (func_num_args() != 2) {
throw new Exception('the money modifier expects one parameter: the idSite.');
}
$idSite = func_get_args();
$idSite = $idSite[1];
return piwik_format_money($amount, $idSite);
});
$this->twig->addFilter($moneyFilter);
}
protected function addFilterSumTime()
{
$formatter = $this->formatter;
$sumtimeFilter = new TwigFilter('sumtime', function ($numberOfSeconds) use ($formatter) {
return $formatter->getPrettyTimeFromSeconds($numberOfSeconds, true);
});
$this->twig->addFilter($sumtimeFilter);
}
protected function addFilterUrlRewriteWithParameters()
{
$urlRewriteFilter = new TwigFilter('urlRewriteWithParameters', function ($parameters) {
$parameters['updated'] = null;
$url = Url::getCurrentQueryStringWithParametersModified($parameters);
return $url;
});
$this->twig->addFilter($urlRewriteFilter);
}
protected function addFilterTranslate()
{
$translateFilter = new TwigFilter('translate', function ($stringToken) {
if (func_num_args() <= 1) {
$aValues = array();
} else {
$aValues = func_get_args();
array_shift($aValues);
}
try {
$stringTranslated = Piwik::translate($stringToken, $aValues);
} catch (Exception $e) {
$stringTranslated = $stringToken;
}
return $stringTranslated;
});
$this->twig->addFilter($translateFilter);
}
protected function addFilterListings()
{
$andListing = new TwigFilter('andListing', function ($items) {
if (!is_array($items)) {
return $items; // don't do anything if input data is incorrect
}
return StaticContainer::get(Translator::class)->createAndListing($items);
});
$this->twig->addFilter($andListing);
$orListing = new TwigFilter('orListing', function ($items) {
if (!is_array($items)) {
return $items; // don't do anything if input data is incorrect
}
return StaticContainer::get(Translator::class)->createOrListing($items);
});
$this->twig->addFilter($orListing);
}
private function addPluginNamespaces(FilesystemLoader $loader)
{
$pluginManager = \Piwik\Plugin\Manager::getInstance();
$plugins = $pluginManager->getAllPluginsNames();
foreach ($plugins as $name) {
$pluginsDir = Manager::getPluginDirectory($name);
$path = sprintf("%s/templates/", $pluginsDir);
if (is_dir($path)) {
$loader->addPath(rtrim($path, '/'), $name);
}
}
}
/**
*
* Plugin-Templates can be overwritten by putting identically named templates in plugins/[theme]/templates/plugins/[plugin]/
*
*/
private function addCustomPluginNamespaces(FilesystemLoader $loader, $pluginName)
{
$pluginManager = \Piwik\Plugin\Manager::getInstance();
$plugins = $pluginManager->getAllPluginsNames();
$pluginsDir = Manager::getPluginDirectory($pluginName);
foreach ($plugins as $name) {
$path = sprintf("%s/templates/plugins/%s/", $pluginsDir, $name);
if (is_dir($path)) {
$loader->addPath(rtrim($path, '/'), $name);
}
}
}
/**
* Prepend relative paths with absolute Piwik path
*
* @param string $value relative path (pass by reference)
* @param int $key (don't care)
* @param string $path Piwik root
*/
public static function addPiwikPath(&$value, $key, $path)
{
if ($value[0] != '/' && $value[0] != DIRECTORY_SEPARATOR) {
$value = $path . "/$value";
}
}
private function addFilterSafelink()
{
$safelink = new TwigFilter('safelink', function ($url) {
if (!UrlHelper::isLookLikeSafeUrl($url)) {
return '';
}
return $url;
});
$this->twig->addFilter($safelink);
}
/**
* Modify any links to matomo domains to add campaign tracking parameters
*
* Typical usage:
*
* Apply default campaign tracking parameters:
* {{ 'https://matomo.org/faq/123'|trackmatomolink }}
*
* Apply custom campaign tracking parameters:
* {{ 'https://matomo.org/faq/123'|trackmatomolink('SomeCampaign', 'SomeSource', 'SomeMedium') }}
*
*/
private function addFilterTrackMatomoLink()
{
$trackLink = new TwigFilter('trackmatomolink', function ($url) {
$params = func_get_args();
array_shift($params);
$campaign = (count($params) > 0 ? $params[0] : null);
$source = (count($params) > 1 ? $params[1] : null);
$medium = (count($params) > 2 ? $params[2] : null);
return Url::addCampaignParametersToMatomoLink($url, $campaign, $source, $medium);
});
$this->twig->addFilter($trackLink);
}
private function addFilterImplode()
{
$implode = new TwigFilter('implode', function ($value, $separator) {
return implode($separator, $value);
});
$this->twig->addFilter($implode);
}
private function addTestIsNumeric()
{
$test = new TwigTest(
'numeric',
function ($value) {
return is_numeric($value);
}
);
$this->twig->addTest($test);
}
}