diff options
27 files changed, 233 insertions, 151 deletions
diff --git a/composer.json b/composer.json index b66bec9c80..cec2176dfc 100644 --- a/composer.json +++ b/composer.json @@ -20,13 +20,13 @@ }, "config":{ "platform": { - "php": "7.2.0" + "php": "7.2.5" }, "prepend-autoloader": false, "sort-packages": true }, "require": { - "php": ">=7.2.0", + "php": ">=7.2.5", "composer/semver": "~1.3.0", "davaxi/sparkline": "~1.2", "geoip2/geoip2": "^2.8", @@ -37,7 +37,7 @@ "matomo/ini": "~2.0", "matomo/matomo-php-tracker": "dev-4.x-dev", "matomo/network": "~2.0", - "matomo/referrer-spam-blacklist": "~3.0", + "matomo/referrer-spam-blacklist": "^3.11", "matomo/searchengine-and-social-list": "~3.0", "monolog/monolog": "~1.11", "mustangostang/spyc": "~0.6.0", @@ -51,7 +51,7 @@ "symfony/monolog-bridge": "~2.6.0", "szymach/c-pchart": "^2.0", "tecnickcom/tcpdf": "~6.0", - "twig/twig": "~1.0" + "twig/twig": "^3.0" }, "require-dev": { "lox/xhprof": "dev-master", diff --git a/composer.lock b/composer.lock index a7776941ef..db9c0c696f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a03445c07f2354bce4f64a147a55cdb3", + "content-hash": "af2d8862dd5d4cf2d4a8570cb4e5f307", "packages": [ { "name": "composer/ca-bundle", @@ -1696,6 +1696,65 @@ "time": "2019-11-27T13:56:44+00:00" }, { + "name": "symfony/polyfill-mbstring", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.14-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2020-01-13T11:15:53+00:00" + }, + { "name": "szymach/c-pchart", "version": "v2.0.12", "source": { @@ -1823,37 +1882,34 @@ }, { "name": "twig/twig", - "version": "v1.42.4", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e587180584c3d2d6cb864a0454e777bb6dcb6152" + "reference": "3b88ccd180a6b61ebb517aea3b1a8906762a1dc2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e587180584c3d2d6cb864a0454e777bb6dcb6152", - "reference": "e587180584c3d2d6cb864a0454e777bb6dcb6152", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3b88ccd180a6b61ebb517aea3b1a8906762a1dc2", + "reference": "3b88ccd180a6b61ebb517aea3b1a8906762a1dc2", "shasum": "" }, "require": { - "php": ">=5.5.0", - "symfony/polyfill-ctype": "^1.8" + "php": "^7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { "psr/container": "^1.0", - "symfony/debug": "^3.4|^4.2", - "symfony/phpunit-bridge": "^4.4@dev|^5.0" + "symfony/phpunit-bridge": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.42-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-0": { - "Twig_": "lib/" - }, "psr-4": { "Twig\\": "src/" } @@ -1871,7 +1927,6 @@ }, { "name": "Twig Team", - "homepage": "https://twig.symfony.com/contributors", "role": "Contributors" }, { @@ -1885,7 +1940,7 @@ "keywords": [ "templating" ], - "time": "2019-11-11T16:49:32+00:00" + "time": "2020-02-11T15:33:47+00:00" } ], "packages-dev": [ @@ -3508,11 +3563,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.2.0" + "php": ">=7.2.5" }, "platform-dev": [], "platform-overrides": { - "php": "7.2.0" + "php": "7.2.5" }, "plugin-api-version": "1.1.0" } diff --git a/core/FrontController.php b/core/FrontController.php index 58659da1d1..00b032833e 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -176,7 +176,7 @@ class FrontController extends Singleton * @param \Piwik\NoAccessException $exception The exception that was caught. */ Piwik::postEvent('User.isNotAuthorized', array($exception), $pending = true); - } catch (\Twig_Error_Runtime $e) { + } catch (\Twig\Error\RuntimeError $e) { echo $this->generateSafeModeOutputFromException($e); exit; } catch(StylesheetLessCompileException $e) { diff --git a/core/Twig.php b/core/Twig.php index 78dec0b19b..1ff38003ab 100644 --- a/core/Twig.php +++ b/core/Twig.php @@ -16,13 +16,13 @@ use Piwik\Plugin\Manager; use Piwik\Tracker\GoalManager; use Piwik\View\RenderTokenParser; use Piwik\Visualization\Sparkline; -use Twig_Environment; -use Twig_Extension_Debug; -use Twig_Loader_Chain; -use Twig_Loader_Filesystem; -use Twig_SimpleFilter; -use Twig_SimpleFunction; -use Twig_SimpleTest; +use Twig\Environment; +use Twig\Extension\DebugExtension; +use Twig\Loader\ChainLoader; +use Twig\Loader\FilesystemLoader; +use Twig\TwigFilter; +use Twig\TwigFunction; +use Twig\TwigTest; function piwik_filter_truncate($string, $size) { @@ -64,7 +64,7 @@ function piwik_fix_lbrace($string) return $string; } -function piwik_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) { +function piwik_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) { $string = twig_escape_filter($env, $string, $strategy, $charset, $autoescape); @@ -89,13 +89,13 @@ function piwik_format_money($amount, $idSite) return $numberFormatter->formatCurrency($amount, $currencySymbol, GoalManager::REVENUE_PRECISION); } -class PiwikTwigFilterExtension extends \Twig_Extension +class PiwikTwigFilterExtension extends \Twig\Extension\AbstractExtension { public function getFilters() { return array( - new Twig_SimpleFilter('e', '\Piwik\piwik_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), - new Twig_SimpleFilter('escape', '\Piwik\piwik_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')) + new TwigFilter('e', '\Piwik\piwik_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + new TwigFilter('escape', '\Piwik\piwik_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')) ); } @@ -120,7 +120,7 @@ class Twig <script type="text/javascript">$(function() { piwik.initSparklines(); });</script>'; /** - * @var Twig_Environment + * @var Environment */ private $twig; @@ -150,20 +150,19 @@ class Twig $loaders[] = $loader; - $chainLoader = new Twig_Loader_Chain($loaders); + $chainLoader = new ChainLoader($loaders); // Create new Twig Environment and set cache dir $templatesCompiledPath = StaticContainer::get('path.tmp') . '/templates_c'; - $this->twig = new Twig_Environment($chainLoader, + $this->twig = new Environment($chainLoader, array( 'debug' => true, // to use {{ dump(var) }} in twig templates 'strict_variables' => true, // throw an exception if variables are invalid 'cache' => $templatesCompiledPath, ) ); - $this->twig->addExtension(new Twig_Extension_Debug()); - $this->twig->clearTemplateCache(); + $this->twig->addExtension(new DebugExtension()); $this->addFilter_translate(); $this->addFilter_urlRewriteWithParameters(); @@ -181,10 +180,10 @@ class Twig $this->addFilter_md5(); $this->addFilter_onlyDomain(); $this->addFilter_safelink(); - $this->twig->addFilter(new Twig_SimpleFilter('implode', 'implode')); - $this->twig->addFilter(new Twig_SimpleFilter('ucwords', 'ucwords')); - $this->twig->addFilter(new Twig_SimpleFilter('lcfirst', 'lcfirst')); - $this->twig->addFilter(new Twig_SimpleFilter('ucfirst', 'ucfirst')); + $this->twig->addFilter(new TwigFilter('implode', 'implode')); + $this->twig->addFilter(new TwigFilter('ucwords', 'ucwords')); + $this->twig->addFilter(new TwigFilter('lcfirst', 'lcfirst')); + $this->twig->addFilter(new TwigFilter('ucfirst', 'ucfirst')); $this->addFunction_includeAssets(); $this->addFunction_linkTo(); @@ -205,7 +204,7 @@ class Twig private function addTest_false() { - $test = new Twig_SimpleTest( + $test = new TwigTest( 'false', function ($value) { return false === $value; @@ -216,7 +215,7 @@ class Twig private function addTest_true() { - $test = new Twig_SimpleTest( + $test = new TwigTest( 'true', function ($value) { return true === $value; @@ -227,7 +226,7 @@ class Twig private function addTest_emptyString() { - $test = new Twig_SimpleTest( + $test = new TwigTest( 'emptyString', function ($value) { return '' === $value; @@ -238,7 +237,7 @@ class Twig protected function addFunction_getJavascriptTranslations() { - $getJavascriptTranslations = new Twig_SimpleFunction( + $getJavascriptTranslations = new TwigFunction( 'getJavascriptTranslations', array(StaticContainer::get('Piwik\Translation\Translator'), 'getJavascriptTranslations') ); @@ -247,7 +246,7 @@ class Twig protected function addFunction_isPluginLoaded() { - $isPluginLoadedFunction = new Twig_SimpleFunction('isPluginLoaded', function ($pluginName) { + $isPluginLoadedFunction = new TwigFunction('isPluginLoaded', function ($pluginName) { return \Piwik\Plugin\Manager::getInstance()->isPluginLoaded($pluginName); }); $this->twig->addFunction($isPluginLoadedFunction); @@ -255,7 +254,7 @@ class Twig protected function addFunction_includeAssets() { - $includeAssetsFunction = new Twig_SimpleFunction('includeAssets', function ($params) { + $includeAssetsFunction = new TwigFunction('includeAssets', function ($params) { if (!isset($params['type'])) { throw new Exception("The function includeAssets needs a 'type' parameter."); } @@ -275,7 +274,7 @@ class Twig protected function addFunction_postEvent() { - $postEventFunction = new Twig_SimpleFunction('postEvent', function ($eventName) { + $postEventFunction = new TwigFunction('postEvent', function ($eventName) { // get parameters to twig function $params = func_get_args(); // remove the first value (event name) @@ -294,7 +293,7 @@ class Twig protected function addFunction_sparkline() { - $sparklineFunction = new Twig_SimpleFunction('sparkline', function ($src) { + $sparklineFunction = new TwigFunction('sparkline', function ($src) { $width = Sparkline::DEFAULT_WIDTH; $height = Sparkline::DEFAULT_HEIGHT; return sprintf(Twig::SPARKLINE_TEMPLATE, $src, $width, $height); @@ -304,19 +303,19 @@ class Twig protected function addFunction_linkTo() { - $urlFunction = new Twig_SimpleFunction('linkTo', function ($params) { + $urlFunction = new TwigFunction('linkTo', function ($params) { return 'index.php' . Url::getCurrentQueryStringWithParametersModified($params); }); $this->twig->addFunction($urlFunction); } /** - * @return Twig_Loader_Filesystem + * @return FilesystemLoader */ private function getDefaultThemeLoader() { $themeDir = Manager::getPluginDirectory(\Piwik\Plugin\Manager::DEFAULT_THEME) . '/templates/'; - $themeLoader = new Twig_Loader_Filesystem(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); + $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); return $themeLoader; } @@ -324,7 +323,7 @@ class Twig /** * create template loader for a custom theme * @param \Piwik\Plugin $theme - * @return \Twig_Loader_Filesystem + * @return FilesystemLoader|bool */ protected function getCustomThemeLoader(Plugin $theme) { @@ -334,7 +333,7 @@ class Twig if (!file_exists($themeDir)) { return false; } - $themeLoader = new Twig_Loader_Filesystem(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); + $themeLoader = new FilesystemLoader(array($themeDir), PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR); return $themeLoader; } @@ -347,7 +346,7 @@ class Twig protected function addFilter_notification() { $twigEnv = $this->getTwigEnvironment(); - $notificationFunction = new Twig_SimpleFilter('notification', function ($message, $options) use ($twigEnv) { + $notificationFunction = new TwigFilter('notification', function ($message, $options) use ($twigEnv) { $template = '<div style="display:none" data-role="notification" '; @@ -375,7 +374,7 @@ class Twig protected function addFilter_safeDecodeRaw() { - $rawSafeDecoded = new Twig_SimpleFilter('rawSafeDecoded', function ($string) { + $rawSafeDecoded = new TwigFilter('rawSafeDecoded', function ($string) { $string = str_replace('+', '%2B', $string); $string = str_replace(' ', html_entity_decode(' ', ENT_COMPAT | ENT_HTML401, 'UTF-8'), $string); @@ -389,7 +388,7 @@ class Twig protected function addFilter_prettyDate() { - $prettyDate = new Twig_SimpleFilter('prettyDate', function ($dateString, $period) { + $prettyDate = new TwigFilter('prettyDate', function ($dateString, $period) { return Period\Factory::build($period, $dateString)->getLocalizedShortString(); }); $this->twig->addFilter($prettyDate); @@ -397,7 +396,7 @@ class Twig protected function addFilter_percentage() { - $percentage = new Twig_SimpleFilter('percentage', function ($string, $totalValue, $precision = 1) { + $percentage = new TwigFilter('percentage', function ($string, $totalValue, $precision = 1) { $formatter = NumberFormatter::getInstance(); return $formatter->formatPercent(Piwik::getPercentageSafe($string, $totalValue, $precision), $precision); }); @@ -406,7 +405,7 @@ class Twig protected function addFilter_percent() { - $percentage = new Twig_SimpleFilter('percent', function ($string, $precision = 1) { + $percentage = new TwigFilter('percent', function ($string, $precision = 1) { $formatter = NumberFormatter::getInstance(); return $formatter->formatPercent($string, $precision); }); @@ -415,7 +414,7 @@ class Twig protected function addFilter_percentEvolution() { - $percentage = new Twig_SimpleFilter('percentEvolution', function ($string) { + $percentage = new TwigFilter('percentEvolution', function ($string) { $formatter = NumberFormatter::getInstance(); return $formatter->formatPercentEvolution($string); }); @@ -429,7 +428,7 @@ class Twig protected function addFilter_number() { - $formatter = new Twig_SimpleFilter('number', function ($string, $minFractionDigits = 0, $maxFractionDigits = 0) { + $formatter = new TwigFilter('number', function ($string, $minFractionDigits = 0, $maxFractionDigits = 0) { return piwik_format_number($string, $minFractionDigits, $maxFractionDigits); }); $this->twig->addFilter($formatter); @@ -437,13 +436,13 @@ class Twig protected function addFilter_nonce() { - $nonce = new Twig_SimpleFilter('nonce', array('Piwik\\Nonce', 'getNonce')); + $nonce = new TwigFilter('nonce', array('Piwik\\Nonce', 'getNonce')); $this->twig->addFilter($nonce); } private function addFilter_md5() { - $md5 = new \Twig_SimpleFilter('md5', function ($value) { + $md5 = new TwigFilter('md5', function ($value) { return md5($value); }); $this->twig->addFilter($md5); @@ -451,7 +450,7 @@ class Twig private function addFilter_onlyDomain() { - $domainOnly = new \Twig_SimpleFilter('domainOnly', function ($url) { + $domainOnly = new TwigFilter('domainOnly', function ($url) { $parsed = parse_url($url); return $parsed['scheme'] . '://' . $parsed['host']; }); @@ -460,7 +459,7 @@ class Twig protected function addFilter_truncate() { - $truncateFilter = new Twig_SimpleFilter('truncate', function ($string, $size) { + $truncateFilter = new TwigFilter('truncate', function ($string, $size) { return piwik_filter_truncate($string, $size); }); $this->twig->addFilter($truncateFilter); @@ -468,7 +467,7 @@ class Twig protected function addFilter_money() { - $moneyFilter = new Twig_SimpleFilter('money', function ($amount) { + $moneyFilter = new TwigFilter('money', function ($amount) { if (func_num_args() != 2) { throw new Exception('the money modifier expects one parameter: the idSite.'); } @@ -482,7 +481,7 @@ class Twig protected function addFilter_sumTime() { $formatter = $this->formatter; - $sumtimeFilter = new Twig_SimpleFilter('sumtime', function ($numberOfSeconds) use ($formatter) { + $sumtimeFilter = new TwigFilter('sumtime', function ($numberOfSeconds) use ($formatter) { return $formatter->getPrettyTimeFromSeconds($numberOfSeconds, true); }); $this->twig->addFilter($sumtimeFilter); @@ -490,7 +489,7 @@ class Twig protected function addFilter_urlRewriteWithParameters() { - $urlRewriteFilter = new Twig_SimpleFilter('urlRewriteWithParameters', function ($parameters) { + $urlRewriteFilter = new TwigFilter('urlRewriteWithParameters', function ($parameters) { $parameters['updated'] = null; $url = Url::getCurrentQueryStringWithParametersModified($parameters); return $url; @@ -500,7 +499,7 @@ class Twig protected function addFilter_translate() { - $translateFilter = new Twig_SimpleFilter('translate', function ($stringToken) { + $translateFilter = new TwigFilter('translate', function ($stringToken) { if (func_num_args() <= 1) { $aValues = array(); } else { @@ -518,7 +517,7 @@ class Twig $this->twig->addFilter($translateFilter); } - private function addPluginNamespaces(Twig_Loader_Filesystem $loader) + private function addPluginNamespaces(FilesystemLoader $loader) { $pluginManager = \Piwik\Plugin\Manager::getInstance(); $plugins = $pluginManager->getAllPluginsNames(); @@ -537,7 +536,7 @@ class Twig * Plugin-Templates can be overwritten by putting identically named templates in plugins/[theme]/templates/plugins/[plugin]/ * */ - private function addCustomPluginNamespaces(Twig_Loader_Filesystem $loader, $pluginName) + private function addCustomPluginNamespaces(FilesystemLoader $loader, $pluginName) { $pluginManager = \Piwik\Plugin\Manager::getInstance(); $plugins = $pluginManager->getAllPluginsNames(); @@ -568,7 +567,7 @@ class Twig private function addFilter_safelink() { - $safelink = new Twig_SimpleFilter('safelink', function ($url) { + $safelink = new TwigFilter('safelink', function ($url) { if (!UrlHelper::isLookLikeSafeUrl($url)) { return ''; } @@ -579,7 +578,7 @@ class Twig private function addTest_isNumeric() { - $test = new Twig_SimpleTest( + $test = new TwigTest( 'numeric', function ($value) { return is_numeric($value); diff --git a/core/View.php b/core/View.php index b4f9bcd453..707030b2c3 100644 --- a/core/View.php +++ b/core/View.php @@ -12,7 +12,8 @@ use Exception; use Piwik\AssetManager\UIAssetCacheBuster; use Piwik\Container\StaticContainer; use Piwik\View\ViewInterface; -use Twig_Environment; +use Twig\Environment; +use Twig\Error\Error; /** * Transition for pre-Piwik 0.4.4 @@ -111,7 +112,7 @@ class View implements ViewInterface /** * Instance - * @var Twig_Environment + * @var Environment */ private $twig; protected $templateVars = array(); @@ -305,7 +306,7 @@ class View implements ViewInterface /** * @internal * @ignore - * @return Twig_Environment + * @return Environment */ public function getTwig() { @@ -314,15 +315,7 @@ class View implements ViewInterface protected function renderTwigTemplate() { - try { - $output = $this->twig->render($this->getTemplateFile(), $this->getTemplateVars()); - } catch (Exception $ex) { - // twig does not rethrow exceptions, it wraps them so we log the cause if we can find it - $cause = $ex->getPrevious(); - Log::debug($cause === null ? $ex : $cause); - - throw $ex; - } + $output = $this->twig->render($this->getTemplateFile(), $this->getTemplateVars()); if ($this->enableCacheBuster) { $output = $this->applyFilter_cacheBuster($output); @@ -440,16 +433,8 @@ class View implements ViewInterface */ public static function clearCompiledTemplates() { - $twig = StaticContainer::get(Twig::class); - $environment = $twig->getTwigEnvironment(); - $environment->clearTemplateCache(); - - $cacheDirectory = $environment->getCache(); - if (!empty($cacheDirectory) - && is_dir($cacheDirectory) - ) { - $environment->clearCacheFiles(); - } + $templatesCompiledPath = StaticContainer::get('path.tmp') . '/templates_c'; + Filesystem::unlinkRecursive($templatesCompiledPath, false); } /** diff --git a/core/View/MethodCallExpression.php b/core/View/MethodCallExpression.php new file mode 100644 index 0000000000..111d4e7a5a --- /dev/null +++ b/core/View/MethodCallExpression.php @@ -0,0 +1,48 @@ +<?php +/* + * This file is a modified file from Twig 1.x. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with it's source code. + */ + +namespace Piwik\View; + +use Twig\Compiler; +use Twig\Node\Expression\AbstractExpression; +use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\NameExpression; + +class MethodCallExpression extends AbstractExpression +{ + public function __construct(AbstractExpression $node, $method, ArrayExpression $arguments, $lineno) + { + parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno); + + if ($node instanceof NameExpression) { + $node->setAttribute('always_defined', true); + } + } + + public function compile(Compiler $compiler) + { + $compiler + ->subcompile($this->getNode('node')) + ->raw('->') + ->raw($this->getAttribute('method')) + ->raw('(') + ; + $first = true; + foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + $compiler->subcompile($pair['value']); + } + $compiler->raw(')'); + } +}
\ No newline at end of file diff --git a/core/View/RenderTokenParser.php b/core/View/RenderTokenParser.php index bfcbd43057..099c46108d 100644 --- a/core/View/RenderTokenParser.php +++ b/core/View/RenderTokenParser.php @@ -8,11 +8,10 @@ */ namespace Piwik\View; -use Twig_Node_Expression_Array; -use Twig_Node_Expression_MethodCall; -use Twig_Node_Include; -use Twig_Token; -use Twig_TokenParser; +use Twig\Node\Expression\ArrayExpression; +use Twig\Node\IncludeNode; +use Twig\Token; +use Twig\TokenParser\AbstractTokenParser; /** * Defines a new Twig tag that will render a Piwik View. @@ -23,45 +22,45 @@ use Twig_TokenParser; * * where `theView` is a variable referencing a View instance. */ -class RenderTokenParser extends Twig_TokenParser +class RenderTokenParser extends AbstractTokenParser { /** * Parses the Twig stream and creates a Twig_Node_Include instance that includes * the View's template. * - * @return Twig_Node_Include + * @return \Twig\Node\Node */ - public function parse(Twig_Token $token) + public function parse(Token $token) { $parser = $this->parser; $stream = $parser->getStream(); $view = $parser->getExpressionParser()->parseExpression(); - $variablesOverride = new Twig_Node_Expression_Array(array(), $token->getLine()); - if ($stream->test(Twig_Token::NAME_TYPE, 'with')) { + $variablesOverride = new ArrayExpression(array(), $token->getLine()); + if ($stream->test(Token::NAME_TYPE, 'with')) { $stream->next(); $variablesOverride->addElement($this->parser->getExpressionParser()->parseExpression()); } - $stream->expect(Twig_Token::BLOCK_END_TYPE); + $stream->expect(Token::BLOCK_END_TYPE); - $viewTemplateExpr = new Twig_Node_Expression_MethodCall( + $viewTemplateExpr = new MethodCallExpression( $view, 'getTemplateFile', - new Twig_Node_Expression_Array(array(), $token->getLine()), + new ArrayExpression(array(), $token->getLine()), $token->getLine() ); - $variablesExpr = new Twig_Node_Expression_MethodCall( + $variablesExpr = new MethodCallExpression( $view, 'getTemplateVars', $variablesOverride, $token->getLine() ); - return new Twig_Node_Include( + return new IncludeNode( $viewTemplateExpr, $variablesExpr, $only = false, diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php index 9dc1678b37..5bddad6bfd 100644 --- a/core/testMinimumPhpVersion.php +++ b/core/testMinimumPhpVersion.php @@ -21,7 +21,7 @@ $piwik_errorMessage = ''; // 2) tests/travis/generator/Generator.php // 3) composer.json (in two places) // 4) tests/PHPUnit/Integration/ReleaseCheckListTest.php -$piwik_minimumPHPVersion = '7.2.0'; +$piwik_minimumPHPVersion = '7.2.5'; $piwik_currentPHPVersion = PHP_VERSION; $minimumPhpInvalid = version_compare($piwik_minimumPHPVersion, $piwik_currentPHPVersion) > 0; if ($minimumPhpInvalid) { diff --git a/plugins/CoreHome/templates/_dataTableActions.twig b/plugins/CoreHome/templates/_dataTableActions.twig index aa549d84de..7322800fc7 100644 --- a/plugins/CoreHome/templates/_dataTableActions.twig +++ b/plugins/CoreHome/templates/_dataTableActions.twig @@ -11,7 +11,7 @@ {% set visualizationIcons %} <ul id='dropdownVisualizations{{ randomIdForDropdown }}' class='dropdown-content dataTableFooterIcons'> {% for footerIconGroup in footerIcons %} - {% for footerIcon in footerIconGroup.buttons if footerIcon.icon %} + {% for footerIcon in footerIconGroup.buttons|filter(footerIcon => footerIcon.icon) %} <li> {% set numIcons = numIcons + 1 %} {% set isActiveEcommerceView = clientSideParameters.abandonedCarts is defined and @@ -152,4 +152,4 @@ </li> {% endif %} </ul> - {% endif %}
\ No newline at end of file + {% endif %} diff --git a/plugins/CoreHome/templates/_dataTableCell.twig b/plugins/CoreHome/templates/_dataTableCell.twig index 7c72b57385..2021a75262 100644 --- a/plugins/CoreHome/templates/_dataTableCell.twig +++ b/plugins/CoreHome/templates/_dataTableCell.twig @@ -1,4 +1,4 @@ -{% spaceless %} +{% apply spaceless %} {% set tooltipIndex = column ~ '_tooltip' %} {% if row.getMetadata(tooltipIndex) %}<span class="cell-tooltip" data-tooltip="{{ row.getMetadata(tooltipIndex) }}">{% endif %} {% if not row.getIdSubDataTable() and column=='label' and row.getMetadata('url') %} @@ -38,4 +38,4 @@ {% endif %} {% if row.getMetadata(tooltipIndex) %}</span>{% endif %} -{% endspaceless %} +{% endapply %} diff --git a/plugins/CoreHome/templates/_topBar.twig b/plugins/CoreHome/templates/_topBar.twig index 5a8acade30..598a511c02 100644 --- a/plugins/CoreHome/templates/_topBar.twig +++ b/plugins/CoreHome/templates/_topBar.twig @@ -30,11 +30,11 @@ {% set topMenuAction = currentAction %} {% endif %} - {% spaceless %} + {% apply spaceless %} {% for label,menu in topMenu %} <li role="menuitem" class="{{ _self.isActiveItem(menu, topMenuModule, topMenuAction) }}">{{ _self.topMenuItem(label, menu._icon, menu) }}</li> {% endfor %} - {% endspaceless %} + {% endapply %} </ul> <ul class="side-nav hide-on-large-only" id="mobile-top-menu"> {% for label,menu in topMenu %} diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig index db19df33df..eb126d5c2a 100644 --- a/plugins/CorePluginsAdmin/templates/macros.twig +++ b/plugins/CorePluginsAdmin/templates/macros.twig @@ -239,8 +239,8 @@ <div class="plugin-author"> By {% if plugin.info.authors is defined -%} - {% spaceless %} - {% for author in plugin.info.authors if author.name %} + {% apply spaceless %} + {% for author in plugin.info.authors|filter(author => author.name) %} {% if author.homepage is defined %} <a title="{{ 'CorePluginsAdmin_AuthorHomepage'|translate }}" href="{{ author.homepage }}" rel="noreferrer noopener" target="_blank">{{ author.name }}</a> {% else %} @@ -250,7 +250,7 @@ , {% endif %} {% endfor %} - {% endspaceless %} + {% endapply %} {%- endif %}. </div> {% endif %} diff --git a/plugins/CorePluginsAdmin/templates/safemode.twig b/plugins/CorePluginsAdmin/templates/safemode.twig index 48f3cc11ee..87853af664 100644 --- a/plugins/CorePluginsAdmin/templates/safemode.twig +++ b/plugins/CorePluginsAdmin/templates/safemode.twig @@ -78,8 +78,8 @@ {% endif %} </p> <table> - {% for pluginName, plugin in plugins if plugin.uninstallable and plugin.activated %} - <tr {% if loop.index is divisibleby(2) %}style="background-color: #eeeeee"{% endif %}> + {% for pluginName, plugin in plugins|filter(plugin => plugin.uninstallable and plugin.activated) %} + <tr {% if loop.index is divisible by(2) %}style="background-color: #eeeeee"{% endif %}> <td style="min-width:200px;"> {{ pluginName }} </td> @@ -95,7 +95,7 @@ </table> {% set uninstalledPluginsFound = false %} - {% for pluginName, plugin in plugins if plugin.uninstallable and not plugin.activated %} + {% for pluginName, plugin in plugins|filter(plugin => plugin.uninstallable and not plugin.activated) %} {% set uninstalledPluginsFound = true %} {% endfor %} @@ -107,8 +107,8 @@ </p> <table> - {% for pluginName, plugin in plugins if plugin.uninstallable and not plugin.activated %} - <tr {% if loop.index is divisibleby(2) %}style="background-color: #eeeeee"{% endif %}> + {% for pluginName, plugin in plugins|filter(plugin => plugin.uninstallable and not plugin.activated) %} + <tr {% if loop.index is divisible by(2) %}style="background-color: #eeeeee"{% endif %}> <td style="min-width:200px;"> {{ pluginName }} </td> diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig b/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig index f9f51397e2..827135ba89 100644 --- a/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig +++ b/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig @@ -3,15 +3,15 @@ <span title="{{ value.word|rawSafeDecoded }} ({{ value.value }} {{ properties.translations[cloudColumn]|default(cloudColumn) }})" class="word size{{ value.size }} {# we strike tags with 0 hits #} {% if value.value == 0 %}valueIsZero{% endif %}"> - {% if labelMetadata[value.word].url is not sameas(false) %} + {% if labelMetadata[value.word].url is not same as(false) %} <a href="{{ labelMetadata[value.word].url }}" rel="noreferrer noopener" target="_blank"> {% endif %} - {% if labelMetadata[value.word].logo is not sameas(false) %} + {% if labelMetadata[value.word].logo is not same as(false) %} <img src="{{ labelMetadata[value.word].logo }}" width="{{ value.logoWidth }}" /> {% else %} {{ value.wordTruncated|rawSafeDecoded }} {% endif %} - {% if labelMetadata[value.word].url is not sameas(false) %}</a>{% endif %} + {% if labelMetadata[value.word].url is not same as(false) %}</a>{% endif %} </span> {% endfor %} </div> diff --git a/plugins/CoreVisualizations/templates/macros.twig b/plugins/CoreVisualizations/templates/macros.twig index 2956f591b0..d6c5ad9f56 100644 --- a/plugins/CoreVisualizations/templates/macros.twig +++ b/plugins/CoreVisualizations/templates/macros.twig @@ -5,7 +5,7 @@ {% if evolution.percent < 0 %} {% set evolutionClass = 'negative-evolution' %} {% set evolutionIcon = 'arrow_down.png' %} - {% elseif evolution.percent == 0 %} + {% elseif evolution.percent == 0 or evolution.percent == '0%' %} {% set evolutionClass = 'neutral-evolution' %} {% set evolutionIcon = 'stop.png' %} {% else %} diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts -Subproject e4407e9668e09befede000cf3869da937fb35dd +Subproject affcc9db07300506d7e739f4255a15c6cf4f510 diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions -Subproject 920cb6a38fb652308e0a044d284a05b4372aedf +Subproject 58b758bc2dc5503207a784918b0167d37eafb08 diff --git a/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png b/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png index eb8d9abcdd..7e5cbb6d74 100644 --- a/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png +++ b/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16f6eb7a77e36b275e3293d82b29a7c304587ac1dbf0064f641b2aadaa0915f8 -size 206667 +oid sha256:116075bbcf139586a8c65093f40e7f8859bb8971c024ef26e64f14e9a576c253 +size 206665 diff --git a/plugins/Insights/templates/overviewWidget.twig b/plugins/Insights/templates/overviewWidget.twig index 3c7f07145e..144d7fbe47 100644 --- a/plugins/Insights/templates/overviewWidget.twig +++ b/plugins/Insights/templates/overviewWidget.twig @@ -5,7 +5,7 @@ <div class="dataTableScroller"> <table class="dataTable" title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> - {% for dataTable in reports.getDataTables() if dataTable.getRowsCount > 0 %} + {% for dataTable in reports.getDataTables()|filter(dataTable => dataTable.getRowsCount > 0) %} {% set metadata = dataTable.getAllTableMetadata %} <thead> @@ -17,7 +17,6 @@ {% include "@Insights/table_row.twig" %} {% endfor %} </tbody> - {% endfor %} </table> </div> diff --git a/plugins/Installation/tests/UI/expected-screenshots/Installation_system_check.png b/plugins/Installation/tests/UI/expected-screenshots/Installation_system_check.png index 41b6efef59..aeee5034ca 100644 --- a/plugins/Installation/tests/UI/expected-screenshots/Installation_system_check.png +++ b/plugins/Installation/tests/UI/expected-screenshots/Installation_system_check.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b41beb22c498bcc9fcb7da38a819cdc568db5ad88c4665e2db6cdbdc87abe052 -size 188672 +oid sha256:f9692e2019e911bd5194068ae0699a5b91c65761c6a87b4e1f8394f1c8907320 +size 188686 diff --git a/plugins/Live/templates/index.twig b/plugins/Live/templates/index.twig index 1670382d21..1b025d2798 100644 --- a/plugins/Live/templates/index.twig +++ b/plugins/Live/templates/index.twig @@ -6,7 +6,7 @@ {{ visitors|raw }} </div> -{% spaceless %} +{% apply spaceless %} <div class="visitsLiveFooter"> <a title="{{ 'Live_OnClickPause'|translate('Live_VisitorsInRealTime'|translate) }}" href="javascript:void(0);" onclick="onClickPause();"> <img id="pauseImage" border="0" src="plugins/Live/images/pause.png" /> @@ -19,6 +19,6 @@ <a class="rightLink" href="#" onclick="this.href=broadcast.buildReportingUrl('category=General_Visitors&subcategory=Live_VisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a> {% endif %} </div> -{% endspaceless %} +{% endapply %} -{% if not isWidgetized %}</div>{% endif %}
\ No newline at end of file +{% if not isWidgetized %}</div>{% endif %} diff --git a/plugins/Marketplace/templates/overview.twig b/plugins/Marketplace/templates/overview.twig index 1eccd5d407..49c26637a3 100644 --- a/plugins/Marketplace/templates/overview.twig +++ b/plugins/Marketplace/templates/overview.twig @@ -1,5 +1,4 @@ {% extends inReportingMenu ? "empty.twig" : "admin.twig" %} -{% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %} {% set title %}{{ 'Marketplace_Marketplace'|translate }}{% endset %} diff --git a/plugins/Marketplace/templates/plugin-details.twig b/plugins/Marketplace/templates/plugin-details.twig index e939f67164..a24cbb3e86 100644 --- a/plugins/Marketplace/templates/plugin-details.twig +++ b/plugins/Marketplace/templates/plugin-details.twig @@ -175,7 +175,7 @@ <br /> {% endif %} </div> - + {% if plugin.specialOffer is defined and plugin.specialOffer %}<p style="color: green;"><br />{{ plugin.specialOffer }}</p>{% endif %} <p><br /></p> @@ -213,9 +213,8 @@ </dd> {% endif %} <dt>{{ 'Marketplace_Authors'|translate }}</dt> - <dd>{% for author in plugin.authors if author.name %} - - {% spaceless %} + <dd>{% for author in plugin.authors|filter(author => author.name) %} + {% apply spaceless %} {% if author.homepage %} <a target="_blank" rel="noreferrer noopener" href="{{ author.homepage }}">{{ author.name }}</a> {% elseif author.email %} @@ -227,8 +226,7 @@ {% if loop.index < plugin.authors|length %} , {% endif %} - {% endspaceless %} - + {% endapply %} {% endfor %} </dd> {% endif %} diff --git a/plugins/Marketplace/templates/plugin-list.twig b/plugins/Marketplace/templates/plugin-list.twig index 9577b5c189..a8d13dcb87 100644 --- a/plugins/Marketplace/templates/plugin-list.twig +++ b/plugins/Marketplace/templates/plugin-list.twig @@ -1,11 +1,11 @@ -{% import '@Marketplace/macros.twig' as marketplaceMacro %} - {% if pluginsToShow|length > 0 %} <div class="pluginListContainer row"> {% for plugin in pluginsToShow %} <div class="col s12 m6 l4"> {% embed 'contentBlock.twig' with {'title': ''} %} {% block content %} + {% import '@Marketplace/macros.twig' as marketplaceMacro %} + {% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %} <div class="plugin"> <h3 class="card-title" title="{{ 'General_MoreDetails'|translate }}"> <a href="#" piwik-plugin-name="{{ plugin.name }}">{{ plugin.displayName }}</a> diff --git a/plugins/UserCountry/templates/adminIndex.twig b/plugins/UserCountry/templates/adminIndex.twig index ca11eb6d06..3cd2093cb8 100644 --- a/plugins/UserCountry/templates/adminIndex.twig +++ b/plugins/UserCountry/templates/adminIndex.twig @@ -22,7 +22,7 @@ <div class="col s12 push-m9 m3">{{ 'General_InfoFor'|translate(thisIP) }}</div> </div> - {% for id,provider in locationProviders if provider.isVisible %} + {% for id,provider in locationProviders|filter(provider => provider.isVisible) %} <div class="row form-group provider{{ id|e('html_attr') }}"> <div class="col s12 m4 l2"> <p> diff --git a/tests/PHPUnit/Integration/ReleaseCheckListTest.php b/tests/PHPUnit/Integration/ReleaseCheckListTest.php index 74e4886450..730eac8967 100644 --- a/tests/PHPUnit/Integration/ReleaseCheckListTest.php +++ b/tests/PHPUnit/Integration/ReleaseCheckListTest.php @@ -27,7 +27,7 @@ class ReleaseCheckListTest extends \PHPUnit\Framework\TestCase { private $globalConfig; - const MINIMUM_PHP_VERSION = '7.2.0'; + const MINIMUM_PHP_VERSION = '7.2.5'; public function setUp(): void { diff --git a/tests/resources/trigger-fatal-exception.php b/tests/resources/trigger-fatal-exception.php index ba802fb11d..93f90e6f71 100644 --- a/tests/resources/trigger-fatal-exception.php +++ b/tests/resources/trigger-fatal-exception.php @@ -20,7 +20,7 @@ $executed = false; \Piwik\Piwik::addAction('Request.dispatch', function () use (&$executed) { if (!$executed) { $executed = true; - throw new Twig_Error_Runtime('test message'); + throw new \Twig\Error\RuntimeError('test message'); } }); |