292 lines
9.3 KiB
PHP
292 lines
9.3 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->isPublic = ($blogentry->status == "released");
|
|
}
|
|
$renderIt = $CMS_INDEXING || ($blogentry->status == "released") || ($blogentry->status == "unlisted");
|
|
|
|
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 class="date">' . date("l, F jS Y, H:i", $blogentry->ctime) . "</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_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->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\"><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 = false;
|
|
foreach($posts as $k=>$v) {
|
|
if ($v->isPublic) {
|
|
if (!isset($tagSearch) || in_array($tagSearch, $v->tags)) {
|
|
if ($i++ >= $postsSkip) {
|
|
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 = 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>";
|
|
}
|
|
if (!isset($CMS_RSS))
|
|
echo "</blog>";
|
|
?>
|