simpub/blog.php

324 lines
10 KiB
PHP

<?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 . '">&nbsp;&nbsp;&nbsp;&nbsp;[' . $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">&rarr; Click here to continue reading &larr;</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;
$firstRendered = 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 {
if ($firstRendered) {
echo '<hr class="postSeparator">';
}
$firstRendered = 1;
echo '<div class="post">';
require $v->includeFile;
renderFooter();
echo "</div>";
}
}
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) . '">&lt;&lt; Older posts</a></td>';
if ($postsSkip > 0)
echo '<td style="text-align:right;"><a href="' . "?p=blog$tagSearchUrl&s=" . ($postsPage-1) . '">Newer posts &gt;&gt;</a></td>';
if ($morePosts == 2|| $postsSkip > 0)
echo "</tr></table></p>";
}
}
if (!isset($CMS_RSS))
echo "</blog>";
?>