public function testMerge() { $pc = new Configuration(array('site' => array('title' => "Untitled", 'root' => "/"))); $this->assertEquals("Untitled", $pc->getValue('site/title')); $this->assertEquals("/", $pc->getValue('site/root')); $this->assertEquals(null, $pc->getValue('site/other')); $this->assertEquals(null, $pc->getValue('foo/bar')); $this->assertEquals(null, $pc->getValue('simple')); $pc->merge(array('site' => array('title' => "Merged Title", 'root' => "http://root", 'other' => "Something"), 'foo' => array('bar' => "FOO BAR!"), 'simple' => "simple value")); $this->assertEquals("Merged Title", $pc->getValue('site/title')); $this->assertEquals("http://root", $pc->getValue('site/root')); $this->assertEquals("Something", $pc->getValue('site/other')); $this->assertEquals("FOO BAR!", $pc->getValue('foo/bar')); $this->assertEquals("simple value", $pc->getValue('simple')); }
public function __construct(IPage $page, array $config = null, $validate = true) { $this->page = $page; // This needs to be set first because if $validate is 'true', // we'll need access to the page's PieCrust application for // validating configuration values. parent::__construct($config, $validate); }
protected function convertPage($relative, $outputPath, $isTemplate = false) { $this->logger->debug("Converting {$relative}"); $pieCrustRelative = PieCrustHelper::getRelativePath($this->pieCrust, $outputPath); $this->logger->debug(" -> {$pieCrustRelative}"); $this->modified[$pieCrustRelative] = array(); $absolute = $this->rootDir . $relative; $contents = file_get_contents($absolute); $wrapContentTag = true; $header = Configuration::parseHeader($contents); $text = substr($contents, $header->textOffset); $textBeforeConversion = $text; if ($isTemplate) { $config = $header->config; if (isset($config['layout'])) { // Liquid doesn't support template inheritance, // but Twig does. $text = "{% extends '{$config['layout']}.html' %}\n\n" . "{% block jekyllcontent %}\n" . $text . "\n" . "{% endblock %}\n"; $wrapContentTag = false; $this->modified[$pieCrustRelative]['layout_extends'] = true; } } else { // Convert the config. $config = $header->config; if (isset($config['layout'])) { // PieCrust uses 'none' instead of 'nil'. if ($config['layout'] == 'nil') { $config['layout'] = 'none'; } } // PieCrust defines everything in the config header, // including the format of the text. $pathinfo = pathinfo($relative); if ($pathinfo['extension'] != 'html') { $config['format'] = $pathinfo['extension']; } else { $config['format'] = 'none'; } } // Convert the template stuff we can: // - content tag may have to be wrapped in a `jekyllcontent` // because Jekyll uses implicit layout inheritance // placements. if ($wrapContentTag) { $text = preg_replace('/{{\\s*content\\s*}}/', '{% block jekyllcontent %}{{ content }}{% endblock %}', $text); } // - list of posts $text = preg_replace('/(?<=\\{%|{)([^\\}]*)site.posts/', '\\1blog.posts', $text); $text = preg_replace('/(?<=\\{%|{)([^\\}]*)paginator.posts/', '\\1pagination.posts', $text); // - list of categories or tags $text = preg_replace('/(?<=\\{%|{)([^\\}]*)site.categories/', '\\1blog.categories', $text); $text = preg_replace('/(?<=\\{%|{)([^\\}]*)site.tags/', '\\1blog.tags', $text); // - list of related posts $text = preg_replace('/(?<=\\{%|{)(?<!%\\})site.related_posts/', '\\1pagination.related_posts', $text); // - enumeration limits $text = preg_replace('/{%\\s*for\\s+([^}]+)\\s+limit\\:\\s*(\\d+)/', '{% for \\1 | slice(0, \\2)', $text); $text = preg_replace('/{%\\s*for\\s+([^}]+)\\s+offset\\:\\s*(\\d+)/', '{% for \\1 | slice(\\2)', $text); // - code highlighting $text = preg_replace('/{%\\s*highlight\\s+([\\w\\d]+)\\s*%}/', '{% geshi \'\\1\' %}', $text); $text = preg_replace('/{%\\s*endhighlight\\s*%}/', '{% endgeshi %}', $text); // - unless tag $text = preg_replace('/{%\\s*unless\\s+([^}]+)\\s*%}/', '{% if not \\1 %}', $text); $text = preg_replace('/{%\\s*endunless\\s*%}/', '{% endif %}', $text); // - variable assignment $text = preg_replace('/\\{%\\s*assign\\s+/', '{% set ', $text); // - include tag $text = preg_replace('/\\{%\\s*include\\s+([\\w\\d\\.\\-_]+)\\s*%}/', '{% include "\\1" %}', $text); // - truncate filter $text = preg_replace('/\\|\\s*truncate\\:\\s*(\\d+)/', '|slice(0, \\1)', $text); // - date filter $text = preg_replace('/\\|\\s*date\\:\\s*"([^"]+)"/', '|date("\\1")', $text); // - some filters we don't need $text = preg_replace('/\\|\\s*date_to_string/', '', $text); // Create the destination directory if needed. if (!is_dir(dirname($outputPath))) { mkdir(dirname($outputPath), 0755, true); } // Create a backup file if we converted a lot of stuff. if ($text != $textBeforeConversion) { $this->modified[$pieCrustRelative]['liquid_to_twig'] = true; // Add a backup of the original content. $backupPath = $outputPath . '.original'; file_put_contents($backupPath, $contents); } // Save the converted contents. $convertedContents = ''; if (!$isTemplate and count($config) > 0) { $convertedContents .= "---\n"; $convertedContents .= Yaml::dump($config, 3); $convertedContents .= "---\n"; } $convertedContents .= $text; file_put_contents($outputPath, $convertedContents); }
/** * Gets the page's data for page rendering. * * It's better to call IPage::getData, which calls this function, because it * will also cache the results. It's useful for example when pagination * results needs to be re-used. */ public static function getPageData(IPage $page) { $pieCrust = $page->getApp(); $paginator = new Paginator($page); $assetor = new Assetor($page); $linker = new Linker($page); if ($page->getPaginationDataSource() != null) { $paginator->setPaginationDataSource($page->getPaginationDataSource()); } $data = array('page' => $page->getConfig()->get(), 'asset' => $assetor, 'pagination' => $paginator, 'link' => $linker); $data['page']['url'] = PieCrustHelper::formatUri($pieCrust, $page->getUri()); $data['page']['slug'] = $page->getUri(); $data['page']['timestamp'] = $page->getDate(); $dateFormat = PageHelper::getConfigValueUnchecked($page, 'date_format', $page->getConfig()->getValueUnchecked('blog')); $data['page']['date'] = date($dateFormat, $page->getDate()); switch ($page->getPageType()) { case IPage::TYPE_TAG: if (is_array($page->getPageKey())) { $data['tag'] = implode(' + ', $page->getPageKey()); } else { $data['tag'] = $page->getPageKey(); } break; case IPage::TYPE_CATEGORY: $data['category'] = $page->getPageKey(); break; } $extraData = $page->getExtraPageData(); if ($extraData) { if (is_array($extraData)) { $data = Configuration::mergeArrays($data, $extraData); } else { $data['extra'] = $extraData; } } return $data; }
protected function loadUnsafe() { if ($this->wasCached()) { // Get the page from the cache. $configText = $this->cache->read($this->page->getUri(), 'json'); $config = json_decode($configText, true); $this->page->getConfig()->set($config, false); // false = No need to validate this. if (!$this->page->getConfig()->hasValue('segments')) { throw new PieCrustException("Can't get segments list from cache."); } $contents = array(); foreach ($config['segments'] as $key) { $contents[$key] = json_decode($this->cache->read($this->page->getUri(), $key . '.json'), true); } return $contents; } else { // Load the page from disk. $rawContents = file_get_contents($this->page->getPath()); $parsedContents = Configuration::parseHeader($rawContents); // Set the configuration. $config = $this->page->getConfig(); $config->set($parsedContents['config']); // Set the raw content with the unparsed content segments. $rawSegmentsOffset = $parsedContents['text_offset']; $contents = $this->parseContentSegments($rawContents, $rawSegmentsOffset); // Add the list of known segments to the configuration. foreach ($contents as $key => $segment) { $config->appendValue('segments', $key); } // Cache that shit out. if ($this->cache != null) { $yamlMarkup = json_encode($config->get()); $this->cache->write($this->page->getUri(), 'json', $yamlMarkup); $keys = $config['segments']; foreach ($keys as $key) { $this->cache->write($this->page->getUri() . '.' . $key, 'json', json_encode($contents[$key])); } } return $contents; } }
/** * Gets the page's data for page rendering. * * It's better to call IPage::getData, which calls this function, because it * will also cache the results. It's useful for example when pagination * results needs to be re-used. */ public static function getPageData(IPage $page) { $pieCrust = $page->getApp(); $paginator = new Paginator($page); $assetor = new Assetor($page); $linker = new Linker($page); $recursiveLinker = new RecursiveLinkerIterator($linker); if ($page->getPaginationDataSource() != null) { $paginator->setPaginationDataSource($page->getPaginationDataSource()); } $data = array('page' => $page->getConfig()->get(), 'assets' => $assetor, 'pagination' => $paginator, 'siblings' => $linker, 'family' => $recursiveLinker); $data['page']['url'] = PieCrustHelper::formatUri($pieCrust, $page->getUri()); $data['page']['slug'] = $page->getUri(); $data['page']['timestamp'] = $page->getDate(true); $dateFormat = PageHelper::getConfigValueUnchecked($page, 'date_format', $page->getConfig()->getValueUnchecked('blog')); $data['page']['date'] = date($dateFormat, $page->getDate(true)); switch ($page->getPageType()) { case IPage::TYPE_TAG: if (is_array($page->getPageKey())) { $data['tag'] = implode(' + ', $page->getPageKey()); } else { $data['tag'] = $page->getPageKey(); } if (strpos($data['tag'], '-') >= 0) { // The tag may have been slugified. Let's cheat a bit by looking at // the first tag that matches in the first pagination post, and // using that instead. $paginationPosts = $paginator->posts(); if (count($paginationPosts) > 0) { $firstPost = $paginationPosts[0]; $firstPostTags = $firstPost['tags']; if (!is_array($firstPostTags)) { $firstPostTags = array($firstPostTags); } $flags = $pieCrust->getConfig()->getValue('site/slugify_flags'); if (is_array($page->getPageKey())) { $pageKey = $page->getPageKey(); foreach ($firstPostTags as $t) { $st = UriBuilder::slugify($t, $flags); foreach ($pageKey as &$pk) { if ($st == $pk) { $pk = $t; break; } } } if ($page->getPageKey() == null) { $page->setPageKey($pageKey); } $data['tag'] = implode(' + ', $pageKey); } else { foreach ($firstPostTags as $t) { if (UriBuilder::slugify($t, $flags) == $data['tag']) { if ($page->getPageKey() == null) { $page->setPageKey($t); } $data['tag'] = $t; break; } } } } } break; case IPage::TYPE_CATEGORY: $data['category'] = $page->getPageKey(); if (strpos($page->getPageKey(), '-') >= 0) { // Same remark as for tags. $paginationPosts = $paginator->posts(); if (count($paginationPosts) > 0) { $firstPost = $paginationPosts[0]; if ($page->getPageKey() == null) { $page->setPageKey($firstPost['category']); } $data['category'] = $firstPost['category']; } } break; } $extraData = $page->getExtraPageData(); if ($extraData) { if (is_array($extraData)) { $data = Configuration::mergeArrays($data, $extraData); } else { $data['extra'] = $extraData; } } return $data; }
protected function loadUnsafe() { if ($this->wasCached()) { // Get the page from the cache. $configText = $this->cache->read($this->page->getUri(), 'json'); $config = json_decode($configText, true); $this->page->getConfig()->set($config, false); // false = No need to validate this. if (!$this->page->getConfig()->hasValue('segments')) { throw new PieCrustException("Can't get segments list from cache."); } $contents = array(); foreach ($config['segments'] as $key) { $segmentText = $this->cache->read($this->page->getUri() . '.' . $key, 'json'); $contents[$key] = json_decode($segmentText); // The deserialized JSON object is not of type `ContentSegment` but will // have the same attributes so it should work all fine. // Sanity test: if the first content segment is null, it may mean that the // original page file was in a non-supported encoding. if (count($contents[$key]) > 0 && $contents[$key]->parts[0]->content === null) { throw new PieCrustException("Corrupted cache: is the page not saved in UTF-8 encoding?"); } } return $contents; } else { // Load the page from disk. $rawContents = file_get_contents($this->page->getPath()); $rawContents = PageLoader::removeUnicodeBOM($rawContents); $header = Configuration::parseHeader($rawContents); // Set the format from the file extension. if (!isset($header->config['format'])) { $app = $this->page->getApp(); $autoFormats = $app->getConfig()->getValueUnchecked('site/auto_formats'); $extension = pathinfo($this->page->getPath(), PATHINFO_EXTENSION); if (isset($autoFormats[$extension])) { $format = $autoFormats[$extension]; if ($format) { $header->config['format'] = $autoFormats[$extension]; } } } // Set the configuration. $config = $this->page->getConfig(); $config->set($header->config); // Set the raw content with the unparsed content segments. $contents = $this->parseContentSegments($rawContents, $header->textOffset); // Add the list of known segments to the configuration. foreach ($contents as $key => $segment) { $config->appendValue('segments', $key); } // Cache that shit out. if ($this->cache != null) { $cacheUri = $this->page->getUri(); if ($cacheUri == '') { $cacheUri = '_index'; } $configText = json_encode($config->get()); $this->cache->write($cacheUri, 'json', $configText); $keys = $config['segments']; foreach ($keys as $key) { $segmentText = json_encode($contents[$key]); $this->cache->write($cacheUri . '.' . $key, 'json', $segmentText); } } return $contents; } }