diff --git a/CHANGELOG.md b/CHANGELOG.md index a40cb01f7..444c7f447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Added `Session::regenerateId()` method to properly prevent session fixation issues 1. [](#bugfix) * Fixed `validation: strict` not working in blueprints [#1273](https://github.com/getgrav/grav/issues/1273) + * Fixed Flex Pages having broken `isFirst()`, `isLast()`, `prevSibling()`, `nextSibling()` and `adjacentSibling()` # v1.7.0-rc.6 ## 02/11/2020 diff --git a/UPGRADE-1.7.md b/UPGRADE-1.7.md index 889a1b730..96f9e6d35 100644 --- a/UPGRADE-1.7.md +++ b/UPGRADE-1.7.md @@ -44,7 +44,7 @@ ## DEVELOPERS -### Composer / vendor +### Use composer autoloader * Please add `composer.json` file to your plugin and run `composer update --no-dev` (and remember to keep it updated): @@ -89,6 +89,8 @@ } ``` + See [Composer schema](https://getcomposer.org/doc/04-schema.md) + * Please use autoloader instead of `require` in the code: example.php @@ -145,13 +147,14 @@ ### Pages -* Added experimental support for `Flex Pages` (**Flex-Objects** plugin required) +* Added experimental support for `Flex Pages` * Added page specific permissions support for `Flex Pages` * Fixed wrong `Pages::dispatch()` calls (with redirect) when we really meant to call `Pages::find()` * Added `Pages::getCollection()` method * Moved `collection()` and `evaluate()` logic from `Page` class into `Pages` class * **DEPRECATED** `$page->modular()` in favor of `$page->isModule()` * **BC BREAK** Fixed `Page::modular()` and `Page::modularTwig()` returning `null` for folders and other non-initialized pages. Should not affect your code unless you were checking against `false` or `null`. +* If you're using Flex Pages in admin, collection will behave in slightly different way ### Users @@ -249,3 +252,7 @@ Please call `$grav['admin']->enablePages()` or `{% do admin.enablePages() %}` if you need to access frontend pages. This call can be safely made multiple times. If you're using `Flex Pages`, please use Flex Directory instead, it will make your code so much faster. + +* Admin now uses Flex for editing `Accounts` and `Pages`. If your plugin hooks into either of those, please make sure they still work. + +* Admin cache is enabled by default, make sure your plugin clears cache when needed. Please avoid clearing all cache! diff --git a/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php b/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php index 339468c50..af4726c8f 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php +++ b/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php @@ -16,6 +16,8 @@ use Grav\Common\File\CompiledJsonFile; use Grav\Common\Flex\Traits\FlexGravTrait; use Grav\Common\Flex\Traits\FlexIndexTrait; use Grav\Common\Grav; +use Grav\Common\Page\Interfaces\PageCollectionInterface; +use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Utils; use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; @@ -26,23 +28,11 @@ use Grav\Framework\Flex\Pages\FlexPageIndex; * Class GravPageObject * @package Grav\Plugin\FlexObjects\Types\GravPages * - * @method PageIndex dateRange($startDate, $endDate = false, $field = null) - * @method PageIndex visible() - * @method PageIndex nonVisible() - * @method PageIndex modular() - * @method PageIndex nonModular() - * @method PageIndex published() - * @method PageIndex nonPublished() - * @method PageIndex routable() - * @method PageIndex nonRoutable() - * @method PageIndex ofType(string $type) - * @method PageIndex ofOneOfTheseTypes(array $types) - * @method PageIndex ofOneOfTheseAccessLevels(array $accessLevels) * @method PageIndex withModules(bool $bool = true) * @method PageIndex withPages(bool $bool = true) * @method PageIndex withTranslation(bool $bool = true, string $languageCode = null, bool $fallback = null) */ -class PageIndex extends FlexPageIndex +class PageIndex extends FlexPageIndex implements PageCollectionInterface { use FlexGravTrait; use FlexIndexTrait; @@ -542,4 +532,378 @@ class PageIndex extends FlexPageIndex return date($dateFormat, $timestamp) ?: null; } + + /** + * Add a single page to a collection + * + * @param PageInterface $page + * + * @return PageCollection + */ + public function addPage(PageInterface $page) + { + return $this->getCollection()->addPage($page); + } + + /** + * + * Create a copy of this collection + * + * @return static + */ + public function copy() + { + return clone $this; + } + + /** + * + * Merge another collection with the current collection + * + * @param PageCollectionInterface $collection + * @return $this + */ + public function merge(PageCollectionInterface $collection) + { + return $this->getCollection()->merge($collection); + } + + + /** + * Intersect another collection with the current collection + * + * @param PageCollectionInterface $collection + * @return $this + */ + public function intersect(PageCollectionInterface $collection) + { + return $this->getCollection()->intersect($collection); + } + + /** + * Split collection into array of smaller collections. + * + * @param int $size + * @return PageCollectionInterface[] + */ + public function batch($size) + { + return $this->getCollection()->batch($size); + } + + /** + * Remove item from the list. + * + * @param PageInterface|string|null $key + * + * @return $this + * @throws \InvalidArgumentException + */ + public function remove($key = null) + { + return $this->getCollection()->remove($key); + } + + /** + * Reorder collection. + * + * @param string $by + * @param string $dir + * @param array $manual + * @param string $sort_flags + * + * @return PageCollectionInterface + */ + public function order($by, $dir = 'asc', $manual = null, $sort_flags = null) + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('order', [$by, $dir, $manual, $sort_flags]); + + return $collection; + } + + /** + * Check to see if this item is the first in the collection. + * + * @param string $path + * + * @return bool True if item is first. + */ + public function isFirst($path): bool + { + /** @var bool $result */ + $result = $this->__call('isFirst', [$path]); + + return $result; + + } + + /** + * Check to see if this item is the last in the collection. + * + * @param string $path + * + * @return bool True if item is last. + */ + public function isLast($path): bool + { + /** @var bool $result */ + $result = $this->__call('isLast', [$path]); + + return $result; + } + + /** + * Gets the previous sibling based on current position. + * + * @param string $path + * + * @return PageInterface|null The previous item. + */ + public function prevSibling($path) + { + /** @var PageInterface|null $result */ + $result = $this->__call('prevSibling', [$path]); + + return $result; + } + + /** + * Gets the next sibling based on current position. + * + * @param string $path + * + * @return PageInterface|null The next item. + */ + public function nextSibling($path) + { + /** @var PageInterface|null $result */ + $result = $this->__call('nextSibling', [$path]); + + return $result; + } + + /** + * Returns the adjacent sibling based on a direction. + * + * @param string $path + * @param int $direction either -1 or +1 + * + * @return PageInterface|PageCollectionInterface|false The sibling item. + */ + public function adjacentSibling($path, $direction = 1) + { + /** @var PageInterface|PageCollectionInterface|false $result */ + $result = $this->__call('adjacentSibling', [$path, $direction]); + + return $result; + } + + /** + * Returns the item in the current position. + * + * @param string $path the path the item + * + * @return int|null The index of the current page, null if not found. + */ + public function currentPosition($path): ?int + { + /** @var int|null $result */ + $result = $this->__call('currentPosition', [$path]); + + return $result; + } + + /** + * Returns the items between a set of date ranges of either the page date field (default) or + * an arbitrary datetime page field where end date is optional + * Dates can be passed in as text that strtotime() can process + * http://php.net/manual/en/function.strtotime.php + * + * @param string $startDate + * @param bool $endDate + * @param string|null $field + * + * @return PageCollectionInterface + * @throws \Exception + */ + public function dateRange($startDate, $endDate = false, $field = null) + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('dateRange', [$startDate, $endDate, $field]); + + return $collection; + } + + /** + * Creates new collection with only visible pages + * + * @return PageCollectionInterface The collection with only visible pages + */ + public function visible() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('visible', []); + + return $collection; + } + + /** + * Creates new collection with only non-visible pages + * + * @return PageCollectionInterface The collection with only non-visible pages + */ + public function nonVisible() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('nonVisible', []); + + return $collection; + } + + /** + * Creates new collection with only modular pages + * + * @return PageCollectionInterface The collection with only modular pages + */ + public function modular() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('modular', []); + + return $collection; + } + + /** + * Creates new collection with only non-modular pages + * + * @return PageCollectionInterface The collection with only non-modular pages + */ + public function nonModular() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('nonModular', []); + + return $collection; + + } + + /** + * Creates new collection with only published pages + * + * @return PageCollectionInterface The collection with only published pages + */ + public function published() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('published', []); + + return $collection; + } + + /** + * Creates new collection with only non-published pages + * + * @return PageCollectionInterface The collection with only non-published pages + */ + public function nonPublished() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('nonPublished', []); + + return $collection; + } + + /** + * Creates new collection with only routable pages + * + * @return PageCollectionInterface The collection with only routable pages + */ + public function routable() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('routable', []); + + return $collection; + } + + /** + * Creates new collection with only non-routable pages + * + * @return PageCollectionInterface The collection with only non-routable pages + */ + public function nonRoutable() + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('nonRoutable', []); + + return $collection; + } + + /** + * Creates new collection with only pages of the specified type + * + * @param string $type + * + * @return PageCollectionInterface The collection + */ + public function ofType($type) + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('ofType', []); + + return $collection; + } + + /** + * Creates new collection with only pages of one of the specified types + * + * @param string[] $types + * + * @return PageCollectionInterface The collection + */ + public function ofOneOfTheseTypes($types) + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('ofOneOfTheseTypes', []); + + return $collection; + } + + /** + * Creates new collection with only pages of one of the specified access levels + * + * @param array $accessLevels + * + * @return PageCollectionInterface The collection + */ + public function ofOneOfTheseAccessLevels($accessLevels) + { + /** @var PageCollectionInterface $collection */ + $collection = $this->__call('ofOneOfTheseAccessLevels', []); + + return $collection; + } + + /** + * Converts collection into an array. + * + * @return array + */ + public function toArray() + { + return $this->getCollection()->toArray(); + } + + /** + * Get the extended version of this Collection with each page keyed by route + * + * @return array + * @throws \Exception + */ + public function toExtendedArray() + { + return $this->getCollection()->toExtendedArray(); + } + } diff --git a/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php b/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php index 2551b76ae..2d692fccc 100644 --- a/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php @@ -58,7 +58,7 @@ interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable * Merge another collection with the current collection * * @param PageCollectionInterface $collection - * @return $this + * @return PageCollectionInterface */ public function merge(PageCollectionInterface $collection); @@ -66,7 +66,7 @@ interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable * Intersect another collection with the current collection * * @param PageCollectionInterface $collection - * @return $this + * @return PageCollectionInterface */ public function intersect(PageCollectionInterface $collection); @@ -83,7 +83,7 @@ interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable * * @param PageInterface|string|null $key * - * @return $this + * @return PageCollectionInterface * @throws \InvalidArgumentException */ //public function remove($key = null); @@ -96,7 +96,7 @@ interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable * @param array $manual * @param string $sort_flags * - * @return $this + * @return PageCollectionInterface */ public function order($by, $dir = 'asc', $manual = null, $sort_flags = null); @@ -165,7 +165,7 @@ interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable * @param bool $endDate * @param string|null $field * - * @return $this + * @return PageCollectionInterface * @throws \Exception */ public function dateRange($startDate, $endDate = false, $field = null);