header)) { $this->header = clone($this->header); } } /** * @return array */ public static function getCachedMethods(): array { return [ // Page Content Interface 'header' => false, 'summary' => true, 'content' => true, 'value' => false, 'media' => false, 'title' => true, 'menu' => true, 'visible' => true, 'published' => true, 'publishDate' => true, 'unpublishDate' => true, 'process' => true, 'slug' => true, 'order' => true, 'id' => true, 'modified' => true, 'lastModified' => true, 'folder' => true, 'date' => true, 'dateformat' => true, 'taxonomy' => true, 'shouldProcess' => true, 'isPage' => true, 'isDir' => true, 'folderExists' => true, // Page 'isPublished' => true, 'isOrdered' => true, 'isVisible' => true, 'isRoutable' => true, 'getCreated_Timestamp' => true, 'getPublish_Timestamp' => true, 'getUnpublish_Timestamp' => true, 'getUpdated_Timestamp' => true, ] + parent::getCachedMethods(); } /** * @param bool $test * @return bool */ public function isPublished(bool $test = true): bool { $time = time(); $start = $this->getPublish_Timestamp(); $stop = $this->getUnpublish_Timestamp(); return $this->published() && $start <= $time && (!$stop || $time <= $stop) === $test; } /** * @param bool $test * @return bool */ public function isOrdered(bool $test = true): bool { return ($this->order() !== false) === $test; } /** * @param bool $test * @return bool */ public function isVisible(bool $test = true): bool { return $this->visible() === $test; } /** * @param bool $test * @return bool */ public function isRoutable(bool $test = true): bool { return $this->routable() === $test; } /** * @return int */ public function getCreated_Timestamp(): int { return $this->getFieldTimestamp('created_date') ?? 0; } /** * @return int */ public function getPublish_Timestamp(): int { return $this->getFieldTimestamp('publish_date') ?? $this->getCreated_Timestamp(); } /** * @return int|null */ public function getUnpublish_Timestamp(): ?int { return $this->getFieldTimestamp('unpublish_date'); } /** * @return int */ public function getUpdated_Timestamp(): int { return $this->getFieldTimestamp('updated_date') ?? $this->getPublish_Timestamp(); } /** * @inheritdoc */ public function getFormValue(string $name, $default = null, string $separator = null) { $test = new stdClass(); $value = $this->pageContentValue($name, $test); if ($value !== $test) { return $value; } switch ($name) { case 'name': return $this->getProperty('template'); case 'route': return $this->hasKey() ? '/' . $this->getKey() : null; case 'header.permissions.groups': $encoded = json_encode($this->getPermissions()); if ($encoded === false) { throw new RuntimeException('json_encode(): failed to encode group permissions'); } return json_decode($encoded, true); } return parent::getFormValue($name, $default, $separator); } /** * Get master storage key. * * @return string * @see FlexObjectInterface::getStorageKey() */ public function getMasterKey(): string { $key = (string)($this->storage_key ?? $this->getMetaData()['storage_key'] ?? null); if (($pos = strpos($key, '|')) !== false) { $key = substr($key, 0, $pos); } return $key; } /** * {@inheritdoc} * @see FlexObjectInterface::getCacheKey() */ public function getCacheKey(): string { return $this->hasKey() ? $this->getTypePrefix() . $this->getFlexType() . '.' . $this->getKey() . '.' . $this->getLanguage() : ''; } /** * @param string|null $key * @return FlexObjectInterface */ public function createCopy(string $key = null) { $this->copy(); return parent::createCopy($key); } /** * @param array|bool $reorder * @return FlexObject|FlexObjectInterface */ public function save($reorder = true) { return parent::save(); } /** * Gets the Page Unmodified (original) version of the page. * * Assumes that object has been cloned before modifying it. * * @return FlexPageObject|null The original version of the page. */ public function getOriginal() { return $this->_originalObject; } /** * Store the Page Unmodified (original) version of the page. * * Can be called multiple times, only the first call matters. * * @return void */ public function storeOriginal(): void { if (null === $this->_originalObject) { $this->_originalObject = clone $this; } } /** * Get display order for the associated media. * * @return array */ public function getMediaOrder(): array { $order = $this->getNestedProperty('header.media_order'); if (is_array($order)) { return $order; } if (!$order) { return []; } return array_map('trim', explode(',', $order)); } // Overrides for header properties. /** * Common logic to load header properties. * * @param string $property * @param mixed $var * @param callable $filter * @return mixed|null */ protected function loadHeaderProperty(string $property, $var, callable $filter) { // We have to use parent methods in order to avoid loops. $value = null === $var ? parent::getProperty($property) : null; if (null === $value) { $value = $filter($var ?? $this->getProperty('header')->get($property)); parent::setProperty($property, $value); if ($this->doHasProperty($property)) { $value = parent::getProperty($property); } } return $value; } /** * Common logic to load header properties. * * @param string $property * @param mixed $var * @param callable $filter * @return mixed|null */ protected function loadProperty(string $property, $var, callable $filter) { // We have to use parent methods in order to avoid loops. $value = null === $var ? parent::getProperty($property) : null; if (null === $value) { $value = $filter($var); parent::setProperty($property, $value); if ($this->doHasProperty($property)) { $value = parent::getProperty($property); } } return $value; } /** * @param string $property * @param mixed $default * @return mixed */ public function getProperty($property, $default = null) { $method = static::$headerProperties[$property] ?? static::$calculatedProperties[$property] ?? null; if ($method && method_exists($this, $method)) { return $this->{$method}(); } return parent::getProperty($property, $default); } /** * @param string $property * @param mixed $value * @return $this */ public function setProperty($property, $value) { $method = static::$headerProperties[$property] ?? static::$calculatedProperties[$property] ?? null; if ($method && method_exists($this, $method)) { $this->{$method}($value); return $this; } parent::setProperty($property, $value); return $this; } /** * @param string $property * @param mixed $value * @param string|null $separator * @return $this */ public function setNestedProperty($property, $value, $separator = null) { $separator = $separator ?: '.'; if (strpos($property, 'header' . $separator) === 0) { $this->getProperty('header')->set(str_replace('header' . $separator, '', $property), $value, $separator); return $this; } parent::setNestedProperty($property, $value, $separator); return $this; } /** * @param string $property * @param string|null $separator * @return $this */ public function unsetNestedProperty($property, $separator = null) { $separator = $separator ?: '.'; if (strpos($property, 'header' . $separator) === 0) { $this->getProperty('header')->undef(str_replace('header' . $separator, '', $property), $separator); return $this; } parent::unsetNestedProperty($property, $separator); return $this; } /** * @param array $elements * @param bool $extended * @return void */ protected function filterElements(array &$elements, bool $extended = false): void { // Markdown storage conversion to page structure. if (array_key_exists('content', $elements)) { $elements['markdown'] = $elements['content']; unset($elements['content']); } if (!$extended) { $folder = !empty($elements['folder']) ? trim($elements['folder']) : ''; if ($folder) { $order = !empty($elements['order']) ? (int)$elements['order'] : null; // TODO: broken $elements['storage_key'] = $order ? sprintf('%02d.%s', $order, $folder) : $folder; } } parent::filterElements($elements); } /** * @param string $field * @return int|null */ protected function getFieldTimestamp(string $field): ?int { $date = $this->getFieldDateTime($field); return $date ? $date->getTimestamp() : null; } /** * @param string $field * @return DateTime|null */ protected function getFieldDateTime(string $field): ?DateTime { try { $value = $this->getProperty($field); if (is_numeric($value)) { $value = '@' . $value; } $date = $value ? new DateTime($value) : null; } catch (Exception $e) { /** @var Debugger $debugger */ $debugger = Grav::instance()['debugger']; $debugger->addException($e); $date = null; } return $date; } /** * @return UserCollectionInterface|null * @internal */ protected function loadAccounts() { return Grav::instance()['accounts'] ?? null; } }