<?php /* * blog.php : This file provides the meta data parser for the CMS. It generates the temporary * file containing the sorted posts. It also handles pre-parsing of embedded content * such as images. */ if (!isset($CMS_ACTIVE)) exit(); require_once "shared.php"; if (isset($_GET['b'])) { // Blog post selector $postUrl = $_GET['b']; preg_match("/^[a-z0-9\\-" . POSTS_DIR_SEP . "]*$/", $postUrl, $matches); // SECURITY: Only lowercase a-z + numbers if (!$matches) unset($postUrl); else { $postUrl=substr($postUrl, 0, POSTS_MAX_FILENAME); $postUrl=POSTS_ROOT . str_replace(POSTS_DIR_SEP, "/", $postUrl) . ".php"; if (!file_exists($postUrl)) { unset($postUrl); } } } $postsPage = 0; $postsSkip = 0; if (isset($_GET['s'])) { // Page selector for post listing if (is_numeric($_GET['s']) && $_GET['s'] < 100) { // SECURITY: MAX 100 pages $postsPage = (int)$_GET['s']; $postsSkip = $postsPage * POSTS_PAGE_SIZE; } } $tagSearchUrl = ""; if (isset($_GET['t'])) { // Tag search $tagSearch = $_GET['t']; preg_match("/^[a-z0-9]*$/", $tagSearch, $matches); // SECURITY: Only lowercase a-z + numbers if (!$matches) unset($tagSearch); else { $tagSearch=substr($tagSearch, 0, POSTS_TAG_SEARCH); // SECURITY: Max search length POSTS_TAG_SEARCH $tagSearchUrl="&t=$tagSearch"; } } $userPassword = ""; if (isset($_POST['password'])) { // Protected sections $userPassword = $_POST['password']; preg_match("/^[a-z0-9]*$/", $userPassword, $matches); // SECURITY: Only lowercase a-z + numbers if (!$matches) $userPassword = ""; else { $userPassword=substr($userPassword, 0, 32); // SECURITY: Max PW len = 32 } } // --- $postUrl is now safe to use! (Full FS path) // --- $postsPage is now safe to use! // --- $tagSearch is now safe to use! // --- $userPassword is now safe to use! // Function called by the individual post headers to determin whether to display it and // to configure / complete the metadata. function renderPost() { global $CMS_INDEXING; global $blogentry; global $counters; global $meta; global $pageTitle; global $postUrl; global $htmlHead; $counters['figure'] = 1; $counters['video'] = 1; $counters['sources'] = 1; $meta['sources'] = array(); $blogentry->file = substr($blogentry->file, strlen(POSTS_ROOT), -4); if (!(($posts = apcu_fetch('posts')) && isset($posts[$blogentry->file]) && $blogentry=$posts[$blogentry->file])) { //echo "[DBG] Regenerate meta info block..."; if (!isset($blogentry->file)) $blogentry->file = ""; if (!isset($blogentry->title)) $blogentry->title = "Untitled"; if (!isset($blogentry->description)) $blogentry->description = $blogentry->title; if (!isset($blogentry->author)) $blogentry->author = "Anonymous"; if (!isset($blogentry->status)) $blogentry->status = "private"; if (!isset($blogentry->ctime)) $blogentry->ctime = time(); if (!isset($blogentry->tags)) $blogentry->tags = "untagged"; $blogentry->tags = explode(',', $blogentry->tags); sort($blogentry->tags); $blogentry->permalink = "?p=blog&b=" . str_replace("/", POSTS_DIR_SEP, $blogentry->file); $blogentry->isPublic = ($blogentry->status == "released"); } $renderIt = $CMS_INDEXING || ($blogentry->status == "released") || ($blogentry->status == "unlisted"); if ($renderIt) { if (isset($postUrl) && !$CMS_INDEXING) { // Set meta information $pageTitle = $blogentry->title; $htmlHead .= '<meta name="description" content="' . $blogentry->description . '"> <meta name="keywords" content="' . join(",", $blogentry->tags) . '"> <meta name="author" content="' . $blogentry->author . '">'; } echo "<blogheading><table><tr><td><h1><a href=\"$blogentry->permalink\">$blogentry->title</a></h1></td>"; //echo '<td class="date">' . date("l, F jS Y, H:i", $blogentry->ctime) . "</td></tr></table></blogheading>"; echo '<td class="head"><span class="author">' . $blogentry->author . '</span> | <span class="date">' . date("l, F jS Y, H:i", $blogentry->ctime) . "</span></td></tr></table></blogheading>"; } return $renderIt; } function renderFooter() { global $counters; global $meta; $i = 1; if ($counters['sources'] > 1) { echo '<hr class="sourceSeparator"><p class="sources">Sources:<br>'; foreach ($meta['sources'] as $v) { echo '<span id="src' . $i . '"> [' . $i++ . ']: <a href="' . $v . '">' . $v . '</a></span><br>'; } echo '</p><hr class="sourceSeparator">'; } global $blogentry; echo '<p class="tags">Tags: '; foreach ($blogentry->tags as $v) { echo '<a href="/?p=blog&t=' . $v . '">' . $v . '</a> '; } echo "</p>"; } function extended() { global $CMS_SINGLE_POST; global $blogentry; $renderExt = isset($CMS_SINGLE_POST) && $CMS_SINGLE_POST; if ($renderExt) { echo '<span id="more"></span>'; } else { echo '<p><a href="' . $blogentry->permalink . '#more">→ Click here to continue reading ←</a></p>'; } return $renderExt; } function password($pw) { global $userPassword; if ("$userPassword" == "$pw") { echo '<span id="prot"></span>'; return true; } else { echo "<p>"; if ($userPassword == "") echo "This section is protected. To view it, enter the correct password."; else echo "The password you entered is incorrect! Please try again."; ?> <form action="#prot" method="post"> <input type="password" name="password"> <input type="submit" value="Unlock section"> </form> </p> <?php } } //apcu_clear_cache(); if (!($posts = apcu_fetch('posts'))) { $CMS_INDEXING = true; //echo "[DBG] Regenerate post index"; $posts = array(); foreach ($blogDirs as $dirName) { // Find all posts $Directory = new RecursiveDirectoryIterator('b/' . $dirName . '/'); $Iterator = new RecursiveIteratorIterator($Directory); $objects = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); // Load Meta data and put it into one big archive //$posts = array(); foreach($objects as $file => $object){ ob_start(); // These ob_lines will prevent output of actual HTML (the function already does this?!) require "$file"; $blogentry->includeFile = $file; //echo "<hr>"; ob_end_clean(); // Enable output, drop cached //array_push($posts, $blogentry); $posts[$blogentry->file] = $blogentry; } # Create list of columns relevant for sorting $sort = array(); foreach($posts as $k=>$v) { $sort['ctime'][$k] = $v->ctime;; // $sort['author'][$k] = $v->author; } // Now sort the entries // Multiple keys: array_multisort($sort['author'], SORT_ASC, $sort['title'], SORT_ASC,$posts); array_multisort($sort['ctime'], SORT_DESC, $posts); } // Store to cache apcu_store('posts', $posts, CACHE_POSTS_TTL); $CMS_INDEXING = false; } echo "<blog>"; if (isset($postUrl)) { // Display post $CMS_SINGLE_POST = true; echo '<div class="post">'; // Header is rendered in renderPost() require $postUrl; // This will render the body //if ($blogentry->isPublic) { // Render the blog footer //echo "<p class=\"author\">Written by $blogentry->author</p>"; renderFooter(); //} echo '</div>'; } else { //echo "<h1>Blog posts</h1>"; if (isset($tagSearch)) { echo "<h1 class=\"bloginfoheader\">Archive for '$tagSearch'</h1>"; $pageTitle = "Archive for '$tagSearch'"; } $i = 0; $morePosts = false; foreach($posts as $k=>$v) { if ($v->isPublic) { if (!isset($tagSearch) || in_array($tagSearch, $v->tags)) { if ($i++ >= $postsSkip) { //echo "<a href=\"$v->permalink\">$v->title by $v->author</a><br>"; echo '<div class="post">'; require $v->includeFile; renderFooter(); echo "</div>"; echo '<hr class="postSeparator">'; if (($i % POSTS_PAGE_SIZE) == 0) { $morePosts = true; break; } } } } } if ($morePosts || $postsSkip > 0) echo "<p><table class=\"blognav\"><tr>"; if ($morePosts) echo '<td><a href="' . "?p=blog$tagSearchUrl&s=" . ($postsPage+1) . '"><< Older posts</a></td>'; if ($postsSkip > 0) echo '<td style="text-align:right;"><a href="' . "?p=blog$tagSearchUrl&s=" . ($postsPage-1) . '">Newer posts >></a></td>'; if ($morePosts || $postsSkip > 0) echo "</tr></table></p>"; } echo "</blog>"; ?>