maj
This commit is contained in:
parent
131ffd7020
commit
adc7d775dc
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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user