New sites have compatibility features turned off by default, upgrading from older versions will keep the settings on

This commit is contained in:
Matias Griese
2020-11-23 19:09:55 +02:00
parent b66287cbb9
commit 631e0e0f3f
12 changed files with 1032 additions and 25 deletions

View File

@@ -9,6 +9,7 @@
* Upgraded `bin/composer.phar` to `2.0.2` which is all new and much faster
* Added search option `same_as` to Flex Objects
* Added PHP 8 compatible `function_exists()`: `Utils::functionExists()`
* New sites have `compatibility` features turned off by default, upgrading from older versions will keep the settings on
1. [](#improved)
* Updated bundled JQuery to latest version `3.5.1`
* Forward a `sid` to GPM when downloading a premium package via CLI

View File

@@ -21,7 +21,6 @@
"ext-libxml": "*",
"symfony/polyfill-mbstring": "~1.20",
"symfony/polyfill-iconv": "^1.20",
"symfony/polyfill-php73": "^1.20",
"symfony/polyfill-php74": "^1.20",
"symfony/polyfill-php80": "^1.20",
"psr/simple-cache": "^1.0",

View File

@@ -846,7 +846,8 @@ form:
type: toggle
label: PLUGIN_ADMIN.AUTOESCAPE_VARIABLES
help: PLUGIN_ADMIN.AUTOESCAPE_VARIABLES_HELP
highlight: 0
highlight: 1
default: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
@@ -1565,8 +1566,8 @@ form:
strict_mode.blueprint_compat:
type: toggle
label: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT
highlight: 1
default: 1
highlight: 0
default: 0
help: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT_HELP
options:
1: PLUGIN_ADMIN.YES
@@ -1577,8 +1578,8 @@ form:
strict_mode.yaml_compat:
type: toggle
label: PLUGIN_ADMIN.STRICT_YAML_COMPAT
highlight: 1
default: 1
highlight: 0
default: 0
help: PLUGIN_ADMIN.STRICT_YAML_COMPAT_HELP
options:
1: PLUGIN_ADMIN.YES
@@ -1589,8 +1590,8 @@ form:
strict_mode.twig_compat:
type: toggle
label: PLUGIN_ADMIN.STRICT_TWIG_COMPAT
highlight: 1
default: 1
highlight: 0
default: 0
help: PLUGIN_ADMIN.STRICT_TWIG_COMPAT_HELP
options:
1: PLUGIN_ADMIN.YES

View File

@@ -108,7 +108,7 @@ twig:
cache: true # Set to true to enable Twig caching
debug: true # Enable Twig debug
auto_reload: true # Refresh cache on changes
autoescape: false # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
autoescape: true # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
undefined_functions: true # Allow undefined functions
undefined_filters: true # Allow undefined filters
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
@@ -196,6 +196,6 @@ flex:
lifetime: 600 # Lifetime of cached HTML in seconds (0 = infinite)
strict_mode:
yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility
twig_compat: true # Grav 1.5+: Enables deprecated Twig autoescape setting (autoescape: false)
blueprint_compat: true # Grav 1.7+: Enables backward compatible strict support for blueprints
yaml_compat: false # Set to true to enable YAML backwards compatibility
twig_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
blueprint_compat: false # Set to true to enable backward compatible strict support for blueprints

View File

@@ -1106,6 +1106,14 @@ class Pages
if (null === self::$types) {
$grav = Grav::instance();
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
// Prevent calls made before theme:// has been initialized (happens when upgrading old version of Admin plugin).
if (!$locator->isStream('theme://')) {
return new Types();
}
$scanBlueprintsAndTemplates = static function (Types $types) use ($grav) {
// Scan blueprints
$event = new Event();

View File

@@ -14,13 +14,14 @@ use Grav\Common\Cache;
use Grav\Common\GPM\Installer;
use Grav\Common\Grav;
use Grav\Common\Plugins;
use function dirname;
/**
* Grav installer.
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
class Install
final class Install
{
/** @var int Installer version. */
public $version = 1;
@@ -39,8 +40,7 @@ class Install
'name' => 'Grav',
'versions' => [
'1.6' => '1.6.0',
'1.5' => '1.5.0',
'' => '1.6.22'
'' => '1.6.28'
]
],
'plugins' => [
@@ -49,7 +49,6 @@ class Install
'optional' => true,
'versions' => [
'1.9' => '1.9.0',
'1.8' => '1.8.0',
'' => '1.9.13'
]
],
@@ -58,28 +57,26 @@ class Install
'optional' => true,
'versions' => [
'3.0' => '3.0.0',
'2.7' => '2.7.0',
'' => '3.0.7'
'' => '3.0.10'
]
],
'form' => [
'name' => 'Form',
'optional' => true,
'versions' => [
'4.1' => '4.1.0',
'4.0' => '4.0.0',
'3.0' => '3.0.0',
'2.16' => '2.16.0',
'' => '4.0.5'
'' => '4.1.2'
]
],
'login' => [
'name' => 'Login',
'optional' => true,
'versions' => [
'3.1' => '3.1.0',
'3.3' => '3.3.0',
'3.0' => '3.0.0',
'2.8' => '2.8.0',
'' => '3.1.0'
'' => '3.3.6'
]
],
]
@@ -99,7 +96,11 @@ class Install
/** @var array */
private $classMap = [
// 'Grav\\Installer\\Test' => __DIR__ . '/Test.php',
InstallException::class => __DIR__ . '/InstallException.php',
Versions::class => __DIR__ . '/Versions.php',
VersionUpdate::class => __DIR__ . '/VersionUpdate.php',
VersionUpdater::class => __DIR__ . '/VersionUpdater.php',
YamlUpdater::class => __DIR__ . '/YamlUpdater.php',
];
/** @var string|null */
@@ -108,9 +109,15 @@ class Install
/** @var string|null */
private $location;
/** @var VersionUpdater */
private $updater;
/** @var static */
private static $instance;
/**
* @return static
*/
public static function instance()
{
if (null === self::$instance) {
@@ -143,7 +150,19 @@ class Install
$error[] = "{$req['title']} >= <strong>v{$req['minimum']}</strong> required, you have <strong>v{$req['installed']}</strong>";
}
throw new \RuntimeException(implode("<br />\n", $error));
$errors = implode("<br />\n", $error);
if (\defined('GRAV_CLI') && GRAV_CLI) {
$errors = "\n\n" . strip_tags($errors) . "\n\n";
$errors .= <<<ERR
Please install Grav 1.6.28 first by running following commands:
wget -q https://getgrav.org/download/core/grav-update/1.6.28 -O grav-update.zip
bin/gpm direct-install -y grav-update.zip
rm grav-update.zip
ERR;
}
throw new \RuntimeException($errors);
}
$this->prepare();
@@ -200,7 +219,14 @@ class Install
// Override Grav\Installer classes by using this version of Grav.
$loader->addClassMap($this->classMap);
$this->legacySupport();
$this->location = dirname($location, 4);
$versions = Versions::instance(GRAV_ROOT . '/user/config/versions.yaml');
$this->updater = new VersionUpdater('core/grav', __DIR__ . '/updates', $versions);
$this->updater->preflight();
}
/**
@@ -238,6 +264,8 @@ class Install
*/
public function finalize(): void
{
$this->updater->postflight();
Cache::clearCache();
clearstatcache();
@@ -305,4 +333,10 @@ class Install
$this->checkVersion($results, 'plugin', $name, $check, $version);
}
}
protected function legacySupport(): void
{
// Support install for Grav 1.6.0 - 1.6.20 by loading the original class from the older version of Grav.
class_exists(\Grav\Console\Cli\CacheCommand::class, true);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Throwable;
/**
* Class InstallException
* @package Grav\Installer
*/
class InstallException extends \RuntimeException
{
/**
* InstallException constructor.
* @param string $message
* @param Throwable $previous
*/
public function __construct(string $message, Throwable $previous)
{
parent::__construct($message, $previous->getCode(), $previous);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Grav\Installer;
use Closure;
/**
* Class VersionUpdate
* @package Grav\Installer
*/
final class VersionUpdate
{
/** @var string */
private $revision;
/** @var string */
private $version;
/** @var string */
private $date;
/** @var string */
private $patch;
/** @var VersionUpdater */
private $updater;
/** @var callable[] */
private $methods;
public function __construct(string $file, VersionUpdater $updater)
{
$name = basename($file, '.php');
$this->revision = $name;
[$this->version, $this->date, $this->patch] = explode('_', $name);
$this->updater = $updater;
$this->methods = require $file;
}
public function getRevision(): string
{
return $this->revision;
}
public function getVersion(): string
{
return $this->version;
}
public function getDate(): string
{
return $this->date;
}
public function getPatch(): string
{
return $this->date;
}
public function getUpdater(): VersionUpdater
{
return $this->updater;
}
/**
* Run right before installation.
*/
public function preflight(VersionUpdater $updater): void
{
$method = $this->methods['preflight'] ?? null;
if ($method instanceof Closure) {
$method->call($this);
}
}
/**
* Runs right after installation.
*/
public function postflight(VersionUpdater $updater): void
{
$method = $this->methods['postflight'] ?? null;
if ($method instanceof Closure) {
$method->call($this);
}
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace Grav\Installer;
use DirectoryIterator;
/**
* Class Update_1_7_0_20201120_1
* @package Grav\Installer\updates
*/
final class VersionUpdater
{
/** @var string */
private $name;
/** @var string */
private $path;
/** @var Versions */
private $versions;
/** @var VersionUpdate[] */
private $updates;
/**
* VersionUpdater constructor.
* @param string $name
* @param string $path
* @param Versions $versions
*/
public function __construct(string $name, string $path, Versions $versions)
{
$this->name = $name;
$this->path = $path;
$this->versions = $versions;
$this->loadUpdates();
}
/**
* Pre-installation methods.
*/
public function preflight(): void
{
foreach ($this->updates as $revision => $update) {
$update->preflight($this);
}
}
/**
* Post-installation methods.
*/
public function postflight(): void
{
$versions = $this->getVersions();
foreach ($this->updates as $revision => $update) {
$update->postflight($this);
$versions->setSchema($this->name, $revision);
$versions->save();
}
}
/**
* @return Versions
*/
public function getVersions(): Versions
{
return $this->versions;
}
/**
* @param string|null $name
* @return string|null
*/
public function getExtensionVersion(string $name = null): ?string
{
return $this->versions->getVersion($name ?? $this->name);
}
/**
* @param string|null $name
* @return string|null
*/
public function getExtensionSchema(string $name = null): ?string
{
return $this->versions->getSchema($name ?? $this->name);
}
/**
* @param string|null $name
* @return array
*/
public function getExtensionHistory(string $name = null): array
{
return $this->versions->getHistory($name ?? $this->name);
}
protected function loadUpdates(): void
{
$schema = $this->getExtensionSchema();
$iterator = new DirectoryIterator($this->path);
foreach ($iterator as $item) {
if (!$item->isFile() || $item->getExtension() !== 'php') {
continue;
}
$revision = $item->getBasename('.php');
if (!$schema || version_compare($revision, $schema, '>')) {
$this->updates[$revision] = new VersionUpdate($item->getRealPath(), $this);
}
}
uksort($this->updates, 'version_compare');
}
}

View File

@@ -0,0 +1,308 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Symfony\Component\Yaml\Yaml;
use function is_array;
use function is_string;
/**
* Grav Versions
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
final class Versions
{
/** @var string */
protected $filename;
/** @var array */
protected $items;
/** @var bool */
protected $updated = false;
/** @var self[] */
protected static $instance;
/**
* @param string|null $filename
* @return static
*/
public static function instance(string $filename = null): self
{
$filename = $filename ?? GRAV_ROOT . '/user/config/versions.yaml';
if (!isset(self::$instance[$filename])) {
self::$instance[$filename] = new self($filename);
}
return self::$instance[$filename];
}
/**
* @return bool True if the file was updated.
*/
public function save(): bool
{
if (!$this->updated) {
return false;
}
file_put_contents($this->filename, Yaml::dump($this->items, 5, 2));
$this->updated = false;
return true;
}
/**
* @return array
*/
public function getAll(): array
{
return $this->items;
}
/**
* @return array|null
*/
public function getGrav(): ?array
{
return $this->get('core/grav');
}
/**
* @return array
*/
public function getPlugins(): array
{
return $this->get('plugins', []);
}
/**
* @param string $name
* @return array|null
*/
public function getPlugin(string $name): ?array
{
return $this->get("plugins/{$name}");
}
/**
* @return array
*/
public function getThemes(): array
{
return $this->get('themes', []);
}
/**
* @param string $name
* @return array|null
*/
public function getTheme(string $name): ?array
{
return $this->get("themes/{$name}");
}
/**
* @param string $extension
* @return array|null
*/
public function getExtension(string $extension): ?array
{
return $this->get($extension);
}
/**
* @param string $extension
* @param array|null $value
*/
public function setExtension(string $extension, ?array $value): void
{
if (null !== $value) {
$this->set($extension, $value);
} else {
$this->undef($extension);
}
}
/**
* @param string $extension
* @return string|null
*/
public function getVersion(string $extension): ?string
{
$version = $this->get("{$extension}/version", null);
return is_string($version) ? $version : null;
}
/**
* @param string $extension
* @param string|null $version
*/
public function setVersion(string $extension, ?string $version): void
{
$this->updateHistory($extension, $version);
}
/**
* NOTE: Updates also history.
*
* @param string $extension
* @param string|null $version
*/
public function updateVersion(string $extension, ?string $version): void
{
$this->set("{$extension}/version", $version);
$this->updateHistory($extension, $version);
}
/**
* @param string $extension
* @return string|null
*/
public function getSchema(string $extension): ?string
{
$version = $this->get("{$extension}/schema", null);
return is_string($version) ? $version : null;
}
/**
* @param string $extension
* @param string|null $schema
*/
public function setSchema(string $extension, ?string $schema): void
{
if (null !== $schema) {
$this->set("{$extension}/schema", $schema);
} else {
$this->undef("{$extension}/schema");
}
}
/**
* @param string $extension
* @return array
*/
public function getHistory(string $extension): array
{
$name = "{$extension}/history";
return $this->get($name, []);
}
/**
* @param string $extension
* @param string|null $version
*/
public function updateHistory(string $extension, ?string $version): void
{
$name = "{$extension}/history";
$history = $this->get($name, []);
$history[] = ['version' => $version, 'date' => gmdate('Y-m-d H:i:s')];
$this->set($name, $history);
}
/**
* Clears extension history. Useful when creating skeletons.
*
* @param string|null $extension
*/
public function removeHistory(string $extension): void
{
$this->undef("{$extension}/history");
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Slash separated path to the requested value.
* @param mixed $default Default value (or null).
* @return mixed Value.
*/
private function get(string $name, $default = null)
{
$path = explode('/', $name) ?: [];
$current = $this->items;
foreach ($path as $field) {
if (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @param string $name Slash separated path to the requested value.
* @param mixed $value New value.
*/
private function set(string $name, $value): void
{
$path = explode('/', $name) ?: [];
$current = &$this->items;
foreach ($path as $field) {
// Handle arrays and scalars.
if (!is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
$current = $value;
$this->updated = true;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
*/
private function undef(string $name): void
{
$path = $name !== '' ? explode('/', $name) : [];
if (!$path) {
return;
}
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (!is_array($current) || !isset($current[$field])) {
return;
}
$current = &$current[$field];
}
unset($current[$var]);
$this->updated = true;
}
private function __construct(string $filename)
{
$this->filename = $filename;
$content = is_file($filename) ? file_get_contents($filename) : null;
if (false === $content) {
throw new \RuntimeException('Versions file cannot be read');
}
$this->items = $content ? Yaml::parse($content) : [];
}
}

View File

@@ -0,0 +1,407 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Symfony\Component\Yaml\Yaml;
use function count;
use function is_array;
use function strlen;
/**
* Grav YAML updater.
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
final class YamlUpdater
{
/** @var string */
protected $filename;
/** @var string[] */
protected $lines;
/** @var array */
protected $comments;
/** @var array */
protected $items;
/** @var bool */
protected $updated = false;
/** @var self[] */
protected static $instance;
public static function instance(string $filename): self
{
if (!isset(self::$instance[$filename])) {
self::$instance[$filename] = new self($filename);
}
return self::$instance[$filename];
}
/**
* @return bool
*/
public function save(): bool
{
if (!$this->updated) {
return false;
}
try {
if (!$this->isHandWritten()) {
$yaml = Yaml::dump($this->items, 5, 2);
} else {
$yaml = implode("\n", $this->lines);
$items = Yaml::parse($yaml);
if ($items !== $this->items) {
throw new \RuntimeException('Failed saving the content');
}
}
file_put_contents($this->filename, $yaml);
} catch (\Exception $e) {
throw new \RuntimeException('Failed to update ' . basename($this->filename) . ': ' . $e->getMessage());
}
return true;
}
/**
* @return bool
*/
public function isHandWritten(): bool
{
return !empty($this->comments);
}
/**
* @return array
*/
public function getComments(): array
{
$comments = [];
foreach ($this->lines as $i => $line) {
if ($this->isLineEmpty($line)) {
$comments[$i+1] = $line;
} elseif ($comment = $this->getInlineComment($line)) {
$comments[$i+1] = $comment;
}
}
return $comments;
}
/**
* @param string $variable
* @param mixed $value
*/
public function define(string $variable, $value)
{
// If variable has already value, we're good.
if ($this->get($variable) !== null) {
return;
}
// If one of the parents isn't array, we're good, too.
if (!$this->canDefine($variable)) {
return;
}
$this->set($variable, $value);
if (!$this->isHandWritten()) {
return;
}
$parts = explode('.', $variable);
$lineNos = $this->findPath($this->lines, $parts);
$count = count($lineNos);
$last = array_key_last($lineNos);
$value = explode("\n", trim(Yaml::dump([$last => $this->get(implode('.', array_keys($lineNos)))], max(0, 5-$count), 2)));
$currentLine = array_pop($lineNos) ?: 0;
$parentLine = array_pop($lineNos);
if ($parentLine !== null) {
$c = $this->getLineIndentation($this->lines[$parentLine] ?? 0);
$n = $this->getLineIndentation($this->lines[$parentLine+1] ?? $this->lines[$parentLine] ?? 0);
$indent = $n > $c ? $n : $c + 2;
} else {
$indent = 0;
array_unshift($value, '');
}
$spaces = str_repeat(' ', $indent);
foreach ($value as &$line) {
$line = $spaces . $line;
}
unset($line);
array_splice($this->lines, abs($currentLine)+1, 0, $value);
}
private function __construct(string $filename)
{
$content = is_file($filename) ? file_get_contents($filename) : '';
$content = rtrim(str_replace(["\r\n", "\r"], "\n", $content));
$this->filename = $filename;
$this->lines = explode("\n", $content);
$this->comments = $this->getComments();
$this->items = $content ? Yaml::parse($content) : [];
}
/**
* Return array of offsets for the parent nodes. Negative value means position, but not found.
*
* @param array $lines
* @param array $parts
* @return array
*/
private function findPath(array $lines, array $parts)
{
$test = true;
$indent = -1;
$current = array_shift($parts);
$j = 1;
$found = [];
$space = '';
foreach ($lines as $i => $line) {
if ($this->isLineEmpty($line)) {
if ($this->isLineComment($line) && $this->getLineIndentation($line) > $indent) {
$j = $i;
}
continue;
}
if ($test === true) {
$test = false;
$spaces = strlen($line) - strlen(ltrim($line, ' '));
if ($spaces <= $indent) {
$found[$current] = -$j;
return $found;
}
$indent = $spaces;
$space = $indent ? str_repeat(' ', $indent) : '';
}
if (0 === \strncmp($line, $space, strlen($space))) {
$pattern = "/^{$space}(['\"]?){$current}\\1\:/";
if (preg_match($pattern, $line)) {
$found[$current] = $i;
$current = array_shift($parts);
if ($current === null) {
return $found;
}
$test = true;
}
} else {
$found[$current] = -$j;
return $found;
}
$j = $i;
}
$found[$current] = -$j;
return $found;
}
/**
* Returns true if the current line is blank or if it is a comment line.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is empty or if it is a comment line, false otherwise
*/
private function isLineEmpty(string $line): bool
{
return $this->isLineBlank($line) || $this->isLineComment($line);
}
/**
* Returns true if the current line is blank.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is blank, false otherwise
*/
private function isLineBlank(string $line): bool
{
return '' === trim($line, ' ');
}
/**
* Returns true if the current line is a comment line.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is a comment line, false otherwise
*/
private function isLineComment(string $line): bool
{
//checking explicitly the first char of the trim is faster than loops or strpos
$ltrimmedLine = ltrim($line, ' ');
return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
}
/**
* @param string $line
* @return bool
*/
private function isInlineComment(string $line): bool
{
return $this->getInlineComment($line) !== null;
}
/**
* @param string $line
* @return string|null
*/
private function getInlineComment(string $line): ?string
{
$pos = strpos($line, ' #');
if (false === $pos) {
return null;
}
$parts = explode(' #', $line);
$part = '';
while ($part .= array_shift($parts)) {
// Remove quoted values.
$part = preg_replace('/(([\'"])[^\2]*\2)/', '', $part);
$part = preg_split('/[\'"]/', $part, 2);
if (!isset($part[1])) {
$part = $part[0];
array_unshift($parts, str_repeat(' ', strlen($part) - strlen(trim($part, ' '))));
break;
}
$part = $part[1];
}
return implode(' #', $parts);
}
/**
* Returns the current line indentation.
*
* @param string $line
* @return int The current line indentation
*/
private function getLineIndentation(string $line): int
{
return \strlen($line) - \strlen(ltrim($line, ' '));
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $default Default value (or null).
* @return mixed Value.
*/
private function get(string $name, $default = null)
{
$path = explode('.', $name) ?: [];
$current = $this->items;
foreach ($path as $field) {
if (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value New value.
*/
private function set(string $name, $value): void
{
$path = explode('.', $name) ?: [];
$current = &$this->items;
foreach ($path as $field) {
// Handle arrays and scalars.
if (!is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
$current = $value;
$this->updated = true;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
*/
private function undef(string $name): void
{
$path = $name !== '' ? explode('.', $name) : [];
if (!$path) {
return;
}
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (!is_array($current) || !isset($current[$field])) {
return;
}
$current = &$current[$field];
}
unset($current[$var]);
$this->updated = true;
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @return bool
*/
private function canDefine(string $name): bool
{
$path = explode('.', $name) ?: [];
$current = $this->items;
foreach ($path as $field) {
if (is_array($current)) {
if (!isset($current[$field])) {
return true;
}
$current = $current[$field];
} else {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,24 @@
<?php
use Grav\Installer\InstallException;
use Grav\Installer\VersionUpdate;
use Grav\Installer\YamlUpdater;
return [
'preflight' =>
function () {
/** @var VersionUpdate $this */
try {
// Keep old defaults for backwards compatibility.
$yaml = YamlUpdater::instance(GRAV_ROOT . '/user/config/system.yaml');
$yaml->define('twig.autoescape', false);
$yaml->define('strict_mode.yaml_compat', true);
$yaml->define('strict_mode.twig_compat', true);
$yaml->define('strict_mode.blueprint_compat', true);
$yaml->save();
} catch (\Exception $e) {
throw new InstallException('Could not update system configuration to maintain backwards compatibility', $e);
}
},
'postflight' => null
];