1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201 |
- <?php
- declare(strict_types=1);
- /**
- * @package Grav\Common\Flex
- *
- * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
- * @license MIT License; see LICENSE file for details.
- */
- namespace Grav\Common\Flex\Types\Pages;
- use Exception;
- use Grav\Common\Debugger;
- use Grav\Common\File\CompiledJsonFile;
- use Grav\Common\File\CompiledYamlFile;
- use Grav\Common\Flex\Traits\FlexGravTrait;
- use Grav\Common\Flex\Traits\FlexIndexTrait;
- use Grav\Common\Grav;
- use Grav\Common\Language\Language;
- use Grav\Common\Page\Header;
- use Grav\Common\Page\Interfaces\PageCollectionInterface;
- use Grav\Common\Page\Interfaces\PageInterface;
- use Grav\Common\User\Interfaces\UserInterface;
- use Grav\Common\Utils;
- use Grav\Framework\Flex\FlexDirectory;
- use Grav\Framework\Flex\Interfaces\FlexStorageInterface;
- use Grav\Framework\Flex\Pages\FlexPageIndex;
- use InvalidArgumentException;
- use RuntimeException;
- use function array_slice;
- use function count;
- use function in_array;
- use function is_array;
- use function is_string;
- /**
- * Class GravPageObject
- * @package Grav\Plugin\FlexObjects\Types\GravPages
- *
- * @template T of PageObject
- * @template C of PageCollection
- * @extends FlexPageIndex<T,C>
- * @implements PageCollectionInterface<string,T>
- *
- * @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 implements PageCollectionInterface
- {
- use FlexGravTrait;
- use FlexIndexTrait;
- public const VERSION = parent::VERSION . '.5';
- public const ORDER_LIST_REGEX = '/(\/\d+)\.[^\/]+/u';
- public const PAGE_ROUTE_REGEX = '/\/\d+\./u';
- /** @var PageObject|array */
- protected $_root;
- /** @var array|null */
- protected $_params;
- /**
- * @param array $entries
- * @param FlexDirectory|null $directory
- */
- public function __construct(array $entries = [], FlexDirectory $directory = null)
- {
- // Remove root if it's taken.
- if (isset($entries[''])) {
- $this->_root = $entries[''];
- unset($entries['']);
- }
- parent::__construct($entries, $directory);
- }
- /**
- * @param FlexStorageInterface $storage
- * @return array
- */
- public static function loadEntriesFromStorage(FlexStorageInterface $storage): array
- {
- // Load saved index.
- $index = static::loadIndex($storage);
- $version = $index['version'] ?? 0;
- $force = static::VERSION !== $version;
- // TODO: Following check flex index to be out of sync after some saves, disabled until better solution is found.
- //$timestamp = $index['timestamp'] ?? 0;
- //if (!$force && $timestamp && $timestamp > time() - 1) {
- // return $index['index'];
- //}
- // Load up to date index.
- $entries = parent::loadEntriesFromStorage($storage);
- return static::updateIndexFile($storage, $index['index'], $entries, ['include_missing' => true, 'force_update' => $force]);
- }
- /**
- * @param string $key
- * @return PageObject|null
- * @phpstan-return T|null
- */
- public function get($key)
- {
- if (mb_strpos($key, '|') !== false) {
- [$key, $params] = explode('|', $key, 2);
- }
- $element = parent::get($key);
- if (null === $element) {
- return null;
- }
- if (isset($params)) {
- $element = $element->getTranslation(ltrim($params, '.'));
- }
- \assert(null === $element || $element instanceof PageObject);
- return $element;
- }
- /**
- * @return PageInterface
- */
- public function getRoot()
- {
- $root = $this->_root;
- if (is_array($root)) {
- $directory = $this->getFlexDirectory();
- $storage = $directory->getStorage();
- $defaults = [
- 'header' => [
- 'routable' => false,
- 'permissions' => [
- 'inherit' => false
- ]
- ]
- ];
- $row = $storage->readRows(['' => null])[''] ?? null;
- if (null !== $row) {
- if (isset($row['__ERROR'])) {
- /** @var Debugger $debugger */
- $debugger = Grav::instance()['debugger'];
- $message = sprintf('Flex Pages: root page is broken in storage: %s', $row['__ERROR']);
- $debugger->addException(new RuntimeException($message));
- $debugger->addMessage($message, 'error');
- $row = ['__META' => $root];
- }
- } else {
- $row = ['__META' => $root];
- }
- $row = array_merge_recursive($defaults, $row);
- /** @var PageObject $root */
- $root = $this->getFlexDirectory()->createObject($row, '/', false);
- $root->name('root.md');
- $root->root(true);
- $this->_root = $root;
- }
- return $root;
- }
- /**
- * @param string|null $languageCode
- * @param bool|null $fallback
- * @return static
- * @phpstan-return static<T,C>
- */
- public function withTranslated(string $languageCode = null, bool $fallback = null)
- {
- if (null === $languageCode) {
- return $this;
- }
- $entries = $this->translateEntries($this->getEntries(), $languageCode, $fallback);
- $params = ['language' => $languageCode, 'language_fallback' => $fallback] + $this->getParams();
- return $this->createFrom($entries)->setParams($params);
- }
- /**
- * @return string|null
- */
- public function getLanguage(): ?string
- {
- return $this->_params['language'] ?? null;
- }
- /**
- * Get the collection params
- *
- * @return array
- */
- public function getParams(): array
- {
- return $this->_params ?? [];
- }
- /**
- * Get the collection param
- *
- * @param string $name
- * @return mixed
- */
- public function getParam(string $name)
- {
- return $this->_params[$name] ?? null;
- }
- /**
- * Set parameters to the Collection
- *
- * @param array $params
- * @return $this
- */
- public function setParams(array $params)
- {
- $this->_params = $this->_params ? array_merge($this->_params, $params) : $params;
- return $this;
- }
- /**
- * Set a parameter to the Collection
- *
- * @param string $name
- * @param mixed $value
- * @return $this
- */
- public function setParam(string $name, $value)
- {
- $this->_params[$name] = $value;
- return $this;
- }
- /**
- * Get the collection params
- *
- * @return array
- */
- public function params(): array
- {
- return $this->getParams();
- }
- /**
- * {@inheritdoc}
- * @see FlexCollectionInterface::getCacheKey()
- */
- public function getCacheKey(): string
- {
- return $this->getTypePrefix() . $this->getFlexType() . '.' . sha1(json_encode($this->getKeys()) . $this->getKeyField() . $this->getLanguage());
- }
- /**
- * Filter pages by given filters.
- *
- * - search: string
- * - page_type: string|string[]
- * - modular: bool
- * - visible: bool
- * - routable: bool
- * - published: bool
- * - page: bool
- * - translated: bool
- *
- * @param array $filters
- * @param bool $recursive
- * @return static
- * @phpstan-return static<T,C>
- */
- public function filterBy(array $filters, bool $recursive = false)
- {
- if (!$filters) {
- return $this;
- }
- if ($recursive) {
- return $this->__call('filterBy', [$filters, true]);
- }
- $list = [];
- $index = $this;
- foreach ($filters as $key => $value) {
- switch ($key) {
- case 'search':
- $index = $index->search((string)$value);
- break;
- case 'page_type':
- if (!is_array($value)) {
- $value = is_string($value) && $value !== '' ? explode(',', $value) : [];
- }
- $index = $index->ofOneOfTheseTypes($value);
- break;
- case 'routable':
- $index = $index->withRoutable((bool)$value);
- break;
- case 'published':
- $index = $index->withPublished((bool)$value);
- break;
- case 'visible':
- $index = $index->withVisible((bool)$value);
- break;
- case 'module':
- $index = $index->withModules((bool)$value);
- break;
- case 'page':
- $index = $index->withPages((bool)$value);
- break;
- case 'folder':
- $index = $index->withPages(!$value);
- break;
- case 'translated':
- $index = $index->withTranslation((bool)$value);
- break;
- default:
- $list[$key] = $value;
- }
- }
- return $list ? $index->filterByParent($list) : $index;
- }
- /**
- * @param array $filters
- * @return static
- * @phpstan-return static<T,C>
- */
- protected function filterByParent(array $filters)
- {
- /** @var static $index */
- $index = parent::filterBy($filters);
- return $index;
- }
- /**
- * @param array $options
- * @return array
- */
- public function getLevelListing(array $options): array
- {
- // Undocumented B/C
- $order = $options['order'] ?? 'asc';
- if ($order === SORT_ASC) {
- $options['order'] = 'asc';
- } elseif ($order === SORT_DESC) {
- $options['order'] = 'desc';
- }
- $options += [
- 'field' => null,
- 'route' => null,
- 'leaf_route' => null,
- 'sortby' => null,
- 'order' => 'asc',
- 'lang' => null,
- 'filters' => [],
- ];
- $options['filters'] += [
- 'type' => ['root', 'dir'],
- ];
- $key = 'page.idx.lev.' . sha1(json_encode($options, JSON_THROW_ON_ERROR) . $this->getCacheKey());
- $checksum = $this->getCacheChecksum();
- $cache = $this->getCache('object');
- /** @var Debugger $debugger */
- $debugger = Grav::instance()['debugger'];
- $result = null;
- try {
- $cached = $cache->get($key);
- $test = $cached[0] ?? null;
- $result = $test === $checksum ? ($cached[1] ?? null) : null;
- } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
- $debugger->addException($e);
- }
- try {
- if (null === $result) {
- $result = $this->getLevelListingRecurse($options);
- $cache->set($key, [$checksum, $result]);
- }
- } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
- $debugger->addException($e);
- }
- return $result;
- }
- /**
- * @param array $entries
- * @param string|null $keyField
- * @return static
- * @phpstan-return static<T,C>
- */
- protected function createFrom(array $entries, string $keyField = null)
- {
- /** @var static $index */
- $index = parent::createFrom($entries, $keyField);
- $index->_root = $this->getRoot();
- return $index;
- }
- /**
- * @param array $entries
- * @param string $lang
- * @param bool|null $fallback
- * @return array
- */
- protected function translateEntries(array $entries, string $lang, bool $fallback = null): array
- {
- $languages = $this->getFallbackLanguages($lang, $fallback);
- foreach ($entries as $key => &$entry) {
- // Find out which version of the page we should load.
- $translations = $this->getLanguageTemplates((string)$key);
- if (!$translations) {
- // No translations found, is this a folder?
- continue;
- }
- // Find a translation.
- $template = null;
- foreach ($languages as $code) {
- if (isset($translations[$code])) {
- $template = $translations[$code];
- break;
- }
- }
- // We couldn't find a translation, remove entry from the list.
- if (!isset($code, $template)) {
- unset($entries['key']);
- continue;
- }
- // Get the main key without template and langauge.
- [$main_key,] = explode('|', $entry['storage_key'] . '|', 2);
- // Update storage key and language.
- $entry['storage_key'] = $main_key . '|' . $template . '.' . $code;
- $entry['lang'] = $code;
- }
- unset($entry);
- return $entries;
- }
- /**
- * @return array
- */
- protected function getLanguageTemplates(string $key): array
- {
- $meta = $this->getMetaData($key);
- $template = $meta['template'] ?? 'folder';
- $translations = $meta['markdown'] ?? [];
- $list = [];
- foreach ($translations as $code => $search) {
- if (isset($search[$template])) {
- // Use main template if possible.
- $list[$code] = $template;
- } elseif (!empty($search)) {
- // Fall back to first matching template.
- $list[$code] = key($search);
- }
- }
- return $list;
- }
- /**
- * @param string|null $languageCode
- * @param bool|null $fallback
- * @return array
- */
- protected function getFallbackLanguages(string $languageCode = null, bool $fallback = null): array
- {
- $fallback = $fallback ?? true;
- if (!$fallback && null !== $languageCode) {
- return [$languageCode];
- }
- $grav = Grav::instance();
- /** @var Language $language */
- $language = $grav['language'];
- $languageCode = $languageCode ?? '';
- if ($languageCode === '' && $fallback) {
- return $language->getFallbackLanguages(null, true);
- }
- return $fallback ? $language->getFallbackLanguages($languageCode, true) : [$languageCode];
- }
- /**
- * @param array $options
- * @return array
- */
- protected function getLevelListingRecurse(array $options): array
- {
- $filters = $options['filters'] ?? [];
- $field = $options['field'];
- $route = $options['route'];
- $leaf_route = $options['leaf_route'];
- $sortby = $options['sortby'];
- $order = $options['order'];
- $language = $options['lang'];
- $status = 'error';
- $msg = null;
- $response = [];
- $children = null;
- $sub_route = null;
- $extra = null;
- // Handle leaf_route
- $leaf = null;
- if ($leaf_route && $route !== $leaf_route) {
- $nodes = explode('/', $leaf_route);
- $sub_route = '/' . implode('/', array_slice($nodes, 1, $options['level']++));
- $options['route'] = $sub_route;
- [$status,,$leaf,$extra] = $this->getLevelListingRecurse($options);
- }
- // Handle no route, assume page tree root
- if (!$route) {
- $page = $this->getRoot();
- } else {
- $page = $this->get(trim($route, '/'));
- }
- $path = $page ? $page->path() : null;
- if ($field) {
- // Get forced filters from the field.
- $blueprint = $page ? $page->getBlueprint() : $this->getFlexDirectory()->getBlueprint();
- $settings = $blueprint->schema()->getProperty($field);
- $filters = array_merge([], $filters, $settings['filters'] ?? []);
- }
- // Clean up filter.
- $filter_type = (array)($filters['type'] ?? []);
- unset($filters['type']);
- $filters = array_filter($filters, static function($val) { return $val !== null && $val !== ''; });
- if ($page) {
- $status = 'success';
- $msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND';
- if ($page->root() && (!$filter_type || in_array('root', $filter_type, true))) {
- if ($field) {
- $response[] = [
- 'name' => '<root>',
- 'value' => '/',
- 'item-key' => '',
- 'filename' => '.',
- 'extension' => '',
- 'type' => 'root',
- 'modified' => $page->modified(),
- 'size' => 0,
- 'symlink' => false,
- 'has-children' => false
- ];
- } else {
- $response[] = [
- 'item-key' => '-root-',
- 'icon' => 'root',
- 'title' => 'Root', // FIXME
- 'route' => [
- 'display' => '<root>', // FIXME
- 'raw' => '_root',
- ],
- 'modified' => $page->modified(),
- 'extras' => [
- 'template' => $page->template(),
- //'lang' => null,
- //'translated' => null,
- 'langs' => [],
- 'published' => false,
- 'visible' => false,
- 'routable' => false,
- 'tags' => ['root', 'non-routable'],
- 'actions' => ['edit'], // FIXME
- ]
- ];
- }
- }
- /** @var PageCollection|PageIndex $children */
- $children = $page->children();
- /** @var PageIndex $children */
- $children = $children->getIndex();
- $selectedChildren = $children->filterBy($filters, true);
- /** @var Header $header */
- $header = $page->header();
- if (!$field && $header->get('admin.children_display_order') === 'collection' && ($orderby = $header->get('content.order.by'))) {
- // Use custom sorting by page header.
- $sortby = $orderby;
- $order = $header->get('content.order.dir', $order);
- $custom = $header->get('content.order.custom');
- }
- if ($sortby) {
- // Sort children.
- $selectedChildren = $selectedChildren->order($sortby, $order, $custom ?? null);
- }
- /** @var UserInterface|null $user */
- $user = Grav::instance()['user'] ?? null;
- /** @var PageObject $child */
- foreach ($selectedChildren as $child) {
- $selected = $child->path() === $extra;
- $includeChildren = is_array($leaf) && !empty($leaf) && $selected;
- if ($field) {
- $child_count = count($child->children());
- $payload = [
- 'name' => $child->menu(),
- 'value' => $child->rawRoute(),
- 'item-key' => Utils::basename($child->rawRoute() ?? ''),
- 'filename' => $child->folder(),
- 'extension' => $child->extension(),
- 'type' => 'dir',
- 'modified' => $child->modified(),
- 'size' => $child_count,
- 'symlink' => false,
- 'has-children' => $child_count > 0
- ];
- } else {
- $lang = $child->findTranslation($language) ?? 'n/a';
- /** @var PageObject $child */
- $child = $child->getTranslation($language) ?? $child;
- // TODO: all these features are independent from each other, we cannot just have one icon/color to catch all.
- // TODO: maybe icon by home/modular/page/folder (or even from blueprints) and color by visibility etc..
- if ($child->home()) {
- $icon = 'home';
- } elseif ($child->isModule()) {
- $icon = 'modular';
- } elseif ($child->visible()) {
- $icon = 'visible';
- } elseif ($child->isPage()) {
- $icon = 'page';
- } else {
- // TODO: add support
- $icon = 'folder';
- }
- $tags = [
- $child->published() ? 'published' : 'non-published',
- $child->visible() ? 'visible' : 'non-visible',
- $child->routable() ? 'routable' : 'non-routable'
- ];
- $extras = [
- 'template' => $child->template(),
- 'lang' => $lang ?: null,
- 'translated' => $lang ? $child->hasTranslation($language, false) : null,
- 'langs' => $child->getAllLanguages(true) ?: null,
- 'published' => $child->published(),
- 'published_date' => $this->jsDate($child->publishDate()),
- 'unpublished_date' => $this->jsDate($child->unpublishDate()),
- 'visible' => $child->visible(),
- 'routable' => $child->routable(),
- 'tags' => $tags,
- 'actions' => $this->getListingActions($child, $user),
- ];
- $extras = array_filter($extras, static function ($v) {
- return $v !== null;
- });
- /** @var PageIndex $tmp */
- $tmp = $child->children()->getIndex();
- $child_count = $tmp->count();
- $count = $filters ? $tmp->filterBy($filters, true)->count() : null;
- $route = $child->getRoute();
- $route = $route ? ($route->toString(false) ?: '/') : '';
- $payload = [
- 'item-key' => htmlspecialchars(Utils::basename($child->rawRoute() ?? $child->getKey())),
- 'icon' => $icon,
- 'title' => htmlspecialchars($child->menu()),
- 'route' => [
- 'display' => htmlspecialchars($route) ?: null,
- 'raw' => htmlspecialchars($child->rawRoute()),
- ],
- 'modified' => $this->jsDate($child->modified()),
- 'child_count' => $child_count ?: null,
- 'count' => $count ?? null,
- 'filters_hit' => $filters ? ($child->filterBy($filters, false) ?: null) : null,
- 'extras' => $extras
- ];
- $payload = array_filter($payload, static function ($v) {
- return $v !== null;
- });
- }
- // Add children if any
- if ($includeChildren) {
- $payload['children'] = array_values($leaf);
- }
- $response[] = $payload;
- }
- } else {
- $msg = 'PLUGIN_ADMIN.PAGE_ROUTE_NOT_FOUND';
- }
- if ($field) {
- $temp_array = [];
- foreach ($response as $index => $item) {
- $temp_array[$item['type']][$index] = $item;
- }
- $sorted = Utils::sortArrayByArray($temp_array, $filter_type);
- $response = Utils::arrayFlatten($sorted);
- }
- return [$status, $msg, $response, $path];
- }
- /**
- * @param PageObject $object
- * @param UserInterface $user
- * @return array
- */
- protected function getListingActions(PageObject $object, UserInterface $user): array
- {
- $actions = [];
- if ($object->isAuthorized('read', null, $user)) {
- $actions[] = 'preview';
- $actions[] = 'edit';
- }
- if ($object->isAuthorized('update', null, $user)) {
- $actions[] = 'copy';
- $actions[] = 'move';
- }
- if ($object->isAuthorized('delete', null, $user)) {
- $actions[] = 'delete';
- }
- return $actions;
- }
- /**
- * @param FlexStorageInterface $storage
- * @return CompiledJsonFile|CompiledYamlFile|null
- */
- protected static function getIndexFile(FlexStorageInterface $storage)
- {
- if (!method_exists($storage, 'isIndexed') || !$storage->isIndexed()) {
- return null;
- }
- // Load saved index file.
- $grav = Grav::instance();
- $locator = $grav['locator'];
- $filename = $locator->findResource('user-data://flex/indexes/pages.json', true, true);
- return CompiledJsonFile::instance($filename);
- }
- /**
- * @param int|null $timestamp
- * @return string|null
- */
- private function jsDate(int $timestamp = null): ?string
- {
- if (!$timestamp) {
- return null;
- }
- $config = Grav::instance()['config'];
- $dateFormat = $config->get('system.pages.dateformat.long');
- return date($dateFormat, $timestamp) ?: null;
- }
- /**
- * Add a single page to a collection
- *
- * @param PageInterface $page
- * @return PageCollection
- * @phpstan-return C
- */
- public function addPage(PageInterface $page)
- {
- return $this->getCollection()->addPage($page);
- }
- /**
- *
- * Create a copy of this collection
- *
- * @return static
- * @phpstan-return static<T,C>
- */
- public function copy()
- {
- return clone $this;
- }
- /**
- *
- * Merge another collection with the current collection
- *
- * @param PageCollectionInterface $collection
- * @return PageCollection
- * @phpstan-return C
- */
- public function merge(PageCollectionInterface $collection)
- {
- return $this->getCollection()->merge($collection);
- }
- /**
- * Intersect another collection with the current collection
- *
- * @param PageCollectionInterface $collection
- * @return PageCollection
- * @phpstan-return C
- */
- public function intersect(PageCollectionInterface $collection)
- {
- return $this->getCollection()->intersect($collection);
- }
- /**
- * Split collection into array of smaller collections.
- *
- * @param int $size
- * @return PageCollection[]
- * @phpstan-return C[]
- */
- public function batch($size)
- {
- return $this->getCollection()->batch($size);
- }
- /**
- * Remove item from the list.
- *
- * @param string $key
- * @return PageObject|null
- * @phpstan-return T|null
- * @throws InvalidArgumentException
- */
- public function remove($key)
- {
- return $this->getCollection()->remove($key);
- }
- /**
- * Reorder collection.
- *
- * @param string $by
- * @param string $dir
- * @param array $manual
- * @param string $sort_flags
- * @return static
- * @phpstan-return static<T,C>
- */
- 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 PageObject|null The previous item.
- * @phpstan-return T|null
- */
- public function prevSibling($path)
- {
- /** @var PageObject|null $result */
- $result = $this->__call('prevSibling', [$path]);
- return $result;
- }
- /**
- * Gets the next sibling based on current position.
- *
- * @param string $path
- * @return PageObject|null The next item.
- * @phpstan-return T|null
- */
- public function nextSibling($path)
- {
- /** @var PageObject|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 PageObject|false The sibling item.
- * @phpstan-return T|false
- */
- public function adjacentSibling($path, $direction = 1)
- {
- /** @var PageObject|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 start date and end date are optional
- * Dates must be passed in as text that strtotime() can process
- * http://php.net/manual/en/function.strtotime.php
- *
- * @param string|null $startDate
- * @param string|null $endDate
- * @param string|null $field
- * @return static
- * @phpstan-return static<T,C>
- * @throws Exception
- */
- public function dateRange($startDate = null, $endDate = null, $field = null)
- {
- $collection = $this->__call('dateRange', [$startDate, $endDate, $field]);
- return $collection;
- }
- /**
- * Mimicks Pages class.
- *
- * @return $this
- * @deprecated 1.7 Not needed anymore in Flex Pages (does nothing).
- */
- public function all()
- {
- return $this;
- }
- /**
- * Creates new collection with only visible pages
- *
- * @return static The collection with only visible pages
- * @phpstan-return static<T,C>
- */
- public function visible()
- {
- $collection = $this->__call('visible', []);
- return $collection;
- }
- /**
- * Creates new collection with only non-visible pages
- *
- * @return static The collection with only non-visible pages
- * @phpstan-return static<T,C>
- */
- public function nonVisible()
- {
- $collection = $this->__call('nonVisible', []);
- return $collection;
- }
- /**
- * Creates new collection with only non-modular pages
- *
- * @return static The collection with only non-modular pages
- * @phpstan-return static<T,C>
- */
- public function pages()
- {
- $collection = $this->__call('pages', []);
- return $collection;
- }
- /**
- * Creates new collection with only modular pages
- *
- * @return static The collection with only modular pages
- * @phpstan-return static<T,C>
- */
- public function modules()
- {
- $collection = $this->__call('modules', []);
- return $collection;
- }
- /**
- * Creates new collection with only modular pages
- *
- * @return static The collection with only modular pages
- * @phpstan-return static<T,C>
- */
- public function modular()
- {
- return $this->modules();
- }
- /**
- * Creates new collection with only non-modular pages
- *
- * @return static The collection with only non-modular pages
- * @phpstan-return static<T,C>
- */
- public function nonModular()
- {
- return $this->pages();
- }
- /**
- * Creates new collection with only published pages
- *
- * @return static The collection with only published pages
- * @phpstan-return static<T,C>
- */
- public function published()
- {
- $collection = $this->__call('published', []);
- return $collection;
- }
- /**
- * Creates new collection with only non-published pages
- *
- * @return static The collection with only non-published pages
- * @phpstan-return static<T,C>
- */
- public function nonPublished()
- {
- $collection = $this->__call('nonPublished', []);
- return $collection;
- }
- /**
- * Creates new collection with only routable pages
- *
- * @return static The collection with only routable pages
- * @phpstan-return static<T,C>
- */
- public function routable()
- {
- $collection = $this->__call('routable', []);
- return $collection;
- }
- /**
- * Creates new collection with only non-routable pages
- *
- * @return static The collection with only non-routable pages
- * @phpstan-return static<T,C>
- */
- public function nonRoutable()
- {
- $collection = $this->__call('nonRoutable', []);
- return $collection;
- }
- /**
- * Creates new collection with only pages of the specified type
- *
- * @param string $type
- * @return static The collection
- * @phpstan-return static<T,C>
- */
- public function ofType($type)
- {
- $collection = $this->__call('ofType', []);
- return $collection;
- }
- /**
- * Creates new collection with only pages of one of the specified types
- *
- * @param string[] $types
- * @return static The collection
- * @phpstan-return static<T,C>
- */
- public function ofOneOfTheseTypes($types)
- {
- $collection = $this->__call('ofOneOfTheseTypes', []);
- return $collection;
- }
- /**
- * Creates new collection with only pages of one of the specified access levels
- *
- * @param array $accessLevels
- * @return static The collection
- * @phpstan-return static<T,C>
- */
- public function ofOneOfTheseAccessLevels($accessLevels)
- {
- $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();
- }
- }
|