/** * Takes an individual page and processes the taxonomies configured in its header. It * then adds those taxonomies to the map * * @param Page $page the page to process * @param array $page_taxonomy */ public function addTaxonomy(Page $page, $page_taxonomy = null) { if (!$page_taxonomy) { $page_taxonomy = $page->taxonomy(); } /** @var Config $config */ $config = $this->grav['config']; if ($config->get('site.taxonomies') && count($page_taxonomy) > 0) { foreach ((array) $config->get('site.taxonomies') as $taxonomy) { if (isset($page_taxonomy[$taxonomy])) { foreach ((array) $page_taxonomy[$taxonomy] as $item) { // TODO: move to pages class? $this->taxonomy_map[$taxonomy][(string) $item][$page->path()] = array('slug' => $page->slug()); } } } } }
/** * Takes an individual page and processes the taxonomies configured in its header. It * then adds those taxonomies to the map * * @param Page $page the page to process * @param array $page_taxonomy */ public function addTaxonomy(Page $page, $page_taxonomy = null) { if (!$page_taxonomy) { $page_taxonomy = $page->taxonomy(); } if (!$page->published() || empty($page_taxonomy)) { return; } /** @var Config $config */ $config = $this->grav['config']; if ($config->get('site.taxonomies')) { foreach ((array) $config->get('site.taxonomies') as $taxonomy) { if (isset($page_taxonomy[$taxonomy])) { foreach ((array) $page_taxonomy[$taxonomy] as $item) { $this->taxonomy_map[$taxonomy][(string) $item][$page->path()] = ['slug' => $page->slug()]; } } } } }
/** * Gets and Sets the parent object for this page * * @param Page $var the parent page object * @return Page|null the parent page object if it exists. */ public function parent(Page $var = null) { if ($var) { $this->parent = $var->path(); return $var; } /** @var Pages $pages */ $pages = self::getGrav()['pages']; return $pages->get($this->parent); }
/** * Recursive function to load & build page relationships. * * @param string $directory * @param Page|null $parent * @return Page * @throws \RuntimeException * @internal */ protected function recurse($directory, Page &$parent = null) { $directory = rtrim($directory, DS); $iterator = new \DirectoryIterator($directory); $page = new Page(); /** @var Config $config */ $config = $this->grav['config']; $page->path($directory); if ($parent) { $page->parent($parent); } $page->orderDir($config->get('system.pages.order.dir')); $page->orderBy($config->get('system.pages.order.by')); // Add into instances if (!isset($this->instances[$page->path()])) { $this->instances[$page->path()] = $page; if ($parent && $page->path()) { $this->children[$parent->path()][$page->path()] = array('slug' => $page->slug()); } } else { throw new \RuntimeException('Fatal error when creating page instances.'); } // set current modified of page $last_modified = $page->modified(); // flat for content availability $content_exists = false; /** @var \DirectoryIterator $file */ foreach ($iterator as $file) { if ($file->isDot()) { continue; } $name = $file->getFilename(); if ($file->isFile()) { // Update the last modified if it's newer than already found if ($file->getBasename() !== '.DS_Store' && ($modified = $file->getMTime()) > $last_modified) { $last_modified = $modified; } if (preg_match('/^[^.].*' . CONTENT_EXT . '$/', $name)) { $page->init($file); $content_exists = true; if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onPageProcessed', new Event(['page' => $page])); } } } elseif ($file->isDir()) { if (!$page->path()) { $page->path($file->getPath()); } $path = $directory . DS . $name; $child = $this->recurse($path, $page); if (Utils::startsWith($name, '_')) { $child->routable(false); } $this->children[$page->path()][$child->path()] = array('slug' => $child->slug()); if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } } } // Set routability to false if no page found if (!$content_exists) { $page->routable(false); } // Override the modified and ID so that it takes the latest change into account $page->modified($last_modified); $page->id($last_modified . md5($page->filePath())); // Sort based on Defaults or Page Overridden sort order $this->children[$page->path()] = $this->sort($page); return $page; }
/** * Recursive function to load & build page relationships. * * @param string $directory * @param Page|null $parent * @return Page * @throws \RuntimeException * @internal */ protected function recurse($directory, Page &$parent = null) { $directory = rtrim($directory, DS); $page = new Page(); /** @var Config $config */ $config = $this->grav['config']; /** @var Language $language */ $language = $this->grav['language']; // stuff to do at root page if ($parent === null) { // Fire event for memory and time consuming plugins... if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onBuildPagesInitialized'); } } $page->path($directory); if ($parent) { $page->parent($parent); } $page->orderDir($config->get('system.pages.order.dir')); $page->orderBy($config->get('system.pages.order.by')); // Add into instances if (!isset($this->instances[$page->path()])) { $this->instances[$page->path()] = $page; if ($parent && $page->path()) { $this->children[$parent->path()][$page->path()] = array('slug' => $page->slug()); } } else { throw new \RuntimeException('Fatal error when creating page instances.'); } $content_exists = false; $pages_found = glob($directory . '/*' . CONTENT_EXT); $page_extensions = $language->getFallbackPageExtensions(); if ($pages_found) { foreach ($page_extensions as $extension) { foreach ($pages_found as $found) { if (preg_match('/^.*\\/[0-9A-Za-z\\-\\_]+(' . $extension . ')$/', $found)) { $page_found = $found; $page_extension = $extension; break 2; } } } } if ($parent && !empty($page_found)) { $file = new \SplFileInfo($page_found); $page->init($file, $page_extension); $content_exists = true; if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onPageProcessed', new Event(['page' => $page])); } } // set current modified of page $last_modified = $page->modified(); /** @var \DirectoryIterator $file */ foreach (new \FilesystemIterator($directory) as $file) { $name = $file->getFilename(); // Ignore all hidden files if set. if ($this->ignore_hidden) { if ($name && $name[0] == '.') { continue; } } if ($file->isFile()) { // Update the last modified if it's newer than already found if (!in_array($file->getBasename(), $this->ignore_files) && ($modified = $file->getMTime()) > $last_modified) { $last_modified = $modified; } } elseif ($file->isDir() && !in_array($file->getFilename(), $this->ignore_folders)) { if (!$page->path()) { $page->path($file->getPath()); } $path = $directory . DS . $name; $child = $this->recurse($path, $page); if (Utils::startsWith($name, '_')) { $child->routable(false); } $this->children[$page->path()][$child->path()] = array('slug' => $child->slug()); if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } } } // Set routability to false if no page found if (!$content_exists) { $page->routable(false); } // Override the modified and ID so that it takes the latest change into account $page->modified($last_modified); $page->id($last_modified . md5($page->filePath())); // Sort based on Defaults or Page Overridden sort order $this->children[$page->path()] = $this->sort($page); return $page; }
/** * Add a single page to a collection * * @param Page $page * * @return $this */ public function addPage(Page $page) { $this->items[$page->path()] = ['slug' => $page->slug()]; return $this; }
/** * Twig process that renders a page item. It supports two variations: * 1) Handles modular pages by rendering a specific page based on its modular twig template * 2) Renders individual page items for twig processing before the site rendering * * @param Page $item The page item to render * @param string $content Optional content override * @return string The rendered output * @throws \Twig_Error_Loader */ public function processPage(Page $item, $content = null) { $content = $content !== null ? $content : $item->content(); // override the twig header vars for local resolution $this->grav->fireEvent('onTwigPageVariables'); $twig_vars = $this->twig_vars; $twig_vars['page'] = $item; $twig_vars['media'] = $item->media(); $twig_vars['header'] = $item->header(); $local_twig = clone $this->twig; $modular_twig = $item->modularTwig(); $process_twig = isset($item->header()->process['twig']) ? $item->header()->process['twig'] : false; try { // Process Modular Twig if ($modular_twig) { $twig_vars['content'] = $content; $template = $item->template() . TEMPLATE_EXT; $output = $content = $local_twig->render($template, $twig_vars); } // Process in-page Twig if (!$modular_twig || $modular_twig && $process_twig) { $name = '@Page:' . $item->path(); $this->setTemplate($name, $content); $output = $local_twig->render($name, $twig_vars); } } catch (\Twig_Error_Loader $e) { throw new \RuntimeException($e->getRawMessage(), 404, $e); } return $output; }
/** * Converts links from absolute '/' or relative (../..) to a grav friendly format * * @param $page the current page to use as reference * @param string $markdown_url the URL as it was written in the markdown * * @return string the more friendly formatted url */ public static function convertUrl(Page $page, $markdown_url) { $grav = Grav::instance(); $pages_dir = $grav['locator']->findResource('page://'); $base_url = rtrim($grav['base_url'] . $grav['pages']->base(), '/'); // if absolute and starts with a base_url move on if (pathinfo($markdown_url, PATHINFO_DIRNAME) == '.' && $page->url() == '/') { return '/' . $markdown_url; // no path to convert } elseif ($base_url != '' && Utils::startsWith($markdown_url, $base_url)) { return $markdown_url; // if contains only a fragment } elseif (Utils::startsWith($markdown_url, '#')) { return $markdown_url; } else { $target = null; // see if page is relative to this or absolute if (Utils::startsWith($markdown_url, '/')) { $normalized_url = Utils::normalizePath($base_url . $markdown_url); $normalized_path = Utils::normalizePath($pages_dir . $markdown_url); } else { $normalized_url = $base_url . Utils::normalizePath($page->route() . '/' . $markdown_url); $normalized_path = Utils::normalizePath($page->path() . '/' . $markdown_url); } // special check to see if path checking is required. $just_path = str_replace($normalized_url, '', $normalized_path); if ($just_path == $page->path()) { return $normalized_url; } $url_bits = parse_url($normalized_path); $full_path = $url_bits['path']; if (file_exists($full_path)) { // do nothing } elseif (file_exists(urldecode($full_path))) { $full_path = urldecode($full_path); } else { return $normalized_url; } $path_info = pathinfo($full_path); $page_path = $path_info['dirname']; $filename = ''; if ($markdown_url == '..') { $page_path = $full_path; } else { // save the filename if a file is part of the path if (is_file($full_path)) { if ($path_info['extension'] != 'md') { $filename = '/' . $path_info['basename']; } } else { $page_path = $full_path; } } // get page instances and try to find one that fits $instances = $grav['pages']->instances(); if (isset($instances[$page_path])) { $target = $instances[$page_path]; $url_bits['path'] = $base_url . $target->route() . $filename; return Uri::buildUrl($url_bits); } return $normalized_url; } }
/** * Recursive function to load & build page relationships. * * @param string $directory * @param null $parent * @return Page * @throws \RuntimeException * @internal */ protected function recurse($directory = PAGES_DIR, Page &$parent = null) { $directory = rtrim($directory, DS); $iterator = new \DirectoryIterator($directory); $page = new Page(); $config = $this->grav['config']; $page->path($directory); if ($parent) { $page->parent($parent); } $page->orderDir($config->get('system.pages.order.dir')); $page->orderBy($config->get('system.pages.order.by')); // Add into instances if (!isset($this->instances[$page->path()])) { $this->instances[$page->path()] = $page; if ($parent && $page->path()) { $this->children[$parent->path()][$page->path()] = array('slug' => $page->slug()); } } else { throw new \RuntimeException('Fatal error when creating page instances.'); } $last_modified = 0; /** @var \DirectoryIterator $file */ foreach ($iterator as $file) { $name = $file->getFilename(); $date = $file->getMTime(); if ($date > $last_modified) { $last_modified = $date; } if ($file->isFile() && Utils::endsWith($name, CONTENT_EXT)) { $page->init($file); if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onPageProcessed', new Event(['page' => $page])); } } elseif ($file->isDir() && !$file->isDot()) { if (!$page->path()) { $page->path($file->getPath()); } $path = $directory . DS . $name; $child = $this->recurse($path, $page); if (Utils::startsWith($name, '_')) { $child->routable(false); } $this->children[$page->path()][$child->path()] = array('slug' => $child->slug()); // set the modified time if not already set if (!$page->date()) { $page->date($file->getMTime()); } // set the last modified time on pages $this->lastModified($file->getMTime()); if ($config->get('system.pages.events.page')) { $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } } } // Override the modified and ID so that it takes the latest change // into account $page->modified($last_modified); $page->id($last_modified . md5($page->filePath())); // Sort based on Defaults or Page Overridden sort order $this->children[$page->path()] = $this->sort($page); return $page; }
/** * Checks if a page has already been compiled yet. * * @param Page $page The page to check * @return boolean Returns true if page has already been * compiled yet, false otherwise */ protected function compileOnce(Page $page) { static $processed = []; $id = md5($page->path()); // Make sure that contents is only processed once if (!isset($processed[$id]) || $processed[$id] < $page->modified()) { $processed[$id] = $page->modified(); return true; } return false; }
/** * Twig process that renders a page item. It supports two variations: * 1) Handles modular pages by rendering a specific page based on its modular twig template * 2) Renders individual page items for twig processing before the site rendering * * @param Page $item The page item to render * @param string $content Optional content override * @return string The rendered output * @throws \Twig_Error_Loader */ public function processPage(Page $item, $content = null) { $content = $content !== null ? $content : $item->content(); // override the twig header vars for local resolution $this->grav->fireEvent('onTwigPageVariables'); $twig_vars = $this->twig_vars; $twig_vars['page'] = $item; $twig_vars['media'] = $item->media(); $twig_vars['header'] = $item->header(); $local_twig = clone $this->twig; // Get Twig template layout if ($item->modularTwig()) { $twig_vars['content'] = $content; $template = $item->template() . TEMPLATE_EXT; $output = $local_twig->render($template, $twig_vars); } else { $name = '@Page:' . $item->path(); $this->setTemplate($name, $content); $output = $local_twig->render($name, $twig_vars); } return $output; }