<?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; global $CMS_RSS; $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 = getServerUrl() . "/?p=blog&b=" . str_replace("/", POSTS_DIR_SEP, $blogentry->file); $blogentry->isVisible = ($blogentry->ctime <= time()) && (($blogentry->status == "released") || ($blogentry->status == "unlisted")); $blogentry->isPublic = ($blogentry->status == "released") && $blogentry->isVisible; } $renderIt = $CMS_INDEXING || $blogentry->isVisible; if ($renderIt && !isset($CMS_RSS)) { 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 title="Original release: ' . date("l, F jS Y, H:i", $blogentry->ctime) . '" class="date">'; if (isset($blogentry->utime)) { echo "Updated " . date("l, F jS Y, H:i", $blogentry->utime); } else { echo date("l, F jS Y, H:i", $blogentry->ctime); } echo "</span></td></tr></table></blogheading>"; } return $renderIt; } function renderFooter() { global $CMS_SINGLE_POST; global $counters; global $meta; $i = 1; $renderExt = isset($CMS_SINGLE_POST) && $CMS_SINGLE_POST; if ($renderExt && $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; } if (isset($CMS_SITEMAP)) { foreach($posts as $k=>$v) { if ($v->isPublic) { echo "<url>\n\t<loc>" . htmlspecialchars($v->permalink) . "</loc>\n</url>\n"; } } return; } if (!isset($CMS_RSS)) 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->isVisible) { // Render the blog footer //echo "<p class=\"author\">Written by $blogentry->author</p>"; renderFooter(); } else { echo("<h1>Private Post</h1>"); echo("<p>This post has not yet been made public by the publisher. Please check back again later. Thank you.</p>"); } echo '</div>'; } else { //echo "<h1>Blog posts</h1>"; if (isset($tagSearch)) { echo "<h1 class=\"bloginfoheader\"><table border=0 style='width: 100%;'><tr><td>Archive for '$tagSearch'</td>"; if (RSS_ENABLED) { echo '<td style="text-align:right;padding:6px; padding-bottom: 0px; "><a href="/rss.php?t=' . $tagSearch . '" title="RSS Feed for \'' . $tagSearch . '\'"><img src="img/rss.svg" style="width: 32px;"></a></td>'; } echo '</tr></table></h1>'; $pageTitle = "Archive for '$tagSearch'"; } if (isset($CMS_RSS) && RSS_SHOW_FULL) { $CMS_SINGLE_POST = true; } $i = 0; $morePosts = 0; foreach($posts as $k=>$v) { if ($v->isPublic) { if (!isset($tagSearch) || in_array($tagSearch, $v->tags)) { if ($i++ >= $postsSkip) { if ($morePosts == 0) { if (isset($CMS_RSS)) { if (!isset($latestDisplayedPost)) { $latestDisplayedPost = $v->ctime; } echo '<item> <title>' . $v->title . '</title> <description><![CDATA['; require $v->includeFile; renderFooter(); echo ']]></description> <link><![CDATA[' . $v->permalink . ']]></link> <guid isPermaLink="true"><![CDATA[' . $v->permalink . ']]></guid> <pubDate>' . date(RSS_DATE_FORMAT, $v->ctime) . '</pubDate> <dc:creator>' . $v->author . '</dc:creator> </item>'; } else { echo '<div class="post">'; require $v->includeFile; renderFooter(); echo "</div>"; echo '<hr class="postSeparator">'; } } if (($i % POSTS_PAGE_SIZE) == 0) { $morePosts = 1; } else { if ($morePosts == 1) { $morePosts = 2; break; } else { $morePosts = 0; } } } } } } if (!isset($CMS_RSS)) { if ($morePosts == 2 || $postsSkip > 0) echo "<p><table class=\"blognav\"><tr>"; if ($morePosts == 2) 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 == 2|| $postsSkip > 0) echo "</tr></table></p>"; } } if (!isset($CMS_RSS)) echo "</blog>"; ?>