Shared = $Shared; $this->data = array_merge(array( Fields::HIDDEN => false, Fields::PRIVATE => false, Fields::LEVEL => 0, Fields::ORIG_URL => '', Fields::PARENT => '', Fields::PATH => '', Fields::TEMPLATE => '', Fields::URL => '', Fields::PAGE_INDEX => '' ), $data); $this->tags = $this->extractTags(); $this->hidden = &$this->data[Fields::HIDDEN]; $this->private = &$this->data[Fields::PRIVATE]; $this->level = &$this->data[Fields::LEVEL]; $this->origUrl = &$this->data[Fields::ORIG_URL]; $this->parentUrl = &$this->data[Fields::PARENT]; $this->path = &$this->data[Fields::PATH]; $this->template = &$this->data[Fields::TEMPLATE]; $this->url = &$this->data[Fields::URL]; $this->index = &$this->data[Fields::PAGE_INDEX]; } /** * Add page. * * @param Page $Parent * @param string $title * @param string $themeTemplate * @param bool $isPrivate * @return string the dashboard URL to the new page */ public static function add(Page $Parent, string $title, string $themeTemplate, bool $isPrivate): string { $theme = dirname($themeTemplate); $template = basename($themeTemplate); // Save new subpage below the current page's path. $subdir = Str::slug($title, true, AM_DIRNAME_MAX_LEN); // Add trailing slash. $subdir .= '/'; // Build path. $newPagePath = $Parent->path . $subdir; $suffix = FileSystem::uniquePathSuffix($newPagePath); $newPagePath = FileSystem::appendSuffixToPath($newPagePath, $suffix); // Data, also directly append possibly existing suffix to title here. $data = array( Fields::TITLE => $title . ucwords(str_replace('-', ' ', $suffix)), Fields::PRIVATE => $isPrivate, Fields::TEMPLATE => $template ); if ($theme != '.') { $data[Fields::THEME] = $theme; } // Set date. $now = date(Page::DATE_FORMAT); $data[Fields::DATE] = $now; $data[Fields::TIME_CREATED] = $now; $data[Fields::TIME_LAST_MODIFIED] = $now; // Save data and add index. DataFile::write($data, $newPagePath); PageIndex::append($Parent->path, $newPagePath); return Page::dashboardUrlByPath($newPagePath); } /** * Return updated view URL based on $path. * * @param string $path * @return string The view URL to the new page */ public static function dashboardUrlByPath(string $path): string { Cache::clear(); $Page = Page::findByPath($path); if (!$Page) { return ''; } return 'page?url=' . urlencode($Page->origUrl); } /** * Delete page. * * @return bool true on success */ public function delete(): bool { PageIndex::remove(dirname($this->path), $this->path); return (bool) FileSystem::movePageDir( $this->path, Page::TRASH_DIRECTORY, basename($this->path) ); } /** * Duplicate a page. * * @return string the new URL */ public function duplicate(): string { $duplicatePath = $this->path; $suffix = FileSystem::uniquePathSuffix($duplicatePath, '-copy'); $duplicatePath = FileSystem::appendSuffixToPath($duplicatePath, $suffix); FileSystem::copyPageFiles($this->path, $duplicatePath); Page::appendSuffixToTitle($duplicatePath, $suffix); PageIndex::append(dirname($duplicatePath), $duplicatePath); return Page::dashboardUrlByPath($duplicatePath); } /** * Find a page by its path. * * @param string $path * @return Page|null */ public static function findByPath(string $path): ?Page { $Automad = Automad::fromCache(); foreach ($Automad->getCollection() as $Page) { if ($Page->path == $path) { return $Page; } } return null; } /** * Get a page from the cache. In case the cache is outdated, create a new Automad object first. * * @param string $url * @return Page|null */ public static function fromCache(string $url): ?Page { $Automad = Automad::fromCache(); return $Automad->getPage($url); } /** * Create a new Page object by loading data from the data file of the given path. * * @param string $path * @param string $url * @param string $index * @param Shared $Shared * @param string $parentUrl * @param int $level * @return ?Page */ public static function fromDataFile( string $path, string $url, string $index, Shared $Shared, string $parentUrl, int $level, ): ?Page { $data = DataFile::read($path); if (empty($data)) { return null; } if (array_key_exists(Fields::PRIVATE, $data)) { $data[Fields::PRIVATE] = ($data[Fields::PRIVATE] && $data[Fields::PRIVATE] !== 'false'); } else { $data[Fields::PRIVATE] = false; } if (!array_key_exists(Fields::TITLE, $data) || ($data[Fields::TITLE] == '')) { if (trim($url, '/')) { $data[Fields::TITLE] = ucwords(str_replace(array('_', '-'), ' ', basename($url))); } else { $data[Fields::TITLE] = $Shared->get(Fields::SITENAME); } } if (empty($data[Fields::URL])) { $data[Fields::URL] = $url; } if (array_key_exists(Fields::HIDDEN, $data)) { $data[Fields::HIDDEN] = ($data[Fields::HIDDEN] && $data[Fields::HIDDEN] !== 'false'); } else { $data[Fields::HIDDEN] = false; } $data[Fields::ORIG_URL] = $url; $data[Fields::PAGE_INDEX] = $index; $data[Fields::PATH] = $path; $data[Fields::LEVEL] = $level; $data[Fields::PARENT] = $parentUrl; $data[Fields::TEMPLATE_LEGACY] = $data[Fields::TEMPLATE] ?? ''; return new Page($data, $Shared); } /** * Return requested data - from the page data array, from the shared data array or as generated system variable. * * The local page data array gets used as first and the shared data array gets used as second source for the requested variable. * That way it is possible to override a shared data value on a per page basis. * Note that not all data is stored in the data arrays. * Some data (:basename ...) should only be generated when requested out of performance reasons. * * @param string $field * @param bool $returnEditorObject * @return object|string The requested value * @psalm-return ($returnEditorObject is true ? object : string) */ public function get(string $field, bool $returnEditorObject = false): object|string { if ($returnEditorObject) { if (array_key_exists($field, $this->data)) { return Value::asEditorObject($this->data[$field]); } return Value::asEditorObject($this->Shared->data[$field] ?? null); } // Return as string value from the data array. if (array_key_exists($field, $this->data)) { return Value::asString($this->data[$field]); } // Return value from the Shared data array. if ($this->Shared && array_key_exists($field, $this->Shared->data)) { return Value::asString($this->Shared->data[$field]); } // Generate system variable value or return an empty string. switch ($field) { case Fields::CURRENT_PAGE: return $this->isCurrent() ? 'true' : ''; case Fields::CURRENT_PATH: return $this->isInCurrentPath() ? 'true' : ''; case Fields::BASENAME: return basename($this->path); default: return ''; } } /** * Return the full file system path of a page's data file. * * @return string The full file system path */ public function getFile(): string { return DataFile::getFile($this); } /** * Return the template of the page. * * @return string The full file system path of the template file. */ public function getTemplate(): string { $packages = AM_BASE_DIR . AM_DIR_PACKAGES . '/'; $templatePath = $packages . $this->get(Fields::THEME) . '/' . $this->template . '.php'; if (file_exists($templatePath)) { return $templatePath; } return $packages . Page::TEMPLATE_FILE_DEFAULT; } /** * Check if page is the current page. * * @return bool true if the the page is the currently requested page */ public function isCurrent(): bool { return (AM_REQUEST == $this->origUrl); } /** * Check if the page URL is a part the current page's URL. * * @return bool true if the the page is a parent of the currently requested page or the requeste page itself */ public function isInCurrentPath(): bool { return (strpos(AM_REQUEST, rtrim($this->origUrl, '/') . '/') === 0 || $this->origUrl == AM_REQUEST); } /** * Move a page directory and update all related links. * * @param string $destParentPath * @param string $slug * @param array|null $layout * @return string the new page path */ public function moveDirAndUpdateLinks(string $destParentPath, string $slug, ?array $layout = null): string { $oldPath = $this->path; $newPath = FileSystem::movePageDir( $this->path, $destParentPath, $slug ); Debug::log(array($oldPath, $newPath, $layout)); if (dirname($oldPath) !== dirname($newPath)) { if ($layout) { $index = array_search($oldPath, $layout); if ($index !== false) { $layout[$index] = $newPath; } PageIndex::write($destParentPath, $layout); } else { PageIndex::append($destParentPath, $newPath); } PageIndex::remove(dirname($oldPath), $oldPath); } else { PageIndex::replace($destParentPath, $oldPath, $newPath); } $this->updatePageLinks($newPath); return $newPath; } /** * Save page data. * * @param string $url * @param array $data * @param string $themeTemplate * @param string $slug * @return array|bool a data array in case the page was moved or its privacy has changed */ public function save(string $url, array $data, string $themeTemplate, string $slug): array|bool { $data[Fields::TITLE] = $data[Fields::TITLE] ?? $slug; $theme = dirname($themeTemplate); $template = basename($themeTemplate); $data[Fields::TEMPLATE] = $template; if ($theme != '.') { $data[Fields::THEME] = $theme; } if (!empty($data[Fields::PRIVATE])) { $data[Fields::PRIVATE] = true; $private = true; } else { $private = false; } $now = date(Page::DATE_FORMAT); $data[Fields::TIME_CREATED] = $this->data[Fields::TIME_CREATED] ?? $now; $data[Fields::TIME_LAST_MODIFIED] = $now; DataFile::write($data, $this->path); $History = History::get($this->path); $History->commit($data); $newSlug = $slug; if ($url != '/' && $data[Fields::TITLE]) { $newSlug = Page::updateSlug( $this->get(Fields::TITLE), $data[Fields::TITLE], $slug ); $newPagePath = $this->moveDirAndUpdateLinks( dirname($this->path), $newSlug ); $newSlug = basename($newPagePath); } else { $newPagePath = '/'; } $newTheme = ''; if (isset($data[Fields::THEME])) { $newTheme = $data[Fields::THEME]; } $currentTheme = ''; if (isset($this->data[Fields::THEME])) { $currentTheme = $this->data[Fields::THEME]; } Cache::clear(); if ($currentTheme != $newTheme || $this->template != $template) { return array( 'redirect' => Page::dashboardUrlByPath($newPagePath) ); } if ($this->path != $newPagePath || $data[Fields::TITLE] != $this->data[Fields::TITLE] || $newSlug != $slug || $private != $this->private ) { Cache::clear(); $Page = Page::findByPath($newPagePath); if ($Page) { $newOrigUrl = $Page->origUrl; $newUrl = $newOrigUrl; if (!empty($data[Fields::URL])) { $newUrl = $data[Fields::URL]; } return array( 'slug' => $newSlug, 'url' => $newUrl, 'path' => $newPagePath, 'origUrl' => $newOrigUrl ); } } return false; } /** * Set a page data value. * * @param string $field * @param string $value */ public function set(string $field, string $value): void { $this->data[$field] = $value; } /** * Create an empty undefined Page object. * * @return Page */ public static function undefined(): Page { return new Page(array(), null); } /** * Open a data text file under the given path, read the data, * append a suffix to the title variable and write back the data. * * @param string $path * @param string $suffix */ private static function appendSuffixToTitle(string $path, string $suffix): void { if ($suffix) { $data = DataFile::read($path); $data[Fields::TITLE] = $data[Fields::TITLE] ?? basename($path); $data[Fields::TITLE] .= ucwords(str_replace('-', ' ', $suffix)); DataFile::write($data, $path); } } /** * Extracts the tags string out of a given array and returns an array with these tags. * * @return array $tags */ private function extractTags(): array { $tags = array(); if (isset($this->data[Fields::TAGS])) { // All tags are splitted into an array $tags = explode(Parse::STRING_SEPARATOR, $this->data[Fields::TAGS]); // Trim & strip tags $tags = array_map(function (string $tag) { return trim(Str::stripTags($tag)); }, $tags); } return $tags; } /** * Update all file and page links based on a new path. * * @param string $newPath * @return bool true on success */ private function updatePageLinks(string $newPath): bool { Cache::clear(); $Automad = Automad::fromCache(); $oldUrl = $this->origUrl; $oldPath = $this->path; if ($oldPath == $newPath) { return false; } $Page = Page::findByPath($newPath); if (!$Page) { return false; } $newUrl = $Page->origUrl; $replace = array( rtrim(AM_DIR_PAGES . $oldPath, '/') => rtrim(AM_DIR_PAGES . $newPath, '/'), $oldUrl => $newUrl ); foreach ($replace as $old => $new) { Links::update($Automad, $old, $new); } Cache::clear(); return true; } /** * Update slug in case it is not a custom one and just represents a sanitized version of the title. * * @param string $currentTitle * @param string $newTitle * @param string $slug * @return string the updated directory name slug */ private static function updateSlug(string $currentTitle, string $newTitle, string $slug): string { if (strlen($slug) === 0 || $slug === Str::slug($currentTitle, true, AM_DIRNAME_MAX_LEN)) { return Str::slug($newTitle); } return Str::slug($slug); } } __halt_compiler();----SIGNATURE:----1EUT8lL9HUi6/Ika9LnorZg2csx4/hIGLNatzYe/swwFq8S0bu5Qb0BkHEqO12JJEB7PcX5sKzCpApZcKPWh9kyhdBaLpo3XAsvb2MC33tkNIneBRxiOVWTugL6S+cVbuWvkb/rSpvD1V5Qe8Ubo8LUhKmrx5GdrwFen0RpYmL0b2ho12uytVgYnTOK7zITI2P4QOWibaLkvUQsh0NnJpqu6kgdHlZVASRg4bOzME+oD6fGPUv70wSoIRy1HX+mkmzZ2GnZB3agXEXu7S0BHje79xvBqtxCHVd7yVgf+tSx9awSidkypzq7K8aA7+Zrq1AtaZJtxtItWCnh8htyIAFPMK10L0DdUdDydT76Bf6S+NagZa1n7tUyTDIw4yCL//wtfAWbwG2f9R/eO/Vk0bYz7fJghAVtkf0MDmMP0Xe7unJTVgZo6oIDUR0w2KzsIY/kiWsJDSf9r3YiaVaY3Y0MnqB68XnxNk2G84y+JlRgtVu63fFJDx+GFexb1UqquIHEJcn2WhheuKbJ4auVfDTohnOKIHee5oSNNzzhG6vaf4RcD8H/f0vYItNBDi9gWEQjETRRV2iQJFLqI9tUJGI3yUxgquuMLIJsia9dA/CtII5Rd1oK1YTITUlrLiuT8GXDL/ehP1as0c4skcwgoEHDli/DT6YPe+O9tFtMOrSc=----ATTACHMENT:----NjM5NDgxOTkxMjQyNTY5OSA3NTU3OTUwMjM5MTM0ODg4IDU5OTA1NzU0MjQ2NDE3MzE=