public function run(ChefContext $context) { $logger = $context->getLog(); $pieCrust = $context->getApp(); $result = $context->getResult(); // Site title. $title = $pieCrust->getConfig()->getValue('site/title'); if ($title == null) { $title = "[Unknown Website Title]"; } // Compute the page count. $pageCount = 0; $callback = function ($page) use(&$pageCount) { $pageCount++; }; PageHelper::processPages($pieCrust, $callback); // Compute the post count. $postCounts = array(); $blogKeys = $pieCrust->getConfig()->getValueUnchecked('site/blogs'); foreach ($blogKeys as $blogKey) { $postCounts[$blogKey] = count($pieCrust->getEnvironment()->getPostInfos($blogKey)); } $logger->info("Stats for '{$title}':"); $logger->info("Root : {$pieCrust->getRootDir()}"); $logger->info("Pages : {$pageCount}"); foreach ($blogKeys as $blogKey) { $logger->info("Posts : {$postCounts[$blogKey]} (in '{$blogKey}')"); } return 0; }
/** * Returns a validated version of the given site configuration. * * This is exposed as a public static function for convenience (unit tests, * etc.) */ public static function getValidatedConfig(IPage $page, $config) { if (!$config) { $config = array(); } // Add the default page config values. $pieCrustConfig = $page->getApp()->getConfig(); $blogKeys = $pieCrustConfig->getValueUnchecked('site/blogs'); $layoutName = PieCrustDefaults::DEFAULT_PAGE_TEMPLATE_NAME; if (PageHelper::isPost($page)) { $layoutName = PieCrustDefaults::DEFAULT_POST_TEMPLATE_NAME; if ($page->getBlogKey()) { // If this is a post in a multi-blog environment, make it use // the `<blogname>/post.html` layout first by default, and // then fallback on `post.html` if that doesn't exist. $layoutName = $page->getBlogKey() . '/' . PieCrustDefaults::DEFAULT_POST_TEMPLATE_NAME . ',' . $layoutName; } } $validatedConfig = array_merge(array('layout' => $layoutName, 'format' => $pieCrustConfig->getValueUnchecked('site/default_format'), 'template_engine' => $pieCrustConfig->getValueUnchecked('site/default_template_engine'), 'content_type' => 'html', 'title' => 'Untitled Page', 'blog' => $page->getBlogKey() != null ? $page->getBlogKey() : $blogKeys[0], 'segments' => array()), $config); // Detect common problems. if (isset($validatedConfig['category'])) { if (is_array($validatedConfig['category'])) { throw new PieCrustException("Page '{$page->getUri()}': `category` is an array -- it must be a string. For multiple values, use `tags` instead."); } } if (isset($validatedConfig['tags'])) { if (!is_array($validatedConfig['tags'])) { $validatedConfig['tags'] = array($validatedConfig['tags']); } } if (isset($validatedConfig['single_page'])) { throw new PieCrustException("Page '{$page->getUri()}': `single_page` has been deprecated. Use `blog.posts` with some limits if you want a single page showing the most recent posts."); } return $validatedConfig; }
public function run(ChefContext $context) { $logger = $context->getLog(); $pieCrust = $context->getApp(); $result = $context->getResult(); // Validate options. if ($result->command->options['order_by_name'] && $result->command->options['order_by_count']) { throw new PieCrustException("Can't specify both '--order-name' and '--order-count'."); } $blogKeys = $pieCrust->getConfig()->getValue('site/blogs'); if ($result->command->args['blog']) { foreach ($result->command->args['blog'] as $blogKey) { if (!in_array($blogKey, $blogKeys)) { throw new PieCrustException("No such blog in the website : {$blogKey}"); } } $blogKeys = $result->command->args['blog']; } $categories = array(); foreach ($blogKeys as $blogKey) { $callback = function ($post) use(&$categories) { $c = $post->getConfig()->getValue('category'); if ($c) { if (!isset($categories[$c])) { $categories[$c] = 0; } $categories[$c] += 1; } }; PageHelper::processPosts($context->getApp(), $blogKey, $callback); } // Only print the count? if ($result->command->options['count']) { $logger->info(count($categories)); return 0; } // Sort appropriately. $reverse = $result->command->options['reverse']; if ($result->command->options['order_by_name']) { if ($reverse) { krsort($categories); } else { ksort($categories); } } else { if ($result->command->options['order_by_count']) { if ($reverse) { array_multisort($categories, SORT_DESC); } else { array_multisort($categories, SORT_ASC); } } } // Print the list. $logger->info(count($categories) . " categories."); foreach ($categories as $c => $count) { $logger->info("{$c} ({$count} posts)"); } }
public function formatContents(array $rawSegments) { try { return $this->formatContentsUnsafe($rawSegments); } catch (Exception $e) { $relativePath = PageHelper::getRelativePath($this->page); throw new PieCrustException("Error formatting page '{$relativePath}': {$e->getMessage()}", 0, $e); } }
public function testGetSimpleSitePages() { $fs = MockFileSystem::create()->withPage('_index', array(), 'Blah.'); $app = $fs->getApp(); $pages = $app->getEnvironment()->getPages(); $this->assertEquals(1, count($pages)); $this->assertEquals('_index.html', PageHelper::getRelativePath($pages[0])); $this->assertEquals(str_replace('\\', '/', $fs->url('kitchen/_content/pages/_index.html')), str_replace('\\', '/', $pages[0]->getPath())); }
/** * Returns a validated version of the given site configuration. * * This is exposed as a public static function for convenience (unit tests, * etc.) */ public static function getValidatedConfig(IPage $page, $config) { if (!$config) { $config = array(); } // Add the default page config values. $pieCrustConfig = $page->getApp()->getConfig(); $blogKeys = $pieCrustConfig->getValueUnchecked('site/blogs'); $validatedConfig = array_merge(array('layout' => PageHelper::isPost($page) ? PieCrustDefaults::DEFAULT_POST_TEMPLATE_NAME : PieCrustDefaults::DEFAULT_PAGE_TEMPLATE_NAME, 'format' => $pieCrustConfig->getValueUnchecked('site/default_format'), 'template_engine' => $pieCrustConfig->getValueUnchecked('site/default_template_engine'), 'content_type' => 'html', 'title' => 'Untitled Page', 'blog' => $page->getBlogKey() != null ? $page->getBlogKey() : $blogKeys[0], 'segments' => array()), $config); return $validatedConfig; }
protected function addCustomValues() { $post = $this->page; $pieCrust = $this->page->getApp(); $blogKey = $this->page->getConfig()->getValueUnchecked('blog'); $postsDateFormat = PageHelper::getConfigValueUnchecked($this->page, 'date_format', $blogKey); // Add the easy values to the values array. $this->values['url'] = PieCrustHelper::formatUri($pieCrust, $post->getUri()); $this->values['slug'] = $post->getUri(); $this->values['timestamp'] = $post->getDate(); //TODO: do we need to move this to the lazy-loaded values? $this->values['date'] = date($postsDateFormat, $post->getDate()); // Add some lazy-loading functions for stuff // that would load the page's contents. $this->lazyValues[self::WILDCARD] = 'loadContent'; }
protected function addCustomValues() { $post = $this->page; $pieCrust = $post->getApp(); $blogKey = $post->getConfig()->getValueUnchecked('blog'); $postsDateFormat = PageHelper::getConfigValueUnchecked($post, 'date_format', $blogKey); // Add the easy values to the values array. $this->values['url'] = PieCrustHelper::formatUri($pieCrust, $post->getUri()); $this->values['slug'] = $post->getUri(); $this->values['timestamp'] = $post->getDate(true); $this->values['date'] = date($postsDateFormat, $post->getDate(true)); // Make it possible to access assets. $assetor = new Assetor($post); $this->values['assets'] = $assetor; // Add some lazy-loading functions for stuff // that would load the page's contents. $this->lazyValues[self::WILDCARD] = 'loadContent'; }
/** * Runs PieCrust on the given URI with the given extra page rendering data, * but without any error handling. */ public function runUnsafe($uri = null, array $server = null, $extraPageData = null, array &$headers = null) { // Create an execution context. $executionContext = $this->pieCrust->getEnvironment()->getExecutionContext(true); // Check the cache validity, and clean it automatically. if ($this->pieCrust->isCachingEnabled()) { $cacheInfo = new PieCrustCacheInfo($this->pieCrust); $cacheValidity = $cacheInfo->getValidity(true); $executionContext->isCacheValid = $cacheValidity['is_valid']; $executionContext->wasCacheCleaned = $cacheValidity['was_cleaned']; } // Get the resource URI and corresponding physical path. if ($server == null) { $server = $_SERVER; } if ($uri == null) { $uri = ServerHelper::getRequestUri($server, $this->pieCrust->getConfig()->getValueUnchecked('site/pretty_urls')); } // Do the heavy lifting. $page = Page::createFromUri($this->pieCrust, $uri); $executionContext->pushPage($page); if ($extraPageData != null) { $page->setExtraPageData($extraPageData); } $pageRenderer = new PageRenderer($page); $output = $pageRenderer->get(); // Set or return the HTML headers. HttpHeaderHelper::setOrAddHeaders(PageRenderer::getHeaders($page->getConfig()->getValue('content_type'), $server), $headers); // Handle caching. if (!$this->pieCrust->isDebuggingEnabled()) { $hash = md5($output); HttpHeaderHelper::setOrAddHeader('Etag', '"' . $hash . '"', $headers); $clientHash = null; if (isset($server['HTTP_IF_NONE_MATCH'])) { $clientHash = $server['HTTP_IF_NONE_MATCH']; } if ($clientHash != null) { $clientHash = trim($clientHash, '"'); if ($hash == $clientHash) { HttpHeaderHelper::setOrAddHeader(0, 304, $headers); HttpHeaderHelper::setOrAddHeader('Content-Length', '0', $headers); return; } } } if ($this->pieCrust->isDebuggingEnabled()) { HttpHeaderHelper::setOrAddHeader('Cache-Control', 'no-cache, must-revalidate', $headers); } else { $cacheTime = PageHelper::getConfigValue($page, 'cache_time', 'site'); if ($cacheTime) { HttpHeaderHelper::setOrAddHeader('Cache-Control', 'public, max-age=' . $cacheTime, $headers); } } // Output with or without GZip compression. $gzipEnabled = ($this->pieCrust->getConfig()->getValueUnchecked('site/enable_gzip') === true and array_key_exists('HTTP_ACCEPT_ENCODING', $server) and strpos($server['HTTP_ACCEPT_ENCODING'], 'gzip') !== false); if ($gzipEnabled) { $zippedOutput = gzencode($output); if ($zippedOutput === false) { HttpHeaderHelper::setOrAddHeader('Content-Length', strlen($output), $headers); echo $output; } else { HttpHeaderHelper::setOrAddHeader('Content-Encoding', 'gzip', $headers); HttpHeaderHelper::setOrAddHeader('Content-Length', strlen($zippedOutput), $headers); echo $zippedOutput; } } else { HttpHeaderHelper::setOrAddHeader('Content-Length', strlen($output), $headers); echo $output; } }
protected function bakePosts() { if ($this->bakeRecord == null) { throw new PieCrustException("Can't bake posts without a bake-record active."); } $blogKeys = $this->pieCrust->getConfig()->getValue('site/blogs'); foreach ($blogKeys as $blogKey) { $posts = PageHelper::getPosts($this->pieCrust, $blogKey); foreach ($posts as $post) { $this->bakePost($post); } } }
/** * 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 bakeSinglePage(PageRenderer $pageRenderer, array $extraData = null) { $page = $pageRenderer->getPage(); // Set the extra template data before the page's data is computed. if ($extraData != null) { $page->setExtraPageData($extraData); } // This is usually done in the PieCrustBaker, but we'll do it here too // because the `PageBaker` could be used on its own. if ($this->parameters['copy_assets']) { $page->setAssetUrlBaseRemap("%site_root%%uri%"); } // Figure out the output HTML path. $bakePath = $this->getOutputPath($page); $this->logger->debug(" p{$page->getPageNumber()} -> {$bakePath}"); // Figure out if we should re-bake this page. $doBake = true; if ($this->parameters['smart']) { // Don't rebake if the output seems up-to-date, and // the page isn't known to be using posts. $bakePathTime = @filemtime($bakePath); if ($bakePathTime !== false && filemtime($page->getPath()) < $bakePathTime) { // TODO: rebake if the page is using pagination and pages/posts were baked this time. $doBake = false; } } if (!$doBake) { $this->logger->debug("Not baking '{$page->getUri()}/{$page->getPageNumber()}' because '{$bakePath}' is up-to-date."); return false; } // If we're using portable URLs, change the site root to a relative // path from the page's directory. $savedSiteRoot = $this->setPortableSiteRoot($page->getApp(), $bakePath); // Render the page. $bakedContents = $pageRenderer->get(); // Get some objects we need. $data = $page->getPageData(); $assetor = $data['assets']; $paginator = $data['pagination']; // Copy the page. PathHelper::ensureDirectory(dirname($bakePath)); file_put_contents($bakePath, $bakedContents); $this->bakedFiles[] = $bakePath; // Copy any used assets for the first sub-page. if ($page->getPageNumber() == 1 and $this->parameters['copy_assets']) { $prettyUrls = PageHelper::getConfigValue($page, 'pretty_urls', 'site'); if ($prettyUrls) { $bakeAssetDir = dirname($bakePath) . '/'; } else { $bakePathInfo = pathinfo($bakePath); $bakeAssetDir = $bakePathInfo['dirname'] . '/' . ($page->getUri() == '' ? '' : $bakePathInfo['filename']) . '/'; } $assetPaths = $assetor->getAssetPathnames(); if ($assetPaths != null) { PathHelper::ensureDirectory($bakeAssetDir); foreach ($assetPaths as $assetPath) { $destinationAssetPath = $bakeAssetDir . basename($assetPath); if (@copy($assetPath, $destinationAssetPath) == false) { throw new PieCrustException("Can't copy '{$assetPath}' to '{$destinationAssetPath}'."); } } } } // Remember a few things. $this->paginationDataAccessed = ($this->paginationDataAccessed or $paginator->wasPaginationDataAccessed()); // Cleanup. if ($savedSiteRoot) { $page->getApp()->getConfig()->setValue('site/root', $savedSiteRoot); } return true; }
protected function ensureLinksCache() { if ($this->linksCache === null) { try { $pieCrust = $this->page->getApp(); $pageRepository = $pieCrust->getEnvironment()->getPageRepository(); $this->linksCache = array(); $skipNames = array('Thumbs.db'); $it = new FilesystemIterator($this->baseDir); foreach ($it as $item) { $basename = $item->getBasename(); // Skip dot files, Thumbs.db, etc. if (!$basename or $basename[0] == '.') { continue; } if (in_array($item->getFilename(), $skipNames)) { continue; } if ($item->isDir()) { $linker = new Linker($this->page, $item->getPathname()); $this->linksCache[$basename . '_'] = $linker; // We add '_' at the end of the directory name to avoid // collisions with a possibly existing page with the same // name (since we strip out the '.html' extension). // This means the user must access directories with // 'link.dirname_' instead of 'link.dirname' but hey, if // you have a better idea, send me an email! } else { $path = $item->getPathname(); try { $relativePath = PageHelper::getRelativePath($this->page); $uri = UriBuilder::buildUri($relativePath); // To get the link's page, we need to be careful with the case // where that page is the currently rendering one. This is // because it could be rendering a sub-page -- but we would be // requesting the default first page, which would effectively // change the page number *while* we're rendering, which leads // to all kinds of bad things! // TODO: obviously, there needs to be some design changes to // prevent this kind of chaotic behaviour. if ($path == $this->page->getPath()) { $page = $this->page; } else { $page = $pageRepository->getOrCreatePage($uri, $path); } $key = str_replace('.', '_', $item->getBasename('.html')); $this->linksCache[$key] = array('uri' => $uri, 'name' => $key, 'is_dir' => false, 'is_self' => $basename == $this->selfName, 'page' => new PaginationData($page)); } catch (Exception $e) { throw new PieCrustException("Error while loading page '{$path}' for linking from '{$this->page->getUri()}': " . $e->getMessage(), 0, $e); } } } if ($this->sortByName) { if (false === usort($this->linksCache, array($this, 'sortByCustom'))) { throw new PieCrustException("Error while sorting pages with the specified setting: {$this->sortByName}"); } } if ($this->selfName != null) { // Add special stuff only for the original Linker // (the one directly created by the current page). if (PageHelper::isRegular($this->page)) { // Add a link to go up to the parent directory, but stay inside // the app's pages directory. $parentBaseDir = dirname($this->baseDir); if (strlen($parentBaseDir) >= strlen($pieCrust->getPagesDir())) { $linker = new Linker($this->page, dirname($this->baseDir)); $this->linksCache['_'] = $linker; } } else { if (PageHelper::isPost($this->page)) { // Add a link to go up to the parent directory, but stay inside // the app's posts directory. $parentBaseDir = dirname($this->baseDir); if (strlen($parentBaseDir) >= strlen($pieCrust->getPostsDir())) { $linker = new Linker($this->page, dirname($this->baseDir)); $this->linksCache['_'] = $linker; } } } if ($pieCrust->getPagesDir()) { // Add a shortcut to the pages directory. $linker = new Linker($this->page, $pieCrust->getPagesDir()); $this->linksCache['_pages_'] = $linker; } if ($pieCrust->getPostsDir()) { // Add a shortcut to the posts directory. $linker = new Linker($this->page, $pieCrust->getPostsDir()); $this->linksCache['_posts_'] = $linker; } } } catch (Exception $e) { throw new PieCrustException("Error while building the links from page '{$this->page->getUri()}': " . $e->getMessage(), 0, $e); } } }
protected function getPaginationFilter() { $filter = new PaginationFilter(); $filterInfo = $this->page->getConfig()->getValue('posts_filters'); if ($filterInfo == 'none' or $filterInfo == 'nil' or $filterInfo == '') { $filterInfo = null; } if (PageHelper::isTag($this->page) or PageHelper::isCategory($this->page)) { // If the current page is a tag/category page, add filtering // for that. $filter->addPageClauses($this->page, $filterInfo); } else { if ($filterInfo != null) { // Add custom filtering clauses specified by the user in the // page configuration header. $filter->addClauses($filterInfo); } } return $filter; }
protected function bakeSinglePage(PageRenderer $pageRenderer, array $extraData = null) { $page = $pageRenderer->getPage(); // Set the extra template data before the page's data is computed. if ($extraData != null) { $page->setExtraPageData($extraData); } // This is usually done in the PieCrustBaker, but we'll do it here too // because the `PageBaker` could be used on its own. if ($this->parameters['copy_assets']) { $page->setAssetUrlBaseRemap("%site_root%%uri%"); } // Figure out the output HTML path. $bakePath = $this->getOutputPath($page); // Figure out if we should re-bake this page. $doBake = true; if (is_file($bakePath) && $this->parameters['smart']) { // Don't rebake if the output seems up-to-date, and // the page isn't known to be using posts. if (filemtime($page->getPath()) < filemtime($bakePath)) { $bakeRecord = $this->parameters['bake_record']; if ($bakeRecord) { $relativePath = PageHelper::getRelativePath($page); if (!$bakeRecord->wasAnyPostBaked() || !$bakeRecord->isPageUsingPosts($relativePath)) { $doBake = false; } } } } if (!$doBake) { $this->logger->debug("Not baking '{$page->getUri()}/{$page->getPageNumber()}' because '{$bakePath}' is up-to-date."); return false; } // Backward compatibility warning and file-copy. // [TODO] Remove in a couple of versions. $copyToOldPath = false; $contentType = $page->getConfig()->getValue('content_type'); $nativeExtension = pathinfo($page->getPath(), PATHINFO_EXTENSION); if ($contentType != 'html' && $nativeExtension == 'html') { $copyToOldPath = $this->bakeDir . $page->getUri(); if ($page->getPageNumber() > 1) { $copyToOldPath .= $page->getPageNumber() . '/'; } $copyToOldPath .= '.' . $contentType; } // If we're using portable URLs, change the site root to a relative // path from the page's directory. $savedSiteRoot = $this->setPortableSiteRoot($page->getApp(), $bakePath); // Render the page. $bakedContents = $pageRenderer->get(); // Get some objects we need. $data = $page->getPageData(); $assetor = $data['asset']; $paginator = $data['pagination']; // Copy the page. PathHelper::ensureDirectory(dirname($bakePath)); file_put_contents($bakePath, $bakedContents); $this->bakedFiles[] = $bakePath; // [TODO] See previous TODO. if ($copyToOldPath) { $this->logger->warning("Page '{$page->getUri()}' has 'content_type' specified but is an HTML file."); $this->logger->warning("Changing a baked file's extension using 'content_type' is deprecated and will be removed in a future version."); $this->logger->warning("For backwards compatibility, the page will also be baked to: " . substr($copyToOldPath, strlen($this->bakeDir))); $this->logger->warning("To fix the problem, change the source file's extension to the desired output extension."); $this->logger->warning("Otherwise, just ignore these messages."); file_put_contents($copyToOldPath, $bakedContents); } // Copy any used assets for the first sub-page. if ($page->getPageNumber() == 1 and $this->parameters['copy_assets']) { $prettyUrls = PageHelper::getConfigValue($page, 'pretty_urls', 'site'); if ($prettyUrls) { $bakeAssetDir = dirname($bakePath) . '/'; } else { $bakePathInfo = pathinfo($bakePath); $bakeAssetDir = $bakePathInfo['dirname'] . '/' . ($page->getUri() == '' ? '' : $bakePathInfo['filename']) . '/'; } $assetPaths = $assetor->getAssetPathnames(); if ($assetPaths != null) { PathHelper::ensureDirectory($bakeAssetDir); foreach ($assetPaths as $assetPath) { $destinationAssetPath = $bakeAssetDir . basename($assetPath); if (@copy($assetPath, $destinationAssetPath) == false) { throw new PieCrustException("Can't copy '" . $assetPath . "' to '" . $destinationAssetPath . "'."); } } } } // Remember a few things. $this->paginationDataAccessed = ($this->paginationDataAccessed or $paginator->wasPaginationDataAccessed()); // Cleanup. if ($savedSiteRoot) { $page->getApp()->getConfig()->setValue('site/root', $savedSiteRoot); } return true; }
protected function ensurePostsCached($blogKey) { if ($this->posts == null) { $this->posts = array(); } if (!isset($this->posts[$blogKey])) { $pageRepository = $this->getPageRepository(); $postInfos = $this->getPostInfos($blogKey); $postUrlFormat = $this->pieCrust->getConfig()->getValue($blogKey . '/post_url'); $posts = array(); foreach ($postInfos as $postInfo) { $uri = UriBuilder::buildPostUri($postUrlFormat, $postInfo); $page = $pageRepository->getOrCreatePage($uri, $postInfo['path'], IPage::TYPE_POST, $blogKey); $page->setDate(PageHelper::getPostDate($postInfo)); $posts[] = $page; } $this->posts[$blogKey] = $posts; } }
protected function ensureLoaded() { if ($this->values != null) { return; } // Gather all posts sorted by the property we want. $dataSources = array(); $posts = PageHelper::getPosts($this->page->getApp(), $this->blogKey); foreach ($posts as $post) { $this->addPageValue($post, $dataSources); } // Now for each property bucket, create a pagination iterator. $this->values = array(); foreach ($dataSources as $property => $dataSource) { $this->values[$property] = new PagePropertyData($this->page, $this->blogKey, $property, $dataSource); } ksort($this->values); }
private function findPages($context, $pages, $fromBlog = false) { $logger = $context->getLog(); $pieCrust = $context->getApp(); $result = $context->getResult(); $rootDir = $pieCrust->getRootDir(); $rootDir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $rootDir); $exact = $result->command->options['exact']; $pattern = $result->command->args['pattern']; $fullPath = $result->command->options['full_path']; $returnComponents = $result->command->options['page_components']; $foundAny = false; foreach ($pages as $page) { if ($result->command->options['no_special']) { // Skip special pages. if ($page->getUri() == PieCrustDefaults::CATEGORY_PAGE_NAME or $page->getUri() == PieCrustDefaults::TAG_PAGE_NAME) { continue; } } if ($exact) { // Match the path exactly, or pass. if (str_replace('\\', '/', $pattern) != str_replace('\\', '/', $page->getPath())) { continue; } } else { if ($pattern) { // Match the regex, or pass. if (!preg_match($pattern, $page->getUri())) { continue; } } } $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $page->getPath()); if (!$fullPath) { if (substr($path, 0, strlen($rootDir)) == $rootDir) { $path = PieCrustHelper::getRelativePath($pieCrust, $path); } } if ($returnComponents) { $components = array('path' => $path, 'type' => 'page', 'uri' => $page->getUri(), 'slug' => $page->getUri()); if (PageHelper::isPost($page)) { $timestamp = $page->getDate(true); $components['type'] = 'post'; $components['year'] = date('Y', $timestamp); $components['month'] = date('m', $timestamp); $components['day'] = date('d', $timestamp); $components['hour'] = date('H', $timestamp); $components['minute'] = date('i', $timestamp); $components['second'] = date('s', $timestamp); $matches = array(); $postsPattern = UriBuilder::buildPostUriPattern($pieCrust->getConfig()->getValue($fromBlog . '/post_url'), $fromBlog); if (preg_match($postsPattern, $page->getUri(), $matches)) { $components['slug'] = $matches['slug']; } } foreach ($components as $k => $v) { $logger->info("{$k}: {$v}"); } $logger->info(""); $foundAny = true; } else { $logger->info($path); $foundAny = true; } } return $foundAny; }
protected function ensureMonths() { if ($this->months) { return; } // Get all the blog posts. $posts = PageHelper::getPosts($this->page->getApp(), $this->blogKey); // Sort them by month. $monthsInfos = array(); $currentMonthAndYear = null; foreach ($posts as $post) { $timestamp = $post->getDate(); $pageMonthAndYear = date('F Y', $timestamp); if (!isset($monthsInfos[$pageMonthAndYear])) { $pageYear = intval(date('Y', $timestamp)); $pageMonth = intval(date('m', $timestamp)); $monthsInfos[$pageMonthAndYear] = array('name' => $pageMonthAndYear, 'timestamp' => mktime(0, 0, 0, $pageMonth, 1, $pageYear), 'data_source' => array()); } $monthsInfos[$pageMonthAndYear]['data_source'][] = $post; } // For each month, create the data class. $this->months = array(); foreach ($monthsInfos as $month => $monthInfo) { $this->months[$month] = new PageTimeData($this->page, $this->blogKey, $monthInfo['name'], $monthInfo['timestamp'], $monthInfo['data_source']); } // Sort the months in inverse chronological order. uasort($this->months, array('PieCrust\\Data\\BlogData', 'sortByReverseTimestamp')); }
protected function ensureMonths() { if ($this->months) { return; } $blogPosts = PageHelper::getPosts($this->page->getApp(), $this->blogKey); $this->months = array(); $currentMonthAndYear = null; foreach ($blogPosts as $post) { $timestamp = $post->getDate(); $pageMonthAndYear = date('Y m', $timestamp); if ($currentMonthAndYear == null or $pageMonthAndYear != $currentMonthAndYear) { $pageYear = intval(date('Y', $timestamp)); $pageMonth = intval(date('m', $timestamp)); $this->months[$pageMonthAndYear] = array('name' => date('F Y', $timestamp), 'timestamp' => mktime(0, 0, 0, $pageMonth, 1, $pageYear), 'posts' => array()); $currentMonthAndYear = $pageMonthAndYear; } $this->months[$currentMonthAndYear]['posts'][] = new PaginationData($post); } ksort($this->months); }
public function run(ChefContext $context) { $logger = $context->getLog(); $pieCrust = $context->getApp(); $result = $context->getResult(); // Warn about deprecated stuff. if ($result->command->options['order_by_name_old']) { $context->getLog()->warning("The `--orderbyname` option has been renamed to `--order-name`."); $result->command->options['order_by_name'] = true; } if ($result->command->options['order_by_count_old']) { $context->getLog()->warning("The `--orderbycount` option has been renamed to `--order-count`."); $result->command->options['order_by_count'] = true; } // Validate options. if ($result->command->options['order_by_name'] && $result->command->options['order_by_count']) { throw new PieCrustException("Can't specify both '--order-name' and '--order-count'."); } $blogKeys = $pieCrust->getConfig()->getValue('site/blogs'); if ($result->command->args['blog']) { foreach ($result->command->args['blog'] as $blogKey) { if (!in_array($blogKey, $blogKeys)) { throw new PieCrustException("No such blog in the website : {$blogKey}"); } } $blogKeys = $result->command->args['blog']; } $tags = array(); foreach ($blogKeys as $blogKey) { $callback = function ($post) use(&$tags) { $postTags = $post->getConfig()->getValue('tags'); if ($postTags) { if (!is_array($postTags)) { $postTags = array($postTags); } foreach ($postTags as $t) { if (!isset($tags[$t])) { $tags[$t] = 0; } $tags[$t] += 1; } } }; PageHelper::processPosts($pieCrust, $blogKey, $callback); } // Only print the count? if ($result->command->options['count']) { $logger->info(count($tags)); return 0; } // Sort appropriately. $reverse = $result->command->options['reverse']; if ($result->command->options['order_by_name']) { if ($reverse) { krsort($tags); } else { ksort($tags); } } else { if ($result->command->options['order_by_count']) { if ($reverse) { array_multisort($tags, SORT_DESC); } else { array_multisort($tags, SORT_ASC); } } } // Print the list. $logger->info(count($tags) . " tags."); foreach ($tags as $t => $count) { $logger->info("{$t} ({$count} posts)"); } }
protected function getPaginationFilter() { $filter = new PaginationFilter(); $filterInfo = $this->page->getConfig()->getValue('posts_filters'); if ($filterInfo == 'none' or $filterInfo == 'nil' or $filterInfo == '') { $filterInfo = null; } if (PageHelper::isTag($this->page) or PageHelper::isCategory($this->page)) { // If the current page is a tag/category page, add filtering // for that. if ($filterInfo != null) { throw new PieCrustException("The `posts_filters` setting cannot be used on a tag or category listing page -- the filter will be automatically set to posts matching the request tag or category."); } $filter->addPageClauses($this->page); } else { if ($filterInfo != null) { // Add custom filtering clauses specified by the user in the // page configuration header. $filter->addClauses($filterInfo); } } return $filter; }
protected function ensurePostsCached($blogKey) { if ($this->posts == null) { $this->posts = array(); } if (!isset($this->posts[$blogKey])) { $this->getLog()->debug("Indexing '{$blogKey}' posts..."); $fs = $this->getFileSystem(); $postInfos = $fs->getPostFiles($blogKey); $this->getLog()->debug("Creating '{$blogKey}' posts..."); $pageRepository = $this->getPageRepository(); $posts = array(); foreach ($postInfos as $postInfo) { $uri = UriBuilder::buildPostUri($this->pieCrust, $blogKey, $postInfo); $page = $pageRepository->getOrCreatePage($uri, $postInfo->path, IPage::TYPE_POST, $blogKey); $page->setDate(PageHelper::getPostDate($postInfo)); $posts[] = $page; } $this->posts[$blogKey] = $posts; } }
protected static function buildUrlBase(IPage $page, $assetUrlBaseRemap) { $siteRoot = $page->getApp()->getConfig()->getValueUnchecked('site/root'); $relativePath = str_replace('\\', '/', PieCrustHelper::getRelativePath($page->getApp(), $page->getPath(), true)); $uri = $page->getUri(); $prettyUrls = PageHelper::getConfigValue($page, 'pretty_urls', 'site'); if (!$prettyUrls) { // Remove the extension from the URI (if any), because without 'pretty URLs', // we want to copy assets to a directory named after the page's filename // (without the extension). See `PageBaker` for more information. $uriInfo = pathinfo($uri); $uri = $uriInfo['dirname']; if ($uri == '.') { $uri = ''; } else { $uri .= '/'; } $uri .= $uriInfo['filename']; } $replacements = array('%site_root%' => $siteRoot, '%path%' => $relativePath, '%uri%' => $uri); return str_replace(array_keys($replacements), array_values($replacements), $assetUrlBaseRemap); }
protected function bakePosts() { $blogKeys = $this->pieCrust->getConfig()->getValue('site/blogs'); foreach ($blogKeys as $blogKey) { $posts = PageHelper::getPosts($this->pieCrust, $blogKey); foreach ($posts as $post) { $this->bakePost($post); } } }
public function run(ChefContext $context) { $logger = $context->getLog(); $pieCrust = $context->getApp(); $result = $context->getResult(); // Get some options. $exact = $result->command->options['exact']; $fullPath = $result->command->options['full_path']; // If no type filters are given, return all types. $returnAllTypes = ($result->command->options['pages'] == false and $result->command->options['posts'] == false and $result->command->options['templates'] == false); // Validate the argument. $pattern = $result->command->args['pattern']; if ($exact) { // Check we have a path to match, and get its absolute value. if (!$pattern) { throw new PieCrustException("You need to specify a path when using the `--exact` option."); } $pattern = PathHelper::getAbsolutePath($pattern); } else { // If a pattern was given, get the Regex'd version. if ($pattern) { $pattern = PathHelper::globToRegex($pattern); } } // Get the pages and posts. $pages = array(); if ($returnAllTypes or $result->command->options['pages']) { $pages = PageHelper::getPages($pieCrust); } if ($returnAllTypes or $result->command->options['posts']) { $blogKeys = $pieCrust->getConfig()->getValue('site/blogs'); if ($result->command->options['blog']) { $blogKeys = array($result->command->options['blog']); } foreach ($blogKeys as $blogKey) { $pages = array_merge($pages, PageHelper::getPosts($pieCrust, $blogKey)); } } // Get some other stuff. $returnComponents = $result->command->options['page_components']; // Get a regex for the posts file-naming convention. $fs = FileSystem::create($pieCrust); $pathComponentsRegex = preg_quote($fs->getPostPathFormat(), '/'); $pathComponentsRegex = str_replace(array('%year%', '%month%', '%day%', '%slug%'), array('(\\d{4})', '(\\d{2})', '(\\d{2})', '(.+)'), $pathComponentsRegex); $pathComponentsRegex = '/' . $pathComponentsRegex . '/'; // Print the matching pages. foreach ($pages as $page) { if ($result->command->options['no_special']) { // Skip special pages. if ($page->getUri() == PieCrustDefaults::CATEGORY_PAGE_NAME or $page->getUri() == PieCrustDefaults::TAG_PAGE_NAME) { continue; } } if ($exact) { // Match the path exactly, or pass. if (str_replace('\\', '/', $pattern) != str_replace('\\', '/', $page->getPath())) { continue; } } else { if ($pattern) { // Match the regex, or pass. if (!preg_match($pattern, $page->getUri())) { continue; } } } $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $page->getPath()); if (!$fullPath) { $path = PieCrustHelper::getRelativePath($pieCrust, $path); } if ($returnComponents) { $components = array('path' => $path, 'type' => 'page', 'uri' => $page->getUri(), 'slug' => $page->getUri()); if (PageHelper::isPost($page)) { $matches = array(); if (preg_match($pathComponentsRegex, str_replace('\\', '/', $path), $matches) !== 1) { throw new PieCrustException("Can't extract path components from path: {$path}"); } $components['type'] = 'post'; $components['year'] = $matches[1]; $components['month'] = $matches[2]; $components['day'] = $matches[3]; $components['slug'] = $matches[4]; } $str = ''; foreach ($components as $k => $v) { $str .= $k . ': ' . $v . PHP_EOL; } $logger->info($str); } else { $logger->info($path); } } // Get the template files and print them. if ($returnAllTypes or $result->command->options['templates']) { $templatesDirs = $pieCrust->getTemplatesDirs(); foreach ($templatesDirs as $dir) { $dirIt = new \RecursiveDirectoryIterator($dir); $it = new \RecursiveIteratorIterator($dirIt); foreach ($it as $path) { if ($it->isDot()) { continue; } $relativePath = PieCrustHelper::getRelativePath($pieCrust, $path->getPathname()); if ($exact) { // Match the path exactly, or pass. if (str_replace('\\', '/', $pattern) != str_replace('\\', '/', $path->getPathname())) { continue; } } else { if ($pattern) { // Match the regex, or pass. if (!preg_match($pattern, $relativePath)) { continue; } } } // Get the path to print. $finalPath = $relativePath; if ($fullPath) { $finalPath = $path->getPathname(); } $finalPath = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $finalPath); // Print the information! if ($returnComponents) { $logger->info("path: {$finalPath}"); $logger->info("type: template"); } else { $logger->info($finalPath); } } } } return 0; }
/** * 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; }