diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7352ebb..73d6f40d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v1.7.18 +## 07/19/2021 + +1. [](#improved) + * Added support for loading Flex Directory configuration from main configuration + * Move SVGs that cannot be sanitized to quarantine folder under `log://quarantine` + * Added support for CloudFlare-forwarded client IP in the `URI::ip()` method +1. [](#bugfix) + * Fixed error when using Flex `SimpleStorage` with no entries + * Fixed page search to include slug field [#3316](https://github.com/getgrav/grav/issues/3316) + * Fixed Admin becoming unusable when GPM cannot be reached [#3383](https://github.com/getgrav/grav/issues/3383) + * Fixed `Failed to save entry: Forbidden` when moving a page to a visible page [#3389](https://github.com/getgrav/grav/issues/3389) + * Better support for Symfony local server on linux [#3400](https://github.com/getgrav/grav/pull/3400) + * Fixed `open_basedir()` error with some forms + # v1.7.17 ## 06/15/2021 diff --git a/index.php b/index.php index 9842952d5..fcbcfd7f3 100644 --- a/index.php +++ b/index.php @@ -17,8 +17,8 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) { } if (PHP_SAPI === 'cli-server') { - $symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'], 'symfony -') !== false; + $symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'], 'symfony') !== false || stripos($_ENV['SERVER_SOFTWARE'], 'symfony') !== false; + if (!isset($_SERVER['PHP_CLI_ROUTER']) && !$symfony_server) { die("PHP webserver requires a router to run Grav, please use:
php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php");
}
diff --git a/system/blueprints/flex/pages.yaml b/system/blueprints/flex/pages.yaml
index ee2e7e5fa..5c6ed8eb5 100644
--- a/system/blueprints/flex/pages.yaml
+++ b/system/blueprints/flex/pages.yaml
@@ -184,9 +184,9 @@ config:
# Fields to be searched
fields:
- key
+ - slug
- menu
- title
- - name
blueprints:
configure:
diff --git a/system/defines.php b/system/defines.php
index 46126d059..4cd520a60 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -9,7 +9,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '1.7.17');
+define('GRAV_VERSION', '1.7.18');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);
diff --git a/system/src/Grav/Common/Flex/Types/Pages/PageObject.php b/system/src/Grav/Common/Flex/Types/Pages/PageObject.php
index a8342d121..21a03d35f 100644
--- a/system/src/Grav/Common/Flex/Types/Pages/PageObject.php
+++ b/system/src/Grav/Common/Flex/Types/Pages/PageObject.php
@@ -273,7 +273,7 @@ class PageObject extends FlexPageObject
$parentKey = $this->getProperty('parent_key');
/** @var PageObject|null $parent */
- $parent = $this->getFlexDirectory()->getObject($parentKey);
+ $parent = $this->getFlexDirectory()->getObject($parentKey, 'storage_key');
if (!$parent || !$parent->isAuthorized('create', null, $user)) {
throw new \RuntimeException('Forbidden', 403);
}
diff --git a/system/src/Grav/Common/GPM/GPM.php b/system/src/Grav/Common/GPM/GPM.php
index 222aa0ada..dfed77a14 100644
--- a/system/src/Grav/Common/GPM/GPM.php
+++ b/system/src/Grav/Common/GPM/GPM.php
@@ -35,7 +35,11 @@ class GPM extends Iterator
/** @var Remote\Packages|null Remote available Packages */
private $repository;
/** @var Remote\GravCore|null Remove Grav Packages */
- public $grav;
+ private $grav;
+ /** @var bool */
+ private $refresh;
+ /** @var callable|null */
+ private $callback;
/** @var array Internal cache */
protected $cache;
@@ -55,13 +59,45 @@ class GPM extends Iterator
public function __construct($refresh = false, $callback = null)
{
parent::__construct();
+
+ Folder::create(GRAV_ROOT . '/cache/gpm');
+
$this->cache = [];
$this->installed = new Local\Packages();
- try {
- $this->repository = new Remote\Packages($refresh, $callback);
- $this->grav = new Remote\GravCore($refresh, $callback);
- } catch (Exception $e) {
+ $this->refresh = $refresh;
+ $this->callback = $callback;
+ }
+
+ /**
+ * Magic getter method
+ *
+ * @param string $offset Asset name value
+ * @return mixed Asset value
+ */
+ public function __get($offset)
+ {
+ switch ($offset) {
+ case 'grav':
+ return $this->getGrav();
}
+
+ return parent::__get($offset);
+ }
+
+ /**
+ * Magic method to determine if the attribute is set
+ *
+ * @param string $offset Asset name value
+ * @return bool True if the value is set
+ */
+ public function __isset($offset)
+ {
+ switch ($offset) {
+ case 'grav':
+ return $this->getGrav() !== null;
+ }
+
+ return parent::__isset($offset);
}
/**
@@ -266,11 +302,12 @@ class GPM extends Iterator
{
$items = [];
- if (null === $this->repository) {
+ $repository = $this->getRepository();
+ if (null === $repository) {
return $items;
}
- $repository = $this->repository['plugins'];
+ $plugins = $repository['plugins'];
// local cache to speed things up
if (isset($this->cache[__METHOD__])) {
@@ -278,18 +315,18 @@ class GPM extends Iterator
}
foreach ($this->installed['plugins'] as $slug => $plugin) {
- if (!isset($repository[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
+ if (!isset($plugins[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
continue;
}
$local_version = $plugin->version ?? 'Unknown';
- $remote_version = $repository[$slug]->version;
+ $remote_version = $plugins[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) {
- $repository[$slug]->available = $remote_version;
- $repository[$slug]->version = $local_version;
- $repository[$slug]->type = $repository[$slug]->release_type;
- $items[$slug] = $repository[$slug];
+ $plugins[$slug]->available = $remote_version;
+ $plugins[$slug]->version = $local_version;
+ $plugins[$slug]->type = $plugins[$slug]->release_type;
+ $items[$slug] = $plugins[$slug];
}
}
@@ -306,19 +343,20 @@ class GPM extends Iterator
*/
public function getLatestVersionOfPackage($package_name)
{
- if (null === $this->repository) {
+ $repository = $this->getRepository();
+ if (null === $repository) {
return null;
}
- $repository = $this->repository['plugins'];
- if (isset($repository[$package_name])) {
- return $repository[$package_name]->available ?: $repository[$package_name]->version;
+ $plugins = $repository['plugins'];
+ if (isset($plugins[$package_name])) {
+ return $plugins[$package_name]->available ?: $plugins[$package_name]->version;
}
//Not a plugin, it's a theme?
- $repository = $this->repository['themes'];
- if (isset($repository[$package_name])) {
- return $repository[$package_name]->available ?: $repository[$package_name]->version;
+ $themes = $repository['themes'];
+ if (isset($themes[$package_name])) {
+ return $themes[$package_name]->available ?: $themes[$package_name]->version;
}
return null;
@@ -356,11 +394,12 @@ class GPM extends Iterator
{
$items = [];
- if (null === $this->repository) {
+ $repository = $this->getRepository();
+ if (null === $repository) {
return $items;
}
- $repository = $this->repository['themes'];
+ $themes = $repository['themes'];
// local cache to speed things up
if (isset($this->cache[__METHOD__])) {
@@ -368,18 +407,18 @@ class GPM extends Iterator
}
foreach ($this->installed['themes'] as $slug => $plugin) {
- if (!isset($repository[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
+ if (!isset($themes[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
continue;
}
$local_version = $plugin->version ?? 'Unknown';
- $remote_version = $repository[$slug]->version;
+ $remote_version = $themes[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) {
- $repository[$slug]->available = $remote_version;
- $repository[$slug]->version = $local_version;
- $repository[$slug]->type = $repository[$slug]->release_type;
- $items[$slug] = $repository[$slug];
+ $themes[$slug]->available = $remote_version;
+ $themes[$slug]->version = $local_version;
+ $themes[$slug]->type = $themes[$slug]->release_type;
+ $items[$slug] = $themes[$slug];
}
}
@@ -407,19 +446,20 @@ class GPM extends Iterator
*/
public function getReleaseType($package_name)
{
- if (null === $this->repository) {
+ $repository = $this->getRepository();
+ if (null === $repository) {
return null;
}
- $repository = $this->repository['plugins'];
- if (isset($repository[$package_name])) {
- return $repository[$package_name]->release_type;
+ $plugins = $repository['plugins'];
+ if (isset($plugins[$package_name])) {
+ return $plugins[$package_name]->release_type;
}
//Not a plugin, it's a theme?
- $repository = $this->repository['themes'];
- if (isset($repository[$package_name])) {
- return $repository[$package_name]->release_type;
+ $themes = $repository['themes'];
+ if (isset($themes[$package_name])) {
+ return $themes[$package_name]->release_type;
}
return null;
@@ -470,7 +510,7 @@ class GPM extends Iterator
*/
public function getRepositoryPlugins()
{
- return $this->repository['plugins'] ?? null;
+ return $this->getRepository()['plugins'] ?? null;
}
/**
@@ -493,7 +533,7 @@ class GPM extends Iterator
*/
public function getRepositoryThemes()
{
- return $this->repository['themes'] ?? null;
+ return $this->getRepository()['themes'] ?? null;
}
/**
@@ -504,9 +544,31 @@ class GPM extends Iterator
*/
public function getRepository()
{
+ if (null === $this->repository) {
+ try {
+ $this->repository = new Remote\Packages($this->refresh, $this->callback);
+ } catch (Exception $e) {}
+ }
+
return $this->repository;
}
+ /**
+ * Returns Grav version available in the repository
+ *
+ * @return Remote\GravCore|null
+ */
+ public function getGrav()
+ {
+ if (null === $this->grav) {
+ try {
+ $this->grav = new Remote\GravCore($this->refresh, $this->callback);
+ } catch (Exception $e) {}
+ }
+
+ return $this->grav;
+ }
+
/**
* Searches for a Package in the repository
*
diff --git a/system/src/Grav/Common/Security.php b/system/src/Grav/Common/Security.php
index fe33d7964..c464e432d 100644
--- a/system/src/Grav/Common/Security.php
+++ b/system/src/Grav/Common/Security.php
@@ -12,6 +12,7 @@ namespace Grav\Common;
use enshrined\svgSanitize\Sanitizer;
use Exception;
use Grav\Common\Config\Config;
+use Grav\Common\Filesystem\Folder;
use Grav\Common\Page\Pages;
use function chr;
use function count;
@@ -56,9 +57,16 @@ class Security
$original_svg = file_get_contents($file);
$clean_svg = $sanitizer->sanitize($original_svg);
- // TODO: what to do with bad SVG files which return false?
- if ($clean_svg !== false && $clean_svg !== $original_svg) {
+ // Quarantine bad SVG files and throw exception
+ if ($clean_svg !== false ) {
file_put_contents($file, $clean_svg);
+ } else {
+ $quarantine_file = basename($file);
+ $quarantine_dir = 'log://quarantine';
+ Folder::mkdir($quarantine_dir);
+ file_put_contents("$quarantine_dir/$quarantine_file", $original_svg);
+ unlink($file);
+ throw new Exception('SVG could not be sanitized, it has been moved to the logs/quarantine folder');
}
}
}
diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php
index 798176bbd..c7f71048e 100644
--- a/system/src/Grav/Common/Uri.php
+++ b/system/src/Grav/Common/Uri.php
@@ -675,10 +675,15 @@ class Uri
*/
public static function ip()
{
+ $ip = 'UNKNOWN';
+
if (getenv('HTTP_CLIENT_IP')) {
$ip = getenv('HTTP_CLIENT_IP');
+ } elseif (getenv('HTTP_CF_CONNECTING_IP')) {
+ $ip = getenv('HTTP_CF_CONNECTING_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) {
- $ip = getenv('HTTP_X_FORWARDED_FOR');
+ $ips = array_map('trim', explode(',', getenv('HTTP_X_FORWARDED_FOR')));
+ $ip = array_shift($ips);
} elseif (getenv('HTTP_X_FORWARDED') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) {
$ip = getenv('HTTP_X_FORWARDED');
} elseif (getenv('HTTP_FORWARDED_FOR')) {
@@ -687,8 +692,6 @@ class Uri
$ip = getenv('HTTP_FORWARDED');
} elseif (getenv('REMOTE_ADDR')) {
$ip = getenv('REMOTE_ADDR');
- } else {
- $ip = 'UNKNOWN';
}
return $ip;
diff --git a/system/src/Grav/Framework/Flex/FlexDirectory.php b/system/src/Grav/Framework/Flex/FlexDirectory.php
index 1f6962663..8d8dd1e64 100644
--- a/system/src/Grav/Framework/Flex/FlexDirectory.php
+++ b/system/src/Grav/Framework/Flex/FlexDirectory.php
@@ -235,7 +235,17 @@ class FlexDirectory implements FlexDirectoryInterface
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
- $filename = $locator->findResource($this->getDirectoryConfigUri($name), true);
+ $uri = $this->getDirectoryConfigUri($name);
+
+ // If configuration is found in main configuration, use it.
+ if (str_starts_with($uri, 'config://')) {
+ $path = strtr(substr($uri, 9, -5), '/', '.');
+
+ return $grav['config']->get($path);
+ }
+
+ // Load the configuration file.
+ $filename = $locator->findResource($uri, true);
if ($filename === false) {
return [];
}
diff --git a/system/src/Grav/Framework/Flex/FlexObject.php b/system/src/Grav/Framework/Flex/FlexObject.php
index 06e383bff..97b2569b0 100644
--- a/system/src/Grav/Framework/Flex/FlexObject.php
+++ b/system/src/Grav/Framework/Flex/FlexObject.php
@@ -1074,6 +1074,17 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
return $action;
}
+ /**
+ * Method to reset blueprints if the type changes.
+ *
+ * @return void
+ * @since 1.7.18
+ */
+ protected function resetBlueprints(): void
+ {
+ $this->_blueprint = [];
+ }
+
// DEPRECATED METHODS
/**
diff --git a/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php b/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php
index 85c14290e..6faac9d84 100644
--- a/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php
+++ b/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php
@@ -455,7 +455,7 @@ class SimpleStorage extends AbstractFilesystemStorage
$content = (array) $file->content();
if ($this->prefix) {
$data = new Data($content);
- $content = $data->get($this->prefix);
+ $content = $data->get($this->prefix, []);
}
$file->free();
diff --git a/system/src/Grav/Framework/Form/FormFlash.php b/system/src/Grav/Framework/Form/FormFlash.php
index 00aab2893..864e992f3 100644
--- a/system/src/Grav/Framework/Form/FormFlash.php
+++ b/system/src/Grav/Framework/Form/FormFlash.php
@@ -120,7 +120,7 @@ class FormFlash implements FormFlashInterface
protected function loadStoredForm(): ?array
{
$file = $this->getTmpIndex();
- $exists = $file->exists();
+ $exists = $file && $file->exists();
$data = null;
if ($exists) {
@@ -246,8 +246,10 @@ class FormFlash implements FormFlashInterface
if ($force || $this->data || $this->files) {
// Only save if there is data or files to be saved.
$file = $this->getTmpIndex();
- $file->save($this->jsonSerialize());
- $this->exists = true;
+ if ($file) {
+ $file->save($this->jsonSerialize());
+ $this->exists = true;
+ }
} elseif ($this->exists) {
// Delete empty form flash if it exists (it carries no information).
return $this->delete();
@@ -476,12 +478,14 @@ class FormFlash implements FormFlashInterface
}
/**
- * @return YamlFile
+ * @return ?YamlFile
*/
- protected function getTmpIndex(): YamlFile
+ protected function getTmpIndex(): ?YamlFile
{
+ $tmpDir = $this->getTmpDir();
+
// Do not use CompiledYamlFile as the file can change multiple times per second.
- return YamlFile::instance($this->getTmpDir() . '/index.yaml');
+ return $tmpDir ? YamlFile::instance($tmpDir . '/index.yaml') : null;
}
/**
@@ -503,7 +507,9 @@ class FormFlash implements FormFlashInterface
{
// Make sure that index file cache gets always cleared.
$file = $this->getTmpIndex();
- $file->free();
+ if ($file) {
+ $file->free();
+ }
$tmpDir = $this->getTmpDir();
if ($tmpDir && file_exists($tmpDir)) {
diff --git a/system/src/Grav/Framework/Object/ObjectCollection.php b/system/src/Grav/Framework/Object/ObjectCollection.php
index 0d82434a1..3a51ec80c 100644
--- a/system/src/Grav/Framework/Object/ObjectCollection.php
+++ b/system/src/Grav/Framework/Object/ObjectCollection.php
@@ -9,7 +9,6 @@
namespace Grav\Framework\Object;
-use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Grav\Framework\Collection\ArrayCollection;
use Grav\Framework\Object\Access\NestedPropertyCollectionTrait;