diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c23ea474..258c9c3ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# v1.6.6 +## 04/17/2019 + +1. [](#new) + * `FormInterface` now implements `RenderInterface` + * Added new `FormInterface::getTask()` method which reads the task from `form.task` in the blueprint +1. [](#improved) + * Updated vendor libraries to latest +1. [](#bugfix) + * Rollback `redirect_default_route` logic as it has issues with multi-lang [#2459](https://github.com/getgrav/grav/issues/2459) + * Fix potential issue with `|contains` Twig filter on PHP 7.3 + * Fixed bug in text field filtering: return empty string if value isn't a string or number [#2460](https://github.com/getgrav/grav/issues/2460) + * Force Asset `priority` to be an integer and not throw error if invalid string passed [#2461](https://github.com/getgrav/grav/issues/2461) + * Fixed bug in text field filtering: return empty string if value isn't a string or number + * Fixed `FlexForm` missing getter methods for defining form variables + # v1.6.5 ## 04/15/2019 diff --git a/composer.lock b/composer.lock index 0f2890f11..2e2f47653 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dce6e4d72c9b5ac769cd3d93fecdf1c2", + "content-hash": "a65d66d9ce6844a3743c992e74c1c55d", "packages": [ { "name": "antoligy/dom-string-iterators", @@ -541,17 +541,17 @@ }, { "name": "gregwar/image", - "version": "v2.0.24", + "version": "v2.0.25", "target-dir": "Gregwar/Image", "source": { "type": "git", "url": "https://github.com/Gregwar/Image.git", - "reference": "52145816255dd20cb4bb115d0f9e1030c6287994" + "reference": "03534d5760cbea5c96e6292041ff81a3bb205c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Image/zipball/52145816255dd20cb4bb115d0f9e1030c6287994", - "reference": "52145816255dd20cb4bb115d0f9e1030c6287994", + "url": "https://api.github.com/repos/Gregwar/Image/zipball/03534d5760cbea5c96e6292041ff81a3bb205c36", + "reference": "03534d5760cbea5c96e6292041ff81a3bb205c36", "shasum": "" }, "require": { @@ -589,7 +589,7 @@ "gd", "image" ], - "time": "2019-01-27T15:10:06+00:00" + "time": "2019-03-01T15:55:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -1774,16 +1774,16 @@ }, { "name": "symfony/console", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "24206aff3efe6962593297e57ef697ebb220e384" + "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", - "reference": "24206aff3efe6962593297e57ef697ebb220e384", + "url": "https://api.github.com/repos/symfony/console/zipball/e2840bb38bddad7a0feaf85931e38fdcffdb2f81", + "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81", "shasum": "" }, "require": { @@ -1842,7 +1842,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-04-01T07:32:59+00:00" + "time": "2019-04-08T14:23:48+00:00" }, { "name": "symfony/contracts", @@ -1914,16 +1914,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544" + "reference": "fbce53cd74ac509cbe74b6f227622650ab759b02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", - "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/fbce53cd74ac509cbe74b6f227622650ab759b02", + "reference": "fbce53cd74ac509cbe74b6f227622650ab759b02", "shasum": "" }, "require": { @@ -1974,7 +1974,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-03-30T15:58:42+00:00" + "time": "2019-04-06T13:51:08+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2267,16 +2267,16 @@ }, { "name": "symfony/process", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6" + "reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", - "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", + "url": "https://api.github.com/repos/symfony/process/zipball/8cf39fb4ccff793340c258ee7760fd40bfe745fe", + "reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe", "shasum": "" }, "require": { @@ -2312,20 +2312,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-03-10T20:07:02+00:00" + "time": "2019-04-10T16:20:36+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9f87189ac10b42edf7fb8edc846f1937c6d157cf" + "reference": "e760a38e12b15032325e64be63f7ffc1817af617" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9f87189ac10b42edf7fb8edc846f1937c6d157cf", - "reference": "9f87189ac10b42edf7fb8edc846f1937c6d157cf", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e760a38e12b15032325e64be63f7ffc1817af617", + "reference": "e760a38e12b15032325e64be63f7ffc1817af617", "shasum": "" }, "require": { @@ -2388,11 +2388,11 @@ "debug", "dump" ], - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-04-17T14:57:01+00:00" }, { "name": "symfony/yaml", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -2451,16 +2451,16 @@ }, { "name": "twig/twig", - "version": "v1.38.4", + "version": "v1.39.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "7732e9e7017d751313811bd118de61302e9c8b35" + "reference": "23e7b6f0cfa1d7ba3de69f30d8e05cf957412fec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/7732e9e7017d751313811bd118de61302e9c8b35", - "reference": "7732e9e7017d751313811bd118de61302e9c8b35", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/23e7b6f0cfa1d7ba3de69f30d8e05cf957412fec", + "reference": "23e7b6f0cfa1d7ba3de69f30d8e05cf957412fec", "shasum": "" }, "require": { @@ -2475,7 +2475,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.38-dev" + "dev-master": "1.39-dev" } }, "autoload": { @@ -2513,7 +2513,7 @@ "keywords": [ "templating" ], - "time": "2019-03-23T14:27:19+00:00" + "time": "2019-04-16T17:12:57+00:00" }, { "name": "willdurand/negotiation", @@ -3172,16 +3172,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", "shasum": "" }, "require": { @@ -3216,7 +3216,7 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "time": "2019-04-07T13:18:21+00:00" }, { "name": "nette/bootstrap", @@ -3416,7 +3416,7 @@ "homepage": "https://nette.org/contributors" } ], - "description": "? Nette Finder: find files and directories with an intuitive API.", + "description": "🔍 Nette Finder: find files and directories with an intuitive API.", "homepage": "https://nette.org", "keywords": [ "filesystem", @@ -3476,7 +3476,7 @@ "homepage": "https://nette.org/contributors" } ], - "description": "? Nette NEON: encodes and decodes NEON file format.", + "description": "🍸 Nette NEON: encodes and decodes NEON file format.", "homepage": "http://ne-on.org", "keywords": [ "export", @@ -5228,16 +5228,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0" + "reference": "c09c18cca96d7067152f78956faf55346c338283" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c09c18cca96d7067152f78956faf55346c338283", + "reference": "c09c18cca96d7067152f78956faf55346c338283", "shasum": "" }, "require": { @@ -5281,11 +5281,11 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-04-07T09:56:43+00:00" }, { "name": "symfony/css-selector", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -5338,7 +5338,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -5395,16 +5395,16 @@ }, { "name": "symfony/finder", - "version": "v4.2.5", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a" + "reference": "e45135658bd6c14b61850bf131c4f09a55133f69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/267b7002c1b70ea80db0833c3afe05f0fbde580a", - "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a", + "url": "https://api.github.com/repos/symfony/finder/zipball/e45135658bd6c14b61850bf131c4f09a55133f69", + "reference": "e45135658bd6c14b61850bf131c4f09a55133f69", "shasum": "" }, "require": { @@ -5440,7 +5440,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:42:05+00:00" + "time": "2019-04-06T13:51:08+00:00" }, { "name": "theseer/tokenizer", @@ -5593,7 +5593,8 @@ "ext-mbstring": "*", "ext-openssl": "*", "ext-curl": "*", - "ext-zip": "*" + "ext-zip": "*", + "ext-dom": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/system/defines.php b/system/defines.php index 0acfafdcb..d9152c547 100644 --- a/system/defines.php +++ b/system/defines.php @@ -8,7 +8,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.6.5'); +define('GRAV_VERSION', '1.6.6'); define('GRAV_TESTING', false); define('DS', '/'); diff --git a/system/src/Grav/Common/Assets/BaseAsset.php b/system/src/Grav/Common/Assets/BaseAsset.php index 98f0f0560..49e38642e 100644 --- a/system/src/Grav/Common/Assets/BaseAsset.php +++ b/system/src/Grav/Common/Assets/BaseAsset.php @@ -78,6 +78,9 @@ abstract class BaseAsset extends PropertyObject } } + // Force priority to be an int + $this->priority = (int) $this->priority; + // Do some special stuff for CSS/JS (not inline) if (!Utils::startsWith($this->getType(), 'inline')) { $this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/'; diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index 969ad8135..8ff199bd9 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -154,6 +154,10 @@ class Validation protected static function filterText($value, array $params, array $field) { + if (!\is_string($value) && !is_numeric($value)) { + return ''; + } + if (!empty($params['trim'])) { $value = trim($value); } diff --git a/system/src/Grav/Common/Service/PagesServiceProvider.php b/system/src/Grav/Common/Service/PagesServiceProvider.php index 2310c0014..0a37e831d 100644 --- a/system/src/Grav/Common/Service/PagesServiceProvider.php +++ b/system/src/Grav/Common/Service/PagesServiceProvider.php @@ -80,8 +80,6 @@ class PagesServiceProvider implements ServiceProviderInterface } // Default route test and redirect if ($config->get('system.pages.redirect_default_route') && $page->route() !== $path) { - $uri->setUriProperties(['path' => $page->route()]); - $url = $uri->toOriginalString(); $c->redirect($url); } } diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index f266b5132..baced9abb 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -447,7 +447,11 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn */ public function containsFilter($haystack, $needle) { - return (strpos($haystack, $needle) !== false); + if (empty($needle)) { + return $haystack; + } + + return (strpos($haystack, (string) $needle) !== false); } /** diff --git a/system/src/Grav/Framework/Flex/Flex.php b/system/src/Grav/Framework/Flex/Flex.php index 8f47c46bf..8e71b2a84 100644 --- a/system/src/Grav/Framework/Flex/Flex.php +++ b/system/src/Grav/Framework/Flex/Flex.php @@ -168,6 +168,11 @@ class Flex implements \Countable $keyFieldFind = 'storage_key'; foreach ($keys as $flexKey) { + if (!$flexKey) { + continue; + } + + $flexKey = (string)$flexKey; // Normalize key and type using fallback to default type if it was set. [$key, $type, $guess] = $this->resolveKeyAndType($flexKey, $defaultType); diff --git a/system/src/Grav/Framework/Flex/FlexForm.php b/system/src/Grav/Framework/Flex/FlexForm.php index 2629448ef..e3b9b42fa 100644 --- a/system/src/Grav/Framework/Flex/FlexForm.php +++ b/system/src/Grav/Framework/Flex/FlexForm.php @@ -12,11 +12,15 @@ namespace Grav\Framework\Flex; use Grav\Common\Data\Blueprint; use Grav\Common\Data\Data; use Grav\Common\Grav; +use Grav\Common\Twig\Twig; use Grav\Common\Utils; use Grav\Framework\Flex\Interfaces\FlexFormInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Form\Traits\FormTrait; use Grav\Framework\Route\Route; +use Twig\Error\LoaderError; +use Twig\Error\SyntaxError; +use Twig\TemplateWrapper; /** * Class FlexForm @@ -45,6 +49,7 @@ class FlexForm implements FlexFormInterface { $this->name = $name; $this->form = $form; + $uniqueId = $object->exists() ? $object->getStorageKey() : "{$object->getFlexType()}:new"; $this->setObject($object); $this->setId($this->getName()); @@ -221,6 +226,42 @@ class FlexForm implements FlexFormInterface $this->doUnserialize($data); } + public function __get($name) + { + $method = "get{$name}"; + if (method_exists($this, $method)) { + return $this->{$method}(); + } + + $form = $this->getBlueprint()->form(); + + return $form[$name] ?? null; + } + + public function __set($name, $value) + { + $method = "set{$name}"; + if (method_exists($this, $method)) { + $this->{$method}($value); + } + } + + public function __isset($name) + { + $method = "get{$name}"; + if (method_exists($this, $method)) { + return true; + } + + $form = $this->getBlueprint()->form(); + + return isset($form[$name]); + } + + public function __unset($name) + { + } + /** * Note: this method clones the object. * @@ -234,6 +275,29 @@ class FlexForm implements FlexFormInterface return $this; } + /** + * @param string $layout + * @return TemplateWrapper + * @throws LoaderError + * @throws SyntaxError + */ + protected function getTemplate($layout) + { + $grav = Grav::instance(); + + /** @var Twig $twig */ + $twig = $grav['twig']; + + return $twig->twig()->resolveTemplate( + [ + "flex-objects/layouts/{$this->getFlexType()}/form/{$layout}.html.twig", + "flex-objects/layouts/_default/form/{$layout}.html.twig", + "forms/{$layout}/form.html.twig", + 'forms/default/form.html.twig' + ] + ); + } + /** * @param array $data * @param array $files @@ -243,6 +307,7 @@ class FlexForm implements FlexFormInterface { /** @var FlexObject $object */ $object = clone $this->getObject(); + $object->update($data, $files); $object->save(); diff --git a/system/src/Grav/Framework/Form/Interfaces/FormInterface.php b/system/src/Grav/Framework/Form/Interfaces/FormInterface.php index d957cb681..7bfd85fdc 100644 --- a/system/src/Grav/Framework/Form/Interfaces/FormInterface.php +++ b/system/src/Grav/Framework/Form/Interfaces/FormInterface.php @@ -11,6 +11,7 @@ namespace Grav\Framework\Form\Interfaces; use Grav\Common\Data\Blueprint; use Grav\Common\Data\Data; +use Grav\Framework\Interfaces\RenderInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UploadedFileInterface; @@ -18,7 +19,7 @@ use Psr\Http\Message\UploadedFileInterface; * Interface FormInterface * @package Grav\Framework\Form */ -interface FormInterface extends \Serializable +interface FormInterface extends RenderInterface, \Serializable { /** * Get HTML id="..." attribute. @@ -83,6 +84,13 @@ interface FormInterface extends \Serializable */ public function getNonce(): string; + /** + * Get task for the form if set in blueprints. + * + * @return string + */ + public function getTask(): string; + /** * Get form action (URL). If action is empty, it points to the current page. * diff --git a/system/src/Grav/Framework/Form/Traits/FormTrait.php b/system/src/Grav/Framework/Form/Traits/FormTrait.php index d46517742..41e22dd24 100644 --- a/system/src/Grav/Framework/Form/Traits/FormTrait.php +++ b/system/src/Grav/Framework/Form/Traits/FormTrait.php @@ -14,10 +14,15 @@ use Grav\Common\Data\Data; use Grav\Common\Data\ValidationException; use Grav\Common\Form\FormFlash; use Grav\Common\Grav; +use Grav\Common\Twig\Twig; use Grav\Common\Utils; +use Grav\Framework\ContentBlock\HtmlBlock; use Grav\Framework\Form\Interfaces\FormInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UploadedFileInterface; +use Twig\Error\LoaderError; +use Twig\Error\SyntaxError; +use Twig\TemplateWrapper; /** * Trait FormTrait @@ -94,6 +99,11 @@ trait FormTrait return ''; } + public function getTask(): string + { + return $this->getBlueprint()->get('form/task') ?? ''; + } + public function getData(string $name = null) { return null !== $name ? $this->data[$name] : $this->data; @@ -337,6 +347,30 @@ trait FormTrait return $this->flash; } + /** + * {@inheritdoc} + * @see FormInterface::render() + */ + public function render(string $layout = null, array $context = []) + { + if (null === $layout) { + $layout = 'default'; + } + + $grav = Grav::instance(); + + $block = HtmlBlock::create(); + $block->disableCache(); + + $output = $this->getTemplate($layout)->render( + ['grav' => $grav, 'block' => $block, 'form' => $this, 'layout' => $layout] + $context + ); + + $block->setContent($output); + + return $block; + } + protected function unsetFlash(): void { $this->flash = null; @@ -362,6 +396,27 @@ trait FormTrait $this->errors[] = $error; } + /** + * @param string $layout + * @return TemplateWrapper + * @throws LoaderError + * @throws SyntaxError + */ + protected function getTemplate($layout) + { + $grav = Grav::instance(); + + /** @var Twig $twig */ + $twig = $grav['twig']; + + return $twig->twig()->resolveTemplate( + [ + "forms/{$layout}/form.html.twig", + 'forms/default/form.html.twig' + ] + ); + } + /** * Parse PSR-7 ServerRequest into data and files. * diff --git a/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php index 1896f47a5..22008714b 100644 --- a/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php @@ -103,7 +103,7 @@ trait NestedPropertyTrait $current[$offset] = []; } } else { - throw new \RuntimeException('Cannot set nested property on non-array value'); + throw new \RuntimeException("Cannot set nested property {$property} on non-array value"); } $current = &$current[$offset]; @@ -147,7 +147,7 @@ trait NestedPropertyTrait return $this; } } else { - throw new \RuntimeException('Cannot set nested property on non-array value'); + throw new \RuntimeException("Cannot unset nested property {$property} on non-array value"); } $current = &$current[$offset];