Merge branch 'release/1.7.18'

This commit is contained in:
Andy Miller
2021-07-19 12:16:38 -06:00
13 changed files with 171 additions and 57 deletions

View File

@@ -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

View File

@@ -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: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
}

View File

@@ -184,9 +184,9 @@ config:
# Fields to be searched
fields:
- key
- slug
- menu
- title
- name
blueprints:
configure:

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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
*

View File

@@ -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');
}
}
}

View File

@@ -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;

View File

@@ -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 [];
}

View File

@@ -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
/**

View File

@@ -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();

View File

@@ -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)) {

View File

@@ -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;