maj
This commit is contained in:
		
							
								
								
									
										113
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,115 @@
 | 
			
		||||
# v1.7.42.3
 | 
			
		||||
## 07/18/2023
 | 
			
		||||
 | 
			
		||||
2. [](#improved)
 | 
			
		||||
   * Fixed a typo in `Utils::isDangerousFunction`
 | 
			
		||||
 | 
			
		||||
# v1.7.42.2
 | 
			
		||||
## 07/18/2023
 | 
			
		||||
 | 
			
		||||
2. [](#improved)
 | 
			
		||||
   * In `Utils::isDangerousFunction`, handle double `\\` in `|map` twig filter to mitigate SSTI attack
 | 
			
		||||
   * Better handle empty email in `Validatoin::typeEmail()`
 | 
			
		||||
 | 
			
		||||
# v1.7.42.1
 | 
			
		||||
## 06/15/2023
 | 
			
		||||
 | 
			
		||||
2. [](#improved)
 | 
			
		||||
   * Quick fix for `isDangerousFunction` when `$name` was a closure [#3727](https://github.com/getgrav/grav/issues/3727)
 | 
			
		||||
 | 
			
		||||
# v1.7.42
 | 
			
		||||
## 06/14/2023
 | 
			
		||||
 | 
			
		||||
1. [](#new)
 | 
			
		||||
   * Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed.
 | 
			
		||||
1. [](#improved)
 | 
			
		||||
   * More robust SSTI handling in `filter`, `map`, and `reduce` Twig filters and functions
 | 
			
		||||
   * Various SSTI improvements `Utils::isDangerousFunction()`
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
   * Fixed Twig `|map()` allowing code execution
 | 
			
		||||
   * Fixed Twig `|reduce()` allowing code execution
 | 
			
		||||
 | 
			
		||||
# v1.7.41.2
 | 
			
		||||
## 06/01/2023
 | 
			
		||||
 | 
			
		||||
1. [](#improved)
 | 
			
		||||
   * Added the ability to set a configurable 'key' for the Twig Cache Tag: `{% cache 'my-key' 600 %}`
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
   * Fixed an issue with special characters in slug's would cause redirect loops
 | 
			
		||||
 | 
			
		||||
# v1.7.41.1
 | 
			
		||||
## 05/10/2023
 | 
			
		||||
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
   * Fixed certain UTF-8 characters breaking `Truncator` class [#3716](https://github.com/getgrav/grav/issues/3716)
 | 
			
		||||
 | 
			
		||||
# v1.7.41
 | 
			
		||||
## 05/09/2023
 | 
			
		||||
 | 
			
		||||
1. [](#improved)
 | 
			
		||||
   * Removed `FILTER_SANITIZE_STRING` input filter in favor of `htmlspecialchars(strip_tags())` for PHP 8.2+
 | 
			
		||||
   * Added `GRAV_SANITIZE_STRING` constant to replace `FILTER_SANITIZE_STRING` for PHP 8.2+
 | 
			
		||||
   * Support non-deprecated style dynamic properties in `Parsedown` class via `ParseDownGravTrait` for PHP 8.2+
 | 
			
		||||
   * Modified `Truncator` to not use deprecated `mb_convert_encoding()` for PHP 8.2+
 | 
			
		||||
   * Fixed passing null into `mb_strpos()` deprecated for PHP 8.2+
 | 
			
		||||
   * Updated internal `TwigDeferredExtension` to be PHP 8.2+ compatible
 | 
			
		||||
   * Upgraded `getgrav/image` fork to take advantage of various PHP 8.2+ fixes
 | 
			
		||||
   * Use `UserGroupObject::groupNames` method in blueprints for PHP 8.2+
 | 
			
		||||
   * Comment out `files-upload` deprecated message as this is not going to be removed
 | 
			
		||||
   * Added various public `Twig` class variables used by admin to address deprecated messages for PHP 8.2+
 | 
			
		||||
   * Added `parse_url` to list of PHP functions supported in Twig Extension
 | 
			
		||||
   * Added support for dynamic functions in `Parsedown` to stop deprecation messages in PHP 8.2+
 | 
			
		||||
 
 | 
			
		||||
# v1.7.40
 | 
			
		||||
## 03/22/2023
 | 
			
		||||
 | 
			
		||||
1. [](#new)
 | 
			
		||||
    * Added a new `timestamp: true|false` option for individual assets
 | 
			
		||||
1. [](#improved)
 | 
			
		||||
    * Removed outdated `xcache` setting [#3615](https://github.com/getgrav/grav/pull/3615)
 | 
			
		||||
    * Updated `robots.txt` [#3625](https://github.com/getgrav/grav/pull/3625)
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Fixed `force_ssl` redirect in case of undefined hostname [#3702](https://github.com/getgrav/grav/pull/3702)
 | 
			
		||||
    * Fixed an issue with duplicate identical page paths
 | 
			
		||||
    * Fixed `BlueprintSchema:flattenData` to properly handle ignored fields
 | 
			
		||||
    * Fixed LogViewer regex greediness [#3684](https://github.com/getgrav/grav/pull/3684)
 | 
			
		||||
    * Fixed `whoami` command [#3695](https://github.com/getgrav/grav/pull/3695)
 | 
			
		||||
 | 
			
		||||
# v1.7.39.4
 | 
			
		||||
## 02/22/2023
 | 
			
		||||
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Reverted a reorganization of `account.yaml` that caused username to be disabled [admin#2344](https://github.com/getgrav/grav-plugin-admin/issues/2344)
 | 
			
		||||
 | 
			
		||||
# v1.7.39.3
 | 
			
		||||
## 02/21/2023
 | 
			
		||||
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Fix for overzealous modular page template rendering fix in 1.7.39 causing Feed plugin to break [#3689](https://github.com/getgrav/grav/issues/3689)
 | 
			
		||||
 | 
			
		||||
# v1.7.39.2
 | 
			
		||||
## 02/20/2023
 | 
			
		||||
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Fix for invalid session breaking Flex Accounts (when switching from Regular to Flex)
 | 
			
		||||
 | 
			
		||||
# v1.7.39.1
 | 
			
		||||
## 02/20/2023
 | 
			
		||||
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Fix for broken image CSS with the latest version of DebugBar
 | 
			
		||||
 | 
			
		||||
# v1.7.39
 | 
			
		||||
## 02/19/2023
 | 
			
		||||
 | 
			
		||||
1. [](#improved)
 | 
			
		||||
    * Vendor library updates to latest versions
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
    * Various PHP 8.2 fixes
 | 
			
		||||
    * Fixed an issue with modular pages rendering thew wrong template when dynamically changing the page
 | 
			
		||||
    * Fixed an issue with `email` validation that was failing on UTF-8 characters. Following best practices and now only check for `@` and length.
 | 
			
		||||
    * Fixed PHPUnit tests to remove deprecation warnings
 | 
			
		||||
 | 
			
		||||
# v1.7.38
 | 
			
		||||
## 01/02/2023
 | 
			
		||||
 | 
			
		||||
@@ -7,7 +119,6 @@
 | 
			
		||||
   * Vendor library updates to latest versions
 | 
			
		||||
   * Updated `bin/composer.phar` to latest `2.4.4` version [#3627](https://github.com/getgrav/grav/issues/3627)
 | 
			
		||||
1. [](#bugfix)
 | 
			
		||||
 | 
			
		||||
   * Don't fail hard if pages recurse with same path
 | 
			
		||||
   * Github workflows security hardening [#3624](https://github.com/getgrav/grav/pull/3624)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										510
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										510
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -14,11 +14,8 @@ div.phpdebugbar {
 | 
			
		||||
    padding: 5px 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
 | 
			
		||||
    background-image: url(data:image/png;base64,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.phpdebugbar a.phpdebugbar-restore-btn {
 | 
			
		||||
    background-image: url(data:image/png;base64,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);
 | 
			
		||||
    width: 13px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -448,6 +448,17 @@ form:
 | 
			
		||||
              validate:
 | 
			
		||||
                type: bool
 | 
			
		||||
 | 
			
		||||
            languages.debug:
 | 
			
		||||
              type: toggle
 | 
			
		||||
              label: PLUGIN_ADMIN.LANGUAGE_DEBUG
 | 
			
		||||
              help: PLUGIN_ADMIN.LANGUAGE_DEBUG_HELP
 | 
			
		||||
              highlight: 0
 | 
			
		||||
              options:
 | 
			
		||||
                1: PLUGIN_ADMIN.YES
 | 
			
		||||
                0: PLUGIN_ADMIN.NO
 | 
			
		||||
              validate:
 | 
			
		||||
                type: bool
 | 
			
		||||
 | 
			
		||||
        http_headers:
 | 
			
		||||
          type: tab
 | 
			
		||||
          title: PLUGIN_ADMIN.HTTP_HEADERS
 | 
			
		||||
@@ -608,7 +619,6 @@ form:
 | 
			
		||||
                file: File
 | 
			
		||||
                apc: APC
 | 
			
		||||
                apcu: APCu
 | 
			
		||||
                xcache: Xcache
 | 
			
		||||
                memcache: Memcache
 | 
			
		||||
                memcached: Memcached
 | 
			
		||||
                wincache: WinCache
 | 
			
		||||
 
 | 
			
		||||
@@ -140,7 +140,7 @@ form:
 | 
			
		||||
                    multiple: true
 | 
			
		||||
                    size: large
 | 
			
		||||
                    label: PLUGIN_ADMIN.GROUPS
 | 
			
		||||
                    data-options@: '\Grav\Common\User\Group::groupNames'
 | 
			
		||||
                    data-options@: 'Grav\Common\Flex\Types\UserGroups\UserGroupObject::groupNames'
 | 
			
		||||
                    classes: fancy
 | 
			
		||||
                    help: PLUGIN_ADMIN.GROUPS_HELP
 | 
			
		||||
                    validate:
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ languages:
 | 
			
		||||
  override_locale: false                         # Override the default or system locale with language specific one
 | 
			
		||||
  content_fallback: {}                           # Custom language fallbacks. eg: {fr: ['fr', 'en']}
 | 
			
		||||
  pages_fallback_only: false                     # DEPRECATED: Use `content_fallback` instead
 | 
			
		||||
  debug: false                                   # Debug language detection
 | 
			
		||||
 | 
			
		||||
home:
 | 
			
		||||
  alias: '/home'                                 # Default path for home, ie /
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 | 
			
		||||
// Some standard defines
 | 
			
		||||
define('GRAV', true);
 | 
			
		||||
define('GRAV_VERSION', '1.7.38');
 | 
			
		||||
define('GRAV_VERSION', '1.7.42.3');
 | 
			
		||||
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
 | 
			
		||||
define('GRAV_TESTING', false);
 | 
			
		||||
 | 
			
		||||
@@ -99,3 +99,6 @@ define('RAW_CONTENT', 1);
 | 
			
		||||
define('TWIG_CONTENT', 2);
 | 
			
		||||
define('TWIG_CONTENT_LIST', 3);
 | 
			
		||||
define('TWIG_TEMPLATES', 4);
 | 
			
		||||
 | 
			
		||||
// Filters
 | 
			
		||||
define('GRAV_SANITIZE_STRING', 5001);
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,13 @@ class Assets extends PropertyObject
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add timestamp
 | 
			
		||||
        $options['timestamp'] = $this->timestamp;
 | 
			
		||||
        $timestamp_override = $options['timestamp'] ?? true;
 | 
			
		||||
 | 
			
		||||
        if (filter_var($timestamp_override, FILTER_VALIDATE_BOOLEAN)) {
 | 
			
		||||
            $options['timestamp'] = $this->timestamp;
 | 
			
		||||
        } else {
 | 
			
		||||
            $options['timestamp'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set order
 | 
			
		||||
        $group = $options['group'] ?? 'head';
 | 
			
		||||
 
 | 
			
		||||
@@ -192,6 +192,7 @@ trait AssetUtilsTrait
 | 
			
		||||
        $querystring = '';
 | 
			
		||||
 | 
			
		||||
        $asset = $asset ?? $this->asset;
 | 
			
		||||
        $attributes = $this->attributes;
 | 
			
		||||
 | 
			
		||||
        if (!empty($this->query)) {
 | 
			
		||||
            if (Utils::contains($asset, '?')) {
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
 | 
			
		||||
            $items = $name !== '' ? $this->getProperty($name)['fields'] ?? [] : $this->items;
 | 
			
		||||
            foreach ($items as $key => $rules) {
 | 
			
		||||
                $type = $rules['type'] ?? '';
 | 
			
		||||
                if (!str_starts_with($type, '_') && !str_contains($key, '*')) {
 | 
			
		||||
                $ignore = (bool) array_filter((array)($rules['validate']['ignore'] ?? [])) ?? false;
 | 
			
		||||
                if (!str_starts_with($type, '_') && !str_contains($key, '*') && $ignore !== true) {
 | 
			
		||||
                    $list[$prefix . $key] = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -631,6 +631,10 @@ class Validation
 | 
			
		||||
     */
 | 
			
		||||
    public static function typeEmail($value, array $params, array $field)
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($value)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!isset($params['max'])) {
 | 
			
		||||
            $params['max'] = 320;
 | 
			
		||||
        }
 | 
			
		||||
@@ -638,7 +642,7 @@ class Validation
 | 
			
		||||
        $values = !is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
 | 
			
		||||
 | 
			
		||||
        foreach ($values as $val) {
 | 
			
		||||
            if (!(self::typeText($val, $params, $field) && filter_var($val, FILTER_VALIDATE_EMAIL))) {
 | 
			
		||||
            if (!(self::typeText($val, $params, $field) && strpos($val, '@', 1))) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ class SimplePageHandler extends Handler
 | 
			
		||||
        $vars = array(
 | 
			
		||||
            'stylesheet' => file_get_contents($cssFile),
 | 
			
		||||
            'code'        => $code,
 | 
			
		||||
            'message'     => filter_var(rawurldecode($message), FILTER_SANITIZE_STRING),
 | 
			
		||||
            'message'     => htmlspecialchars(strip_tags(rawurldecode($message)), ENT_QUOTES, 'UTF-8'),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $helper->setVariables($vars);
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ declare(strict_types=1);
 | 
			
		||||
namespace Grav\Common\Flex\Types\UserGroups;
 | 
			
		||||
 | 
			
		||||
use Grav\Common\Flex\FlexObject;
 | 
			
		||||
use Grav\Common\Grav;
 | 
			
		||||
use Grav\Common\User\Access;
 | 
			
		||||
use Grav\Common\User\Interfaces\UserGroupInterface;
 | 
			
		||||
use function is_bool;
 | 
			
		||||
@@ -74,6 +75,18 @@ class UserGroupObject extends FlexObject implements UserGroupInterface
 | 
			
		||||
        return $access->authorize('admin.super') ? true : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function groupNames(): array
 | 
			
		||||
    {
 | 
			
		||||
        $groups = [];
 | 
			
		||||
        $user_groups = Grav::instance()['user_groups'];
 | 
			
		||||
 | 
			
		||||
        foreach ($user_groups as $key => $group) {
 | 
			
		||||
            $groups[$key] = $group->readableName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $groups;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Access
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ use function is_string;
 | 
			
		||||
class LogViewer
 | 
			
		||||
{
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $pattern = '/\[(?P<date>.*)\] (?P<logger>\w+).(?P<level>\w+): (?P<message>.*[^ ]+) (?P<context>[^ ]+) (?P<extra>[^ ]+)/';
 | 
			
		||||
    protected $pattern = '/\[(?P<date>.*?)\] (?P<logger>\w+)\.(?P<level>\w+): (?P<message>.*[^ ]+) (?P<context>[^ ]+) (?P<extra>[^ ]+)/';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the objects of a tailed file
 | 
			
		||||
 
 | 
			
		||||
@@ -144,7 +144,7 @@ class Truncator
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Transform multibyte entities which otherwise display incorrectly.
 | 
			
		||||
        $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
 | 
			
		||||
        $html = mb_encode_numericentity($html, [0x80, 0x10FFFF, 0, ~0], 'UTF-8');
 | 
			
		||||
 | 
			
		||||
        // Internal errors enabled as HTML5 not fully supported.
 | 
			
		||||
        libxml_use_internal_errors(true);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ use Grav\Common\Page\Markdown\Excerpts;
 | 
			
		||||
 */
 | 
			
		||||
class Parsedown extends \Parsedown
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    use ParsedownGravTrait;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ trait ParsedownGravTrait
 | 
			
		||||
    public $completable_blocks = [];
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    public $continuable_blocks = [];
 | 
			
		||||
    public $plugins = [];
 | 
			
		||||
 | 
			
		||||
    /** @var Excerpts */
 | 
			
		||||
    protected $excerpts;
 | 
			
		||||
@@ -292,7 +293,12 @@ trait ParsedownGravTrait
 | 
			
		||||
    #[\ReturnTypeWillChange]
 | 
			
		||||
    public function __call($method, $args)
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->{$method}) === true) {
 | 
			
		||||
 | 
			
		||||
        if (isset($this->plugins[$method]) === true) {
 | 
			
		||||
            $func = $this->plugins[$method];
 | 
			
		||||
 | 
			
		||||
            return call_user_func_array($func, $args);
 | 
			
		||||
        } elseif (isset($this->{$method}) === true) {
 | 
			
		||||
            $func = $this->{$method};
 | 
			
		||||
 | 
			
		||||
            return call_user_func_array($func, $args);
 | 
			
		||||
@@ -300,4 +306,14 @@ trait ParsedownGravTrait
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __set($name, $value)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_callable($value)) {
 | 
			
		||||
            $this->plugins[$name] = $value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -62,8 +62,8 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
 | 
			
		||||
        if (!($this->offsetExists('width') && $this->offsetExists('height') && $this->offsetExists('mime'))) {
 | 
			
		||||
            $image_info = getimagesize($path);
 | 
			
		||||
            if ($image_info) {
 | 
			
		||||
                $this->def('width', $image_info[0]);
 | 
			
		||||
                $this->def('height', $image_info[1]);
 | 
			
		||||
                $this->def('width', (int) $image_info[0]);
 | 
			
		||||
                $this->def('height', (int) $image_info[1]);
 | 
			
		||||
                $this->def('mime', $image_info['mime']);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -299,7 +299,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($width && $height) {
 | 
			
		||||
            $this->__call('cropResize', [$width, $height]);
 | 
			
		||||
            $this->__call('cropResize', [(int) $width, (int) $height]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return parent::lightbox($width, $height, $reset);
 | 
			
		||||
@@ -361,8 +361,8 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
 | 
			
		||||
 | 
			
		||||
        // Scaling operations
 | 
			
		||||
        $scale     = ($scale ?? $config->get('system.images.watermark.scale', 100)) / 100;
 | 
			
		||||
        $wwidth    = (int)$this->get('width')  * $scale;
 | 
			
		||||
        $wheight   = (int)$this->get('height') * $scale;
 | 
			
		||||
        $wwidth    = (int) ($this->get('width')  * $scale);
 | 
			
		||||
        $wheight   = (int) ($this->get('height') * $scale);
 | 
			
		||||
        $watermark->resize($wwidth, $wheight);
 | 
			
		||||
 | 
			
		||||
        // Position operations
 | 
			
		||||
@@ -392,11 +392,11 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'right':
 | 
			
		||||
                $positionX = (int)$this->get('width')-$wwidth;
 | 
			
		||||
                $positionX = (int) ($this->get('width')-$wwidth);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'center':
 | 
			
		||||
                $positionX = ((int)$this->get('width')/2) - ($wwidth/2);
 | 
			
		||||
                $positionX = (int) (($this->get('width')/2) - ($wwidth/2));
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -431,8 +431,8 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
 | 
			
		||||
        return $this;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $dst_width = $image->width()+2*$border;
 | 
			
		||||
      $dst_height = $image->height()+2*$border;
 | 
			
		||||
      $dst_width = (int) ($image->width()+2*$border);
 | 
			
		||||
      $dst_height = (int) ($image->height()+2*$border);
 | 
			
		||||
 | 
			
		||||
      $frame = ImageFile::create($dst_width, $dst_height);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1270,9 +1270,14 @@ class Page implements PageInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function blueprintName()
 | 
			
		||||
    {
 | 
			
		||||
        $blueprint_name = filter_input(INPUT_POST, 'blueprint', FILTER_SANITIZE_STRING) ?: $this->template();
 | 
			
		||||
        if (!isset($_POST['blueprint'])) {
 | 
			
		||||
            return $this->template();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $blueprint_name;
 | 
			
		||||
        $post_value = $_POST['blueprint'];
 | 
			
		||||
        $sanitized_value = htmlspecialchars(strip_tags($post_value), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
 | 
			
		||||
        return $sanitized_value ?: $this->template();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1802,7 +1807,7 @@ class Page implements PageInterface
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (empty($this->slug)) {
 | 
			
		||||
            $this->slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder)) ?: null;
 | 
			
		||||
            $this->slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', (string) $this->folder)) ?: null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->slug;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ use Grav\Common\Page\Interfaces\PageInterface;
 | 
			
		||||
use Grav\Common\Taxonomy;
 | 
			
		||||
use Grav\Common\Uri;
 | 
			
		||||
use Grav\Common\Utils;
 | 
			
		||||
use Grav\Events\TypesEvent;
 | 
			
		||||
use Grav\Framework\Flex\Flex;
 | 
			
		||||
use Grav\Framework\Flex\FlexDirectory;
 | 
			
		||||
use Grav\Framework\Flex\Interfaces\FlexTranslateInterface;
 | 
			
		||||
@@ -1289,7 +1290,7 @@ class Pages
 | 
			
		||||
 | 
			
		||||
            $scanBlueprintsAndTemplates = static function (Types $types) use ($grav) {
 | 
			
		||||
                // Scan blueprints
 | 
			
		||||
                $event = new Event();
 | 
			
		||||
                $event = new TypesEvent();
 | 
			
		||||
                $event->types = $types;
 | 
			
		||||
                $grav->fireEvent('onGetPageBlueprints', $event);
 | 
			
		||||
 | 
			
		||||
@@ -1303,7 +1304,7 @@ class Pages
 | 
			
		||||
                $types->scanBlueprints($lookup);
 | 
			
		||||
 | 
			
		||||
                // Scan templates
 | 
			
		||||
                $event = new Event();
 | 
			
		||||
                $event = new TypesEvent();
 | 
			
		||||
                $event->types = $types;
 | 
			
		||||
                $grav->fireEvent('onGetPageTemplates', $event);
 | 
			
		||||
 | 
			
		||||
@@ -1773,7 +1774,7 @@ class Pages
 | 
			
		||||
        $dirs = (array) $grav['config']->get('system.pages.dirs', ['page://']);
 | 
			
		||||
        foreach ($dirs as $dir) {
 | 
			
		||||
            $path = $locator->findResource($dir);
 | 
			
		||||
            if (file_exists($path)) {
 | 
			
		||||
            if (file_exists($path) && !in_array($path, $paths, true)) {
 | 
			
		||||
                $paths[] = $path;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
namespace Grav\Common\Processors;
 | 
			
		||||
 | 
			
		||||
use Grav\Common\Page\Interfaces\PageInterface;
 | 
			
		||||
use Grav\Events\PageEvent;
 | 
			
		||||
use Grav\Framework\RequestHandler\Exception\RequestException;
 | 
			
		||||
use Grav\Plugin\Form\Forms;
 | 
			
		||||
use RocketTheme\Toolbox\Event\Event;
 | 
			
		||||
@@ -66,7 +67,7 @@ class PagesProcessor extends ProcessorBase
 | 
			
		||||
        if (!$page->routable()) {
 | 
			
		||||
            $exception = new RequestException($request, 'Page Not Found', 404);
 | 
			
		||||
            // If no page found, fire event
 | 
			
		||||
            $event = new Event([
 | 
			
		||||
            $event = new PageEvent([
 | 
			
		||||
                'page' => $page,
 | 
			
		||||
                'code' => $exception->getCode(),
 | 
			
		||||
                'message' => $exception->getMessage(),
 | 
			
		||||
 
 | 
			
		||||
@@ -357,7 +357,7 @@ class Scheduler
 | 
			
		||||
     */
 | 
			
		||||
    public function whoami()
 | 
			
		||||
    {
 | 
			
		||||
        $process = new Process('whoami');
 | 
			
		||||
        $process = new Process(['whoami']);
 | 
			
		||||
        $process->run();
 | 
			
		||||
 | 
			
		||||
        if ($process->isSuccessful()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ class PagesServiceProvider implements ServiceProviderInterface
 | 
			
		||||
            /** @var Uri $uri */
 | 
			
		||||
            $uri = $grav['uri'];
 | 
			
		||||
 | 
			
		||||
            $path = $uri->path() ?: '/'; // Don't trim to support trailing slash default routes
 | 
			
		||||
            $path = $uri->path() ? urldecode($uri->path()) : '/'; // Don't trim to support trailing slash default routes
 | 
			
		||||
            $page = $pages->dispatch($path);
 | 
			
		||||
 | 
			
		||||
            // Redirection tests
 | 
			
		||||
@@ -72,7 +72,7 @@ class PagesServiceProvider implements ServiceProviderInterface
 | 
			
		||||
                if ($config->get('system.force_ssl')) {
 | 
			
		||||
                    $scheme = $uri->scheme(true);
 | 
			
		||||
                    if ($scheme !== 'https') {
 | 
			
		||||
                        $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 | 
			
		||||
                        $url = 'https://' . $uri->host() . $uri->uri();
 | 
			
		||||
                        $grav->redirect($url);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ class TaskServiceProvider implements ServiceProviderInterface
 | 
			
		||||
 | 
			
		||||
            $task = $body['task'] ?? $c['uri']->param('task');
 | 
			
		||||
            if (null !== $task) {
 | 
			
		||||
                $task = filter_var($task, FILTER_SANITIZE_STRING);
 | 
			
		||||
                $task = htmlspecialchars(strip_tags($task), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $task ?: null;
 | 
			
		||||
@@ -46,7 +46,7 @@ class TaskServiceProvider implements ServiceProviderInterface
 | 
			
		||||
 | 
			
		||||
            $action = $body['action'] ?? $c['uri']->param('action');
 | 
			
		||||
            if (null !== $action) {
 | 
			
		||||
                $action = filter_var($action, FILTER_SANITIZE_STRING);
 | 
			
		||||
                $action = htmlspecialchars(strip_tags($action), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $action ?: null;
 | 
			
		||||
 
 | 
			
		||||
@@ -122,10 +122,10 @@ class Session extends \Grav\Framework\Session\Session
 | 
			
		||||
 | 
			
		||||
            // Make sure that Forms 3.0+ has been installed.
 | 
			
		||||
            if (null === $object && isset($grav['forms'])) {
 | 
			
		||||
                user_error(
 | 
			
		||||
                    __CLASS__ . '::' . __FUNCTION__ . '(\'files-upload\') is deprecated since Grav 1.6, use $form->getFlash()->getLegacyFiles() instead',
 | 
			
		||||
                    E_USER_DEPRECATED
 | 
			
		||||
                );
 | 
			
		||||
//                user_error(
 | 
			
		||||
//                    __CLASS__ . '::' . __FUNCTION__ . '(\'files-upload\') is deprecated since Grav 1.6, use $form->getFlash()->getLegacyFiles() instead',
 | 
			
		||||
//                    E_USER_DEPRECATED
 | 
			
		||||
//                );
 | 
			
		||||
 | 
			
		||||
                /** @var Uri $uri */
 | 
			
		||||
                $uri = $grav['uri'];
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ use Twig\Error\RuntimeError;
 | 
			
		||||
use Twig\Extension\AbstractExtension;
 | 
			
		||||
use Twig\Extension\GlobalsInterface;
 | 
			
		||||
use Twig\Loader\FilesystemLoader;
 | 
			
		||||
use Twig\Markup;
 | 
			
		||||
use Twig\TwigFilter;
 | 
			
		||||
use Twig\TwigFunction;
 | 
			
		||||
use function array_slice;
 | 
			
		||||
@@ -170,8 +171,10 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
            new TwigFilter('count', 'count'),
 | 
			
		||||
            new TwigFilter('array_diff', 'array_diff'),
 | 
			
		||||
 | 
			
		||||
            // Security fix
 | 
			
		||||
            new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]),
 | 
			
		||||
            // Security fixes
 | 
			
		||||
            new TwigFilter('filter', [$this, 'filterFunc'], ['needs_environment' => true]),
 | 
			
		||||
            new TwigFilter('map', [$this, 'mapFunc'], ['needs_environment' => true]),
 | 
			
		||||
            new TwigFilter('reduce', [$this, 'reduceFunc'], ['needs_environment' => true]),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -247,6 +250,12 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
            new TwigFunction('is_object', 'is_object'),
 | 
			
		||||
            new TwigFunction('count', 'count'),
 | 
			
		||||
            new TwigFunction('array_diff', 'array_diff'),
 | 
			
		||||
            new TwigFunction('parse_url', 'parse_url'),
 | 
			
		||||
 | 
			
		||||
            // Security fixes
 | 
			
		||||
            new TwigFunction('filter', [$this, 'filterFunc'], ['needs_environment' => true]),
 | 
			
		||||
            new TwigFunction('map', [$this, 'mapFunc'], ['needs_environment' => true]),
 | 
			
		||||
            new TwigFunction('reduce', [$this, 'reduceFunc'], ['needs_environment' => true]),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -468,7 +477,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function base64EncodeFilter($str)
 | 
			
		||||
    {
 | 
			
		||||
        return base64_encode($str);
 | 
			
		||||
        return base64_encode((string) $str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -904,8 +913,13 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
            return $this->grav['admin']->translate($args, $lang);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // else use the default grav translate functionality
 | 
			
		||||
        return $this->grav['language']->translate($args);
 | 
			
		||||
        $translation = $this->grav['language']->translate($args);
 | 
			
		||||
 | 
			
		||||
        if ($this->config->get('system.languages.debug', false)) {
 | 
			
		||||
            return new Markup("<span class=\"translate-debug\" data-toggle=\"tooltip\" title=\"" . $args[0] . "\">$translation</span>", 'UTF-8');
 | 
			
		||||
        } else {
 | 
			
		||||
            return $translation;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -949,7 +963,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function repeatFunc($input, $multiplier)
 | 
			
		||||
    {
 | 
			
		||||
        return str_repeat($input, $multiplier);
 | 
			
		||||
        return str_repeat($input, (int) $multiplier);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1203,6 +1217,9 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function jsonDecodeFilter($str, $assoc = false, $depth = 512, $options = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if ($str === null) {
 | 
			
		||||
            $str = '';
 | 
			
		||||
        }
 | 
			
		||||
        return json_decode(html_entity_decode($str, ENT_COMPAT | ENT_HTML401, 'UTF-8'), $assoc, $depth, $options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1214,7 +1231,13 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function getCookie($key)
 | 
			
		||||
    {
 | 
			
		||||
        return filter_input(INPUT_COOKIE, $key, FILTER_SANITIZE_STRING);
 | 
			
		||||
        $cookie_value = filter_input(INPUT_COOKIE, $key);
 | 
			
		||||
 | 
			
		||||
        if ($cookie_value === null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return htmlspecialchars(strip_tags($cookie_value), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1689,12 +1712,44 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 | 
			
		||||
     * @return array|CallbackFilterIterator
 | 
			
		||||
     * @throws RuntimeError
 | 
			
		||||
     */
 | 
			
		||||
    function filterFilter(Environment $env, $array, $arrow)
 | 
			
		||||
    function filterFunc(Environment $env, $array, $arrow)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($arrow) && Utils::isDangerousFunction($arrow)) {
 | 
			
		||||
        if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
 | 
			
		||||
            throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return twig_array_filter($env, $array, $arrow);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Environment $env
 | 
			
		||||
     * @param array $array
 | 
			
		||||
     * @param callable|string $arrow
 | 
			
		||||
     * @return array|CallbackFilterIterator
 | 
			
		||||
     * @throws RuntimeError
 | 
			
		||||
     */
 | 
			
		||||
    function mapFunc(Environment $env, $array, $arrow)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
 | 
			
		||||
            throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return twig_array_map($env, $array, $arrow);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Environment $env
 | 
			
		||||
     * @param array $array
 | 
			
		||||
     * @param callable|string $arrow
 | 
			
		||||
     * @return array|CallbackFilterIterator
 | 
			
		||||
     * @throws RuntimeError
 | 
			
		||||
     */
 | 
			
		||||
    function reduceFunc(Environment $env, $array, $arrow)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
 | 
			
		||||
            throw new RuntimeError('Twig |reduce("' . $arrow . '") is not allowed.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return twig_array_map($env, $array, $arrow);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,15 @@
 | 
			
		||||
namespace Grav\Common\Twig\Node;
 | 
			
		||||
 | 
			
		||||
use Twig\Compiler;
 | 
			
		||||
use Twig\Node\Expression\AbstractExpression;
 | 
			
		||||
use Twig\Node\Node;
 | 
			
		||||
use Twig\Node\NodeOutputInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class TwigNodeCache
 | 
			
		||||
 * @package Grav\Common\Twig\Node
 | 
			
		||||
 */
 | 
			
		||||
class TwigNodeCache extends Node
 | 
			
		||||
class TwigNodeCache extends Node implements NodeOutputInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string    $key       unique name for key
 | 
			
		||||
@@ -25,25 +27,58 @@ class TwigNodeCache extends Node
 | 
			
		||||
     * @param integer   $lineno
 | 
			
		||||
     * @param string|null $tag
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(string $key, int $lifetime, Node $body, $lineno, $tag = null)
 | 
			
		||||
    public function __construct(Node $body, ?AbstractExpression $key, ?AbstractExpression $lifetime, array $defaults, int $lineno, string $tag)
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct(array('body' => $body), array( 'key' => $key, 'lifetime' => $lifetime), $lineno, $tag);
 | 
			
		||||
        $nodes = ['body' => $body];
 | 
			
		||||
 | 
			
		||||
        if ($key !== null) {
 | 
			
		||||
            $nodes['key'] = $key;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($lifetime !== null) {
 | 
			
		||||
            $nodes['lifetime'] = $lifetime;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        parent::__construct($nodes, $defaults, $lineno, $tag);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function compile(Compiler $compiler): void
 | 
			
		||||
    {
 | 
			
		||||
        $boo = $this->getAttribute('key');
 | 
			
		||||
        $compiler->addDebugInfo($this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Generate the cache key
 | 
			
		||||
        if ($this->hasNode('key')) {
 | 
			
		||||
            $compiler
 | 
			
		||||
                ->write('$key = "twigcache-" . ')
 | 
			
		||||
                ->subcompile($this->getNode('key'))
 | 
			
		||||
                ->raw(";\n");
 | 
			
		||||
        } else {
 | 
			
		||||
            $compiler
 | 
			
		||||
                ->write('$key = ')
 | 
			
		||||
                ->string($this->getAttribute('key'))
 | 
			
		||||
                ->raw(";\n");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set the cache timeout
 | 
			
		||||
        if ($this->hasNode('lifetime')) {
 | 
			
		||||
            $compiler
 | 
			
		||||
                ->write('$lifetime = ')
 | 
			
		||||
                ->subcompile($this->getNode('lifetime'))
 | 
			
		||||
                ->raw(";\n");
 | 
			
		||||
        } else {
 | 
			
		||||
            $compiler
 | 
			
		||||
                ->write('$lifetime = ')
 | 
			
		||||
                ->write($this->getAttribute('lifetime'))
 | 
			
		||||
                ->raw(";\n");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $compiler
 | 
			
		||||
            ->addDebugInfo($this)
 | 
			
		||||
            ->write("\$cache = \\Grav\\Common\\Grav::instance()['cache'];\n")
 | 
			
		||||
            ->write("\$key = \"twigcache-\" . \"" . $this->getAttribute('key') . "\";\n")
 | 
			
		||||
            ->write("\$lifetime = " . $this->getAttribute('lifetime') . ";\n")
 | 
			
		||||
            ->write("\$cache_body = \$cache->fetch(\$key);\n")
 | 
			
		||||
            ->write("if (\$cache_body === false) {\n")
 | 
			
		||||
            ->indent()
 | 
			
		||||
                ->write("\\Grav\\Common\\Grav::instance()['debugger']->addMessage(\"Cache Key: \$key, Lifetime: \$lifetime\");\n")
 | 
			
		||||
                ->write("ob_start();\n")
 | 
			
		||||
                    ->indent()
 | 
			
		||||
                        ->subcompile($this->getNode('body'))
 | 
			
		||||
@@ -53,6 +88,6 @@ class TwigNodeCache extends Node
 | 
			
		||||
                ->write("\$cache->save(\$key, \$cache_body, \$lifetime);\n")
 | 
			
		||||
            ->outdent()
 | 
			
		||||
            ->write("}\n")
 | 
			
		||||
            ->write("echo \$cache_body;\n");
 | 
			
		||||
            ->write("echo '' === \$cache_body ? '' : new Markup(\$cache_body, \$this->env->getCharset());\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,6 @@ namespace Grav\Common\Twig\TokenParser;
 | 
			
		||||
 | 
			
		||||
use Grav\Common\Grav;
 | 
			
		||||
use Grav\Common\Twig\Node\TwigNodeCache;
 | 
			
		||||
use Twig\Error\SyntaxError;
 | 
			
		||||
use Twig\Token;
 | 
			
		||||
use Twig\TokenParser\AbstractTokenParser;
 | 
			
		||||
 | 
			
		||||
@@ -22,50 +21,54 @@ use Twig\TokenParser\AbstractTokenParser;
 | 
			
		||||
 * {{ some_complex_work() }}
 | 
			
		||||
 * {% endcache %}
 | 
			
		||||
 *
 | 
			
		||||
 * Where the `600` is an optional lifetime in seconds
 | 
			
		||||
 * Also can provide a unique key for the cache:
 | 
			
		||||
 *
 | 
			
		||||
 * {% cache "prefix-"~lang 600 %}
 | 
			
		||||
 *
 | 
			
		||||
 * Where the "prefix-"~lang will use a unique key based on the current language. "prefix-en" for example
 | 
			
		||||
 */
 | 
			
		||||
class TwigTokenParserCache extends AbstractTokenParser
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Token $token
 | 
			
		||||
     * @return TwigNodeCache
 | 
			
		||||
     * @throws SyntaxError
 | 
			
		||||
     */
 | 
			
		||||
    public function parse(Token $token)
 | 
			
		||||
    {
 | 
			
		||||
        $lineno = $token->getLine();
 | 
			
		||||
        $stream = $this->parser->getStream();
 | 
			
		||||
        $key = $this->parser->getVarName() . $lineno;
 | 
			
		||||
        $lifetime = Grav::instance()['cache']->getLifetime();
 | 
			
		||||
        $lineno = $token->getLine();
 | 
			
		||||
 | 
			
		||||
        // Check for optional lifetime override
 | 
			
		||||
        if (!$stream->test(Token::BLOCK_END_TYPE)) {
 | 
			
		||||
            $lifetime_expr = $this->parser->getExpressionParser()->parseExpression();
 | 
			
		||||
            $lifetime = $lifetime_expr->getAttribute('value');
 | 
			
		||||
        // Parse the optional key and timeout parameters
 | 
			
		||||
        $defaults = [
 | 
			
		||||
            'key' => $this->parser->getVarName() . $lineno,
 | 
			
		||||
            'lifetime' => Grav::instance()['cache']->getLifetime()
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $key = null;
 | 
			
		||||
        $lifetime = null;
 | 
			
		||||
        while (!$stream->test(Token::BLOCK_END_TYPE)) {
 | 
			
		||||
            if ($stream->test(Token::STRING_TYPE)) {
 | 
			
		||||
                $key = $this->parser->getExpressionParser()->parseExpression();
 | 
			
		||||
            } elseif ($stream->test(Token::NUMBER_TYPE)) {
 | 
			
		||||
                $lifetime = $this->parser->getExpressionParser()->parseExpression();
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new \Twig\Error\SyntaxError("Unexpected token type in cache tag.", $token->getLine(), $stream->getSourceContext());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $stream->expect(Token::BLOCK_END_TYPE);
 | 
			
		||||
        $body = $this->parser->subparse(array($this, 'decideCacheEnd'), true);
 | 
			
		||||
 | 
			
		||||
        // Parse the content inside the cache block
 | 
			
		||||
        $body = $this->parser->subparse([$this, 'decideCacheEnd'], true);
 | 
			
		||||
 | 
			
		||||
        $stream->expect(Token::BLOCK_END_TYPE);
 | 
			
		||||
 | 
			
		||||
        return new TwigNodeCache($key, $lifetime, $body, $lineno, $this->getTag());
 | 
			
		||||
        return new TwigNodeCache($body, $key, $lifetime, $defaults, $lineno, $this->getTag());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decide if current token marks end of cache block.
 | 
			
		||||
     *
 | 
			
		||||
     * @param Token $token
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    public function decideCacheEnd(Token $token): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $token->test('endcache');
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    public function getTag(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'cache';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -57,6 +57,15 @@ class Twig
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    public $template;
 | 
			
		||||
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    public $plugins_hooked_nav = [];
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    public $plugins_quick_tray = [];
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    public $plugins_hooked_dashboard_widgets_top = [];
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    public $plugins_hooked_dashboard_widgets_main = [];
 | 
			
		||||
 | 
			
		||||
    /** @var Grav */
 | 
			
		||||
    protected $grav;
 | 
			
		||||
    /** @var FilesystemLoader */
 | 
			
		||||
@@ -493,13 +502,19 @@ class Twig
 | 
			
		||||
    /**
 | 
			
		||||
     * Simple helper method to get the twig template if it has already been set, else return
 | 
			
		||||
     * the one being passed in
 | 
			
		||||
     * NOTE: Modular pages that are injected should not use this pre-set template as it's usually set at the page level
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string $template the template name
 | 
			
		||||
     * @return string           the template name
 | 
			
		||||
     */
 | 
			
		||||
    public function template($template)
 | 
			
		||||
    public function template(string $template): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->template ?? $template;
 | 
			
		||||
        if (isset($this->template)) {
 | 
			
		||||
            $template = $this->template;
 | 
			
		||||
            unset($this->template);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $template;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -513,7 +528,7 @@ class Twig
 | 
			
		||||
        $default = $page->isModule() ? 'modular/default' : 'default';
 | 
			
		||||
        $extension = $format ?: $page->templateFormat();
 | 
			
		||||
        $twig_extension = $extension ? '.'. $extension .TWIG_EXT : TEMPLATE_EXT;
 | 
			
		||||
        $template_file = $this->template($page->template() . $twig_extension);
 | 
			
		||||
        $template_file = $this->template($template . $twig_extension);
 | 
			
		||||
 | 
			
		||||
        // TODO: no longer needed in Twig 3.
 | 
			
		||||
        /** @var ExistsLoaderInterface $loader */
 | 
			
		||||
 
 | 
			
		||||
@@ -1005,7 +1005,7 @@ class Uri
 | 
			
		||||
            foreach ($matches as $match) {
 | 
			
		||||
                $param = explode($delimiter, $match[1]);
 | 
			
		||||
                if (count($param) === 2) {
 | 
			
		||||
                    $plain_var = filter_var(rawurldecode($param[1]), FILTER_SANITIZE_STRING);
 | 
			
		||||
                    $plain_var = htmlspecialchars(strip_tags(rawurldecode($param[1])), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
                    $params[$param[0]] = $plain_var;
 | 
			
		||||
                    $uri = str_replace($match[0], '', $uri);
 | 
			
		||||
                }
 | 
			
		||||
@@ -1388,7 +1388,11 @@ class Uri
 | 
			
		||||
        if ($this->post && null !== $element) {
 | 
			
		||||
            $item = Utils::getDotNotation($this->post, $element);
 | 
			
		||||
            if ($filter_type) {
 | 
			
		||||
                $item = filter_var($item, $filter_type);
 | 
			
		||||
                if ($filter_type === FILTER_SANITIZE_STRING || $filter_type === GRAV_SANITIZE_STRING) {
 | 
			
		||||
                    $item = htmlspecialchars(strip_tags($item), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
                } else {
 | 
			
		||||
                    $item = filter_var($item, $filter_type);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return $item;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1514,7 +1518,7 @@ class Uri
 | 
			
		||||
            foreach ($matches as $match) {
 | 
			
		||||
                $param = explode($delimiter, $match[1]);
 | 
			
		||||
                if (count($param) === 2) {
 | 
			
		||||
                    $plain_var = filter_var($param[1], FILTER_SANITIZE_STRING);
 | 
			
		||||
                    $plain_var = htmlspecialchars(strip_tags($param[1]), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
                    $this->params[$param[0]] = $plain_var;
 | 
			
		||||
                    $uri = str_replace($match[0], '', $uri);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,7 @@ abstract class Utils
 | 
			
		||||
        $compare_func = $case_sensitive ? 'mb_strpos' : 'mb_stripos';
 | 
			
		||||
 | 
			
		||||
        foreach ((array)$needle as $each_needle) {
 | 
			
		||||
            $status = $each_needle === '' || $compare_func($haystack, $each_needle) === 0;
 | 
			
		||||
            $status = $each_needle === '' || $compare_func((string) $haystack, $each_needle) === 0;
 | 
			
		||||
            if ($status) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -225,8 +225,8 @@ abstract class Utils
 | 
			
		||||
        $compare_func = $case_sensitive ? 'mb_strrpos' : 'mb_strripos';
 | 
			
		||||
 | 
			
		||||
        foreach ((array)$needle as $each_needle) {
 | 
			
		||||
            $expectedPosition = mb_strlen($haystack) - mb_strlen($each_needle);
 | 
			
		||||
            $status = $each_needle === '' || $compare_func($haystack, $each_needle, 0) === $expectedPosition;
 | 
			
		||||
            $expectedPosition = mb_strlen((string) $haystack) - mb_strlen($each_needle);
 | 
			
		||||
            $status = $each_needle === '' || $compare_func((string) $haystack, $each_needle, 0) === $expectedPosition;
 | 
			
		||||
            if ($status) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -250,7 +250,7 @@ abstract class Utils
 | 
			
		||||
        $compare_func = $case_sensitive ? 'mb_strpos' : 'mb_stripos';
 | 
			
		||||
 | 
			
		||||
        foreach ((array)$needle as $each_needle) {
 | 
			
		||||
            $status = $each_needle === '' || $compare_func($haystack, $each_needle) !== false;
 | 
			
		||||
            $status = $each_needle === '' || $compare_func((string) $haystack, $each_needle) !== false;
 | 
			
		||||
            if ($status) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1145,9 +1145,9 @@ abstract class Utils
 | 
			
		||||
            $offset_prefix = $offset < 0 ? '-' : '+';
 | 
			
		||||
            $offset_formatted = gmdate('H:i', abs($offset));
 | 
			
		||||
 | 
			
		||||
            $pretty_offset = "UTC${offset_prefix}${offset_formatted}";
 | 
			
		||||
            $pretty_offset = "UTC{$offset_prefix}{$offset_formatted}";
 | 
			
		||||
 | 
			
		||||
            $timezone_list[$timezone] = "(${pretty_offset}) " . str_replace('_', ' ', $timezone);
 | 
			
		||||
            $timezone_list[$timezone] = "({$pretty_offset}) " . str_replace('_', ' ', $timezone);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $timezone_list;
 | 
			
		||||
@@ -1874,9 +1874,9 @@ abstract class Utils
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($block) {
 | 
			
		||||
            $string = $parsedown->text($string);
 | 
			
		||||
            $string = $parsedown->text((string) $string);
 | 
			
		||||
        } else {
 | 
			
		||||
            $string = $parsedown->line($string);
 | 
			
		||||
            $string = $parsedown->line((string) $string);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $string;
 | 
			
		||||
@@ -1950,10 +1950,10 @@ abstract class Utils
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $name
 | 
			
		||||
     * @param string|array|Closure $name
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    public static function isDangerousFunction(string $name): bool
 | 
			
		||||
    public static function isDangerousFunction($name): bool
 | 
			
		||||
    {
 | 
			
		||||
        static $commandExecutionFunctions = [
 | 
			
		||||
            'exec',
 | 
			
		||||
@@ -2048,8 +2048,30 @@ abstract class Utils
 | 
			
		||||
            'posix_setpgid',
 | 
			
		||||
            'posix_setsid',
 | 
			
		||||
            'posix_setuid',
 | 
			
		||||
            'unserialize',
 | 
			
		||||
            'ini_alter',
 | 
			
		||||
            'simplexml_load_file',
 | 
			
		||||
            'simplexml_load_string',
 | 
			
		||||
            'forward_static_call',
 | 
			
		||||
            'forward_static_call_array',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if (is_string($name)) {
 | 
			
		||||
            $name = strtolower($name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($name instanceof \Closure) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (is_array($name) || strpos($name, ":") !== false) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strpos($name, "\\") !== false) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($name, $commandExecutionFunctions)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ class LogViewerCommand extends GravCommand
 | 
			
		||||
                if ($log['trace'] && $verbose) {
 | 
			
		||||
                    $output .= " <white>{$log['message']}</white>\n";
 | 
			
		||||
                    foreach ((array) $log['trace'] as $index => $tracerow) {
 | 
			
		||||
                        $output .= "<white>{$index}</white>${tracerow}\n";
 | 
			
		||||
                        $output .= "<white>{$index}</white>{$tracerow}\n";
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $output .= " {$log['message']}";
 | 
			
		||||
 
 | 
			
		||||
@@ -317,7 +317,7 @@ class InstallCommand extends GpmCommand
 | 
			
		||||
                $questionNoun = 'packages';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $question = new ConfirmationQuestion("${questionAction} {$questionArticle} {$questionNoun}? [Y|n] ", true);
 | 
			
		||||
            $question = new ConfirmationQuestion("{$questionAction} {$questionArticle} {$questionNoun}? [Y|n] ", true);
 | 
			
		||||
            $answer = $this->all_yes ? true : $io->askQuestion($question);
 | 
			
		||||
 | 
			
		||||
            if ($answer) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								system/src/Grav/Events/PageEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								system/src/Grav/Events/PageEvent.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @package    Grav\Events
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright  Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
 | 
			
		||||
 * @license    MIT License; see LICENSE file for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Grav\Events;
 | 
			
		||||
 | 
			
		||||
use Grav\Framework\Flex\Flex;
 | 
			
		||||
use RocketTheme\Toolbox\Event\Event;
 | 
			
		||||
 | 
			
		||||
class PageEvent extends Event
 | 
			
		||||
{
 | 
			
		||||
    public $page;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								system/src/Grav/Events/TypesEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								system/src/Grav/Events/TypesEvent.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @package    Grav\Events
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright  Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
 | 
			
		||||
 * @license    MIT License; see LICENSE file for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Grav\Events;
 | 
			
		||||
 | 
			
		||||
use Grav\Framework\Flex\Flex;
 | 
			
		||||
use RocketTheme\Toolbox\Event\Event;
 | 
			
		||||
 | 
			
		||||
class TypesEvent extends Event
 | 
			
		||||
{
 | 
			
		||||
    public $types;
 | 
			
		||||
}
 | 
			
		||||
@@ -23,6 +23,8 @@ class RecursiveActionIterator implements RecursiveIterator, \Countable
 | 
			
		||||
{
 | 
			
		||||
    use Constructor, Iterator, Countable;
 | 
			
		||||
 | 
			
		||||
    public $items;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @see \Iterator::key()
 | 
			
		||||
     * @return string
 | 
			
		||||
 
 | 
			
		||||
@@ -366,9 +366,14 @@ trait PageLegacyTrait
 | 
			
		||||
     */
 | 
			
		||||
    public function blueprintName(): string
 | 
			
		||||
    {
 | 
			
		||||
        $blueprint_name = filter_input(INPUT_POST, 'blueprint', FILTER_SANITIZE_STRING) ?: $this->template();
 | 
			
		||||
        if (!isset($_POST['blueprint'])) {
 | 
			
		||||
            return $this->template();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $blueprint_name;
 | 
			
		||||
        $post_value = $_POST['blueprint'];
 | 
			
		||||
        $sanitized_value = htmlspecialchars(strip_tags($post_value), ENT_QUOTES, 'UTF-8');
 | 
			
		||||
 | 
			
		||||
        return $sanitized_value ?: $this->template();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ trait NestedPropertyTrait
 | 
			
		||||
    public function getNestedProperty($property, $default = null, $separator = null)
 | 
			
		||||
    {
 | 
			
		||||
        $separator = $separator ?: '.';
 | 
			
		||||
        $path = explode($separator, $property);
 | 
			
		||||
        $path = explode($separator, (string) $property);
 | 
			
		||||
        $offset = array_shift($path);
 | 
			
		||||
 | 
			
		||||
        if (!$this->hasProperty($offset)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ namespace Grav\Framework\Session;
 | 
			
		||||
 | 
			
		||||
use ArrayIterator;
 | 
			
		||||
use Exception;
 | 
			
		||||
use Throwable;
 | 
			
		||||
use Grav\Common\Debugger;
 | 
			
		||||
use Grav\Common\Grav;
 | 
			
		||||
use Grav\Common\User\Interfaces\UserInterface;
 | 
			
		||||
@@ -254,13 +255,17 @@ class Session implements SessionInterface
 | 
			
		||||
        $this->started = true;
 | 
			
		||||
        $this->onSessionStart();
 | 
			
		||||
 | 
			
		||||
        $user = $this->__get('user');
 | 
			
		||||
        if ($user && (!$user instanceof UserInterface || (method_exists($user, 'isValid') && !$user->isValid()))) {
 | 
			
		||||
        try {
 | 
			
		||||
            $user = $this->__get('user');
 | 
			
		||||
            if ($user && (!$user instanceof UserInterface || (method_exists($user, 'isValid') && !$user->isValid()))) {
 | 
			
		||||
                throw new RuntimeException('Bad user');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Throwable $e) {
 | 
			
		||||
            $this->invalidate();
 | 
			
		||||
 | 
			
		||||
            throw new SessionException('Invalid User object, session destroyed.', 500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Extend the lifetime of the session.
 | 
			
		||||
        if ($sessionExists) {
 | 
			
		||||
            $this->setCookie();
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ class UriFactory
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Support ngnix routes.
 | 
			
		||||
        if (strpos($query, '_url=') === 0) {
 | 
			
		||||
        if (strpos((string) $query, '_url=') === 0) {
 | 
			
		||||
            parse_str($query, $q);
 | 
			
		||||
            unset($q['_url']);
 | 
			
		||||
            $query = http_build_query($q);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								system/src/Twig/DeferredExtension/DeferredDeclareNode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								system/src/Twig/DeferredExtension/DeferredDeclareNode.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This file is part of the rybakit/twig-deferred-extension package.
 | 
			
		||||
 *
 | 
			
		||||
 * (c) Eugene Leonovich <gen.work@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view the LICENSE
 | 
			
		||||
 * file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Twig\DeferredExtension;
 | 
			
		||||
 | 
			
		||||
use Twig\Compiler;
 | 
			
		||||
use Twig\Node\Node;
 | 
			
		||||
 | 
			
		||||
final class DeferredDeclareNode extends Node
 | 
			
		||||
{
 | 
			
		||||
    public function compile(Compiler $compiler) : void
 | 
			
		||||
    {
 | 
			
		||||
        $compiler
 | 
			
		||||
            ->write("private \$deferred;\n")
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,7 +16,7 @@ namespace Twig\DeferredExtension;
 | 
			
		||||
use Twig\Compiler;
 | 
			
		||||
use Twig\Node\Node;
 | 
			
		||||
 | 
			
		||||
final class DeferredExtensionNode extends Node
 | 
			
		||||
final class DeferredInitializeNode extends Node
 | 
			
		||||
{
 | 
			
		||||
    public function compile(Compiler $compiler) : void
 | 
			
		||||
    {
 | 
			
		||||
@@ -34,8 +34,9 @@ final class DeferredNodeVisitor implements NodeVisitorInterface
 | 
			
		||||
    public function leaveNode(Node $node, Environment $env) : ?Node
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->hasDeferred && $node instanceof ModuleNode) {
 | 
			
		||||
            $node->setNode('constructor_end', new Node([new DeferredExtensionNode(), $node->getNode('constructor_end')]));
 | 
			
		||||
            $node->setNode('display_end', new Node([new DeferredNode(), $node->getNode('display_end')]));
 | 
			
		||||
            $node->getNode('constructor_end')->setNode('deferred_initialize', new DeferredInitializeNode());
 | 
			
		||||
            $node->getNode('display_end')->setNode('deferred_resolve', new DeferredResolveNode());
 | 
			
		||||
            $node->getNode('class_end')->setNode('deferred_declare', new DeferredDeclareNode());
 | 
			
		||||
            $this->hasDeferred = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,9 @@ final class DeferredNodeVisitorCompat implements NodeVisitorInterface
 | 
			
		||||
    public function leaveNode(\Twig_NodeInterface $node, Environment $env): ?Node
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->hasDeferred && $node instanceof ModuleNode) {
 | 
			
		||||
            $node->setNode('constructor_end', new Node([new DeferredExtensionNode(), $node->getNode('constructor_end')]));
 | 
			
		||||
            $node->setNode('display_end', new Node([new DeferredNode(), $node->getNode('display_end')]));
 | 
			
		||||
            $node->getNode('constructor_end')->setNode('deferred_initialize', new DeferredInitializeNode());
 | 
			
		||||
            $node->getNode('display_end')->setNode('deferred_resolve', new DeferredResolveNode());
 | 
			
		||||
            $node->getNode('class_end')->setNode('deferred_declare', new DeferredDeclareNode());
 | 
			
		||||
            $this->hasDeferred = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								system/src/Twig/DeferredExtension/DeferredResolveNode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								system/src/Twig/DeferredExtension/DeferredResolveNode.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This file is part of the rybakit/twig-deferred-extension package.
 | 
			
		||||
 *
 | 
			
		||||
 * (c) Eugene Leonovich <gen.work@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view the LICENSE
 | 
			
		||||
 * file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Twig\DeferredExtension;
 | 
			
		||||
 | 
			
		||||
use Twig\Compiler;
 | 
			
		||||
use Twig\Node\Node;
 | 
			
		||||
 | 
			
		||||
final class DeferredResolveNode extends Node
 | 
			
		||||
{
 | 
			
		||||
    public function compile(Compiler $compiler) : void
 | 
			
		||||
    {
 | 
			
		||||
        $compiler
 | 
			
		||||
            ->write("\$this->deferred->resolve(\$this, \$context, \$blocks);\n")
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
core:
 | 
			
		||||
  grav:
 | 
			
		||||
    version: 1.7.38
 | 
			
		||||
    version: 1.7.42.3
 | 
			
		||||
    schema: 1.7.0_2020-11-20_1
 | 
			
		||||
    history:
 | 
			
		||||
      - { version: 1.7.16, date: '2021-06-10 14:03:35' }
 | 
			
		||||
@@ -8,3 +8,4 @@ core:
 | 
			
		||||
      - { version: 1.7.25, date: '2021-12-06 12:22:00' }
 | 
			
		||||
      - { version: 1.7.31, date: '2022-03-15 08:48:47' }
 | 
			
		||||
      - { version: 1.7.38, date: '2023-01-03 15:06:08' }
 | 
			
		||||
      - { version: 1.7.42.3, date: '2023-09-19 10:47:33' }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ server {
 | 
			
		||||
    ## Begin - PHP
 | 
			
		||||
    location ~ \.php$ {
 | 
			
		||||
        # Choose either a socket or TCP/IP address
 | 
			
		||||
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
 | 
			
		||||
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
 | 
			
		||||
        # fastcgi_pass unix:/var/run/php5-fpm.sock; #legacy
 | 
			
		||||
        # fastcgi_pass 127.0.0.1:9000;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user