Initial commit
This commit is contained in:
commit
ccb900b158
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
downloads
|
||||||
|
b/*/*.*
|
||||||
|
!b/2017/00-template.php
|
||||||
|
!b/img/2017/fft.png
|
||||||
|
!b/pimg/keep
|
||||||
|
s/*
|
||||||
|
p/*
|
||||||
|
!p/00-template.php
|
18
README.MD
Normal file
18
README.MD
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[Simpub CMS] - A Simple CMS for Nerds
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Directories and Files
|
||||||
|
---------------------
|
||||||
|
* index.php: Main page, assembles layout and loads sub pages
|
||||||
|
* shared.php: Shared functions and global settings
|
||||||
|
* blog.php: Blog core (include from page in /p/ to use)
|
||||||
|
* page.php: Page core (include from page in /p/ to use)
|
||||||
|
* control.php: Admin interface
|
||||||
|
* style.css: Main style file
|
||||||
|
* userstyle.css: User-defined styles used on content pages
|
||||||
|
|
||||||
|
* /p/: User content (use 404.php as error handler)
|
||||||
|
* /b/: User blog posts. Create subdirectory with year, then drop .php blog posts into there
|
||||||
|
* /b/img: Again, create subfolder with year, drop images in there
|
||||||
|
* /b/pimg: Must be writable by www-data, will contain auto-generated thumbnails
|
||||||
|
* /s/: Resources for pages
|
44
_index.html
Normal file
44
_index.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Error 500 - Down for maintenance</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=700px, initial-scale=.5"><!-- or width=device-width -->
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="userstyle.css" />
|
||||||
|
</head>
|
||||||
|
<!--
|
||||||
|
Hello random person reading the source code! Nice to meet you!
|
||||||
|
This page has been generated by [Simpub CMS] which is open source software.
|
||||||
|
You can find it here: https://git.notsyncing.net:8080/web/simpub
|
||||||
|
-->
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<div id="navContainer">
|
||||||
|
<ul class="pagetitle"><li style="float: left;"><a class="pagetitle" href="/">[Simpub CMS]</a></li></ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div id="contentContainer">
|
||||||
|
<content>
|
||||||
|
<div id="content">
|
||||||
|
<p style="text-align:center;">
|
||||||
|
<table border=0>
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:14em;">500</td><td style="padding-left:50px; font-size:4em; text-align:left;">Down for maintenance!</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The site is currently down for maintenance.
|
||||||
|
Please try again later.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<div id="footer">
|
||||||
|
|x| I am falling, I am fading, I have lost it all... |x|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
b/2017/00-template.php
Normal file
55
b/2017/00-template.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
$blogentry = (object) array(
|
||||||
|
'file' => __FILE__,
|
||||||
|
'title' => 'Title',
|
||||||
|
'description' => 'Description',
|
||||||
|
'author' => 'Markus',
|
||||||
|
'tags' => 'video,tags',
|
||||||
|
'status' => 'unlisted', // released, private, unlisted
|
||||||
|
'ctime' => strtotime("2017-01-24 20:20")
|
||||||
|
);
|
||||||
|
if (!isset($CMS_ACTIVE) or !renderPost()) return;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
includeVideo("https://www.youtube.com/embed/fzQ6gRAEoy0");
|
||||||
|
|
||||||
|
// Preview can be "+" (auto gen), "" (take src) or "<url>" (take url)
|
||||||
|
// Link will normally link to self
|
||||||
|
includeGraphics("Random FFT image", "2017/fft.png", $preview="", $style="width:640px;", $link="http://notsyncing.net/");
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
if (extended()) {
|
||||||
|
?>
|
||||||
|
<h2>Hidden section</h2>
|
||||||
|
<p>
|
||||||
|
This will only show when opening the post, but not on the overview page.
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h2>Protected section</h2>
|
||||||
|
<?php
|
||||||
|
if (password("hello")) {
|
||||||
|
?>
|
||||||
|
<p>
|
||||||
|
This will only show after entering the correct password.
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h2>Some provided styles</h2>
|
||||||
|
<p>
|
||||||
|
<div class="code">
|
||||||
|
$ some formatted code <br>
|
||||||
|
$ foobar
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Inline <code>$code</code> is also possible.
|
||||||
|
</p>
|
BIN
b/img/2017/fft.png
Normal file
BIN
b/img/2017/fft.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
0
b/pimg/keep
Normal file
0
b/pimg/keep
Normal file
263
blog.php
Normal file
263
blog.php
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
<?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>";
|
||||||
|
?>
|
4
control.php
Normal file
4
control.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
apcu_clear_cache();
|
||||||
|
?>
|
||||||
|
Purged cache.
|
BIN
img/cc-byncsa.png
Normal file
BIN
img/cc-byncsa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 697 B |
96
index.php
Normal file
96
index.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
$CMS_ACTIVE = true;
|
||||||
|
|
||||||
|
require_once "shared.php";
|
||||||
|
|
||||||
|
$pageTitle = "";
|
||||||
|
if (isset($_GET['p'])) {
|
||||||
|
$page = $_GET['p'];
|
||||||
|
preg_match("/^[a-z0-9\-]*$/", $page, $matches); // SECURITY: Only lowercase a-z + numbers + -
|
||||||
|
if (!$matches)
|
||||||
|
$page=HOME_PAGE;
|
||||||
|
$page=substr($page, 0, 12); // SECURITY: Max 12 chars
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$page=HOME_PAGE;
|
||||||
|
}
|
||||||
|
$pageUrl="p/$page.php";
|
||||||
|
if (!file_exists($pageUrl)) {
|
||||||
|
$pageUrl="p/404.php";
|
||||||
|
$pageTitle = "Error 404";
|
||||||
|
}
|
||||||
|
// --- $page and $pageUrl are now SAFE TO USE
|
||||||
|
|
||||||
|
$htmlHead = "";
|
||||||
|
|
||||||
|
function mkMenu() {
|
||||||
|
global $page;
|
||||||
|
global $menuItems;
|
||||||
|
global $pageTitle;
|
||||||
|
|
||||||
|
foreach ($menuItems as $item) {
|
||||||
|
$menCls = "";
|
||||||
|
$titleStyle = "";
|
||||||
|
if (($_SERVER['REQUEST_URI'] == $item['link']) || (strstr($item['highlight'], $page))) {
|
||||||
|
$menCls = "class=\"selectedMenu\"";
|
||||||
|
$pageTitle = $item['label'];
|
||||||
|
}
|
||||||
|
echo '<li ' . $menCls . '><a href="' . $item['link'] . '"> ' . $item['label'] . ' </a></li> ';
|
||||||
|
// The in the line above can be removed when using a fixed width style in the .css file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($page==HOME_PAGE && !strstr($_SERVER['REQUEST_URI'], "&")) // TODO: The second clause is pretty cheap...
|
||||||
|
$homesel = 'class="selectedMenu"';
|
||||||
|
else
|
||||||
|
$homesel = "";
|
||||||
|
|
||||||
|
ob_start(function($output) {
|
||||||
|
global $pageTitle;
|
||||||
|
global $htmlHead;
|
||||||
|
$output = '<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>' . $pageTitle . ($pageTitle == "" ? "" : " - ") . PAGE_NAME . '</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=700px, initial-scale=.5"><!-- or width=device-width -->
|
||||||
|
' . $htmlHead . '
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="userstyle.css" />
|
||||||
|
</head>' . $output;
|
||||||
|
return $output;
|
||||||
|
});
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Hello random person reading the source code! Nice to meet you!
|
||||||
|
This page has been generated by [Simpub CMS] which is open source software.
|
||||||
|
You can find it here: https://git.notsyncing.net:8080/web/simpub
|
||||||
|
-->
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<div id="navContainer">
|
||||||
|
<ul class="pagetitle"><li <?php echo $homesel; ?> style="float: left;"><a class="pagetitle" href="/"><?php echo PAGE_NAME; ?></a></li></ul>
|
||||||
|
<ul>
|
||||||
|
<?php mkMenu(); ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div id="contentContainer">
|
||||||
|
<content>
|
||||||
|
<div id="content">
|
||||||
|
<?php
|
||||||
|
require $pageUrl;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<div id="footer">
|
||||||
|
Copyright <?php echo PAGE_AUTHOR; ?><!-- and <a href="/?p=extcontent">others</a>--> | <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/us/"><img style="position:relative;top:4px;opacity:0.75;" src="/img/cc-byncsa.png"></a> | <?php echo date("Y"); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
ob_end_flush();
|
||||||
|
?>
|
20
p/00-template.php
Normal file
20
p/00-template.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
if (!isset($CMS_ACTIVE)) exit();
|
||||||
|
require("page.php");
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>Subpage template</h1>
|
||||||
|
<p>
|
||||||
|
This is a subpage. The following code demonstrates the features of page.php.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
beginEntry("Category 0");
|
||||||
|
addEntry("Entry 0", "b/img/2016/fft.png", "//link", "Description");
|
||||||
|
addEntry("Entry 1", "b/img/2016/fft.png", "//link", "Description");
|
||||||
|
endEntry();
|
||||||
|
beginEntry("Category 1");
|
||||||
|
addEntry("Entry 2", "b/img/2016/fft.png", "//link", "Description");
|
||||||
|
addEntry("Entry 3", "b/img/2016/fft.png", "//link", "Description");
|
||||||
|
endEntry();
|
||||||
|
?>
|
13
page.php
Normal file
13
page.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
if (!isset($CMS_ACTIVE)) exit();
|
||||||
|
function beginEntry($name) {
|
||||||
|
echo "<div class=\"post\"><details open><summary class=\"pageDetailsHeading\">$name<!--<h2>$name</h2>--></summary><table class=\"listing\">";
|
||||||
|
}
|
||||||
|
function addEntry($title, $preview, $link, $description) {
|
||||||
|
//echo '<tr><td colspan="2"><h3>' . $title . '</h3></td></tr>';
|
||||||
|
echo '<tr><td style="border-left:2px solid blue;"><a href="' . $link . '"><h3>' . $title . '</h3><img class="previewImage" width=320px src="' . $preview . '"></a></td><td style="border-right:2px solid blue;"><h3> </h3><br>' . $description . '</td></tr><tr><td> </td></tr>';
|
||||||
|
}
|
||||||
|
function endEntry() {
|
||||||
|
echo '</table></details></div><hr class="postSeparator">';
|
||||||
|
}
|
||||||
|
?>
|
138
shared.php
Normal file
138
shared.php
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
define('PAGE_NAME', '[Simpub CMS]'); // Title of the page
|
||||||
|
define('PAGE_AUTHOR', 'Simpub CMS'); // Author of the page (used in footer)
|
||||||
|
|
||||||
|
define('HOME_PAGE', 'blog'); // Entry page (and link for logo)
|
||||||
|
define('CACHE_POSTS_TTL', 3600); // Time to cache the posts list (1h)
|
||||||
|
define('POSTS_BASE', "/b/"); // Root directory of the posts (relative)
|
||||||
|
define('POSTS_ROOT', $_SERVER['DOCUMENT_ROOT'] . POSTS_BASE); // Root directory of the posts (absolute)
|
||||||
|
define('POSTS_PAGE_SIZE', 10); // Numbers of posts on a page
|
||||||
|
|
||||||
|
define('POSTS_DIR_SEP', "."); // Directory separator (/) in post names
|
||||||
|
define('POSTS_MAX_FILENAME', 30); // Maximum length of a post name (including suffix)
|
||||||
|
define('POSTS_TAG_SEARCH', 30); // Maximum length for the tag search argument
|
||||||
|
|
||||||
|
$menuItems = array(
|
||||||
|
array("label" => "Page A", "link" => "?p=00-template", "highlight" => "related-subpages,00-template"),
|
||||||
|
array("label" => "Page B", "link" => "?p=404", "highlight" => "404"),
|
||||||
|
array("label" => "Repository", "link" => "https://git.notsyncing.net:8080", "highlight" => ""),
|
||||||
|
array("label" => "Contact", "link" => "?p=00-template", "highlight" => "")
|
||||||
|
);
|
||||||
|
|
||||||
|
$blogDirs = array("2017");
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will include an image as figure.
|
||||||
|
* returns : Number of the figure for later referencing.
|
||||||
|
* $title : Image title, will be prefixed with Figure n.
|
||||||
|
* $path : If $path starts with http or / it will be
|
||||||
|
* treated as an absolute path and no downscaled preview will be rendered.
|
||||||
|
* Other paths will be prefixed with /b/img.
|
||||||
|
* $preview : When set to "", no preview will be generated. Otherwise the specified
|
||||||
|
* path will be used.
|
||||||
|
* $link : Link to follow when clicking the image. Default is the full resolution image.
|
||||||
|
*/
|
||||||
|
$counters['figure'] = 1;
|
||||||
|
function includeGraphics($title, $path, $preview="+", $style="", $link="+") {
|
||||||
|
global $blogentry;
|
||||||
|
global $counters;
|
||||||
|
|
||||||
|
$title = "Figure " . $counters['figure'] . ". $title";
|
||||||
|
|
||||||
|
// TODO: External image
|
||||||
|
if (substr($path, 0, 4) == "http" || $path[0] == "/") {
|
||||||
|
$preview="";
|
||||||
|
$spath=$path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$spath = POSTS_BASE . "img/" . $path;
|
||||||
|
$fpath = $_SERVER['DOCUMENT_ROOT'] . $spath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($preview == "+") {
|
||||||
|
$previewPath = POSTS_BASE . "pimg/" . str_replace("/", "_", $path);
|
||||||
|
$fpreviewPath = $_SERVER['DOCUMENT_ROOT'] . $previewPath;
|
||||||
|
if (!file_exists($fpreviewPath)) {
|
||||||
|
preg_match("/^[A-Za-z_\.\-0-9\/]*$/", $fpath, $matches); // SANITIZED!
|
||||||
|
if (!$matches) {
|
||||||
|
echo "<p>SERVER ERROR: Image not valid!</p>";
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
$page=substr($page,0,12); // SECURITY: Max 12 chars
|
||||||
|
exec("gm convert '$fpath' -resize 640 '$fpreviewPath';");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($preview == "") {
|
||||||
|
$previewPath = $spath;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$previewPath = $preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($link == "+")
|
||||||
|
$link = $spath;
|
||||||
|
|
||||||
|
echo '<figure>
|
||||||
|
<a href="' . $link . '">
|
||||||
|
<img style="' . $style . '" title="' . $title . '" style="margin: 0px;padding: 0px;" src="' . $previewPath . '">
|
||||||
|
</a>
|
||||||
|
<figcaption>' . $title . '</figcaption>
|
||||||
|
</figure>';
|
||||||
|
|
||||||
|
return $counters['figure']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will include a video.
|
||||||
|
* returns : Number of the video for later referencing.
|
||||||
|
* $url : URL of the video. Autodetects YouTube. Otherwise HTML5 video.
|
||||||
|
* $title : Display "Video n. $title" below the video.
|
||||||
|
* $width : Width of the player
|
||||||
|
*/
|
||||||
|
$counters['video'] = 1;
|
||||||
|
function includeVideo($url, $title="", $width=640) {
|
||||||
|
global $counters;
|
||||||
|
// TODO: HTML5 video
|
||||||
|
echo '<videoframe><div class="videoframe" style="width:' . $width . 'px;height:' . $width*9/16 . 'px">';
|
||||||
|
echo '<iframe width="100%" height="100%" src="' . $url . '" frameborder="0" allowfullscreen></iframe>';
|
||||||
|
echo '</div></videoframe>';
|
||||||
|
if ($title != "") {
|
||||||
|
echo '<vidcaption>Video ' . $counters['video'] . ". $title</vidcaption>";
|
||||||
|
}
|
||||||
|
return $counters['video']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will add a reference
|
||||||
|
* returns : Number of the source for later referencing
|
||||||
|
* $url : URL of the source, if its a number it will not create a new entry.
|
||||||
|
*/
|
||||||
|
$meta['sources'] = array();
|
||||||
|
$counters['sources'] = 1;
|
||||||
|
function addSource($url) {
|
||||||
|
global $counters;
|
||||||
|
global $meta;
|
||||||
|
if (is_numeric($url)) {
|
||||||
|
echo "<a href=\"#src$url\">[" . $url . "]</a>";
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check whether source already exists
|
||||||
|
$i = 1;
|
||||||
|
foreach ($meta['sources'] as $v) {
|
||||||
|
if ($v == $url) {
|
||||||
|
echo "<a href=\"#src$i\">[" . $i . "]</a>";
|
||||||
|
return $i;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
// If not found, then add
|
||||||
|
$meta['sources'][$counters['sources']] = $url;
|
||||||
|
echo "<a href=\"#src$i\">[" . $counters['sources'] . "]</a>";
|
||||||
|
return $counters['sources']++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
277
style.css
Normal file
277
style.css
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/* ------------- General ------------- */
|
||||||
|
body {
|
||||||
|
padding:0px;
|
||||||
|
margin:0px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
min-width:724px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- Nav bar ------------- */
|
||||||
|
nav {
|
||||||
|
width:100%;
|
||||||
|
height:50px;
|
||||||
|
background-color:#151515;
|
||||||
|
box-shadow: 0px 0px 4px white;
|
||||||
|
border-bottom: 1px solid #222222;
|
||||||
|
}
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
nav li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
margin-top: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
nav a {
|
||||||
|
padding-top:12px;
|
||||||
|
padding-bottom:9px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
color: #285064;
|
||||||
|
}
|
||||||
|
nav ul a {
|
||||||
|
/*FIXEDWITH width: 80px;*/
|
||||||
|
padding-top:12px;
|
||||||
|
padding-bottom:14px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
color: #d6d6d6; /*#c21234*/
|
||||||
|
}
|
||||||
|
nav a:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
border-bottom: 2px solid #ff1234;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
.selectedMenu {
|
||||||
|
border-bottom: 2px solid #d91234;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
.pagetitle {
|
||||||
|
float: left;
|
||||||
|
width:auto;
|
||||||
|
font-family: monospace;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
font-size: 20pt;
|
||||||
|
margin-top: -4px;
|
||||||
|
padding-bottom: 9px;
|
||||||
|
}
|
||||||
|
#navContainer {
|
||||||
|
max-width: 1280px;
|
||||||
|
padding:0px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------- Content ------------- */
|
||||||
|
#contentContainer {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
background-color: black;
|
||||||
|
max-width: 1280px;
|
||||||
|
padding:12px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid #444444;
|
||||||
|
text-align:justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
background-color: #333333;
|
||||||
|
max-width: 1280px;
|
||||||
|
padding:10px;
|
||||||
|
padding-left:12px;
|
||||||
|
padding-right:12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border: 1px solid #eeeeee;
|
||||||
|
text-align:center;
|
||||||
|
font-size: 10pt;
|
||||||
|
color: #eeeeee;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer a {
|
||||||
|
color: #222222;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- Elements ------------- */
|
||||||
|
content {
|
||||||
|
font-size:11pt;
|
||||||
|
}
|
||||||
|
content img {
|
||||||
|
|
||||||
|
}
|
||||||
|
content figure {
|
||||||
|
font-style: italic;
|
||||||
|
margin-left:16px;
|
||||||
|
}
|
||||||
|
content figcaption {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
content .videoframe {
|
||||||
|
margin-left:16px;
|
||||||
|
box-shadow: 0px 0px 3px white;
|
||||||
|
border: 1 px solid #f3f3f3;
|
||||||
|
}
|
||||||
|
content vidcaption {
|
||||||
|
margin-left: 18px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
content a {
|
||||||
|
color:#aaaaaa;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
content pre {
|
||||||
|
background-color:#dddddd;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- Blog ------------- */
|
||||||
|
blog .head {
|
||||||
|
font-size:10pt;
|
||||||
|
text-align:right;
|
||||||
|
vertical-align:top;
|
||||||
|
}
|
||||||
|
blog .author {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
blog .date {
|
||||||
|
|
||||||
|
}
|
||||||
|
blog .tags {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
blog .tags a {
|
||||||
|
background-color:#222222;
|
||||||
|
color: white;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
blog .sources {
|
||||||
|
font-size: 9pt;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
blog h2 {
|
||||||
|
margin-top:40px;
|
||||||
|
}
|
||||||
|
blog img {
|
||||||
|
width: 640px;
|
||||||
|
box-shadow: 0px 0px 3px white;
|
||||||
|
border: 1 px solid #f3f3f3;
|
||||||
|
}
|
||||||
|
blog .bloginfoheader { /* Stuff like Search results for 'xx' */
|
||||||
|
background-color:gray;
|
||||||
|
color:#f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
blogheading h1 {
|
||||||
|
/*margin:0px;*/
|
||||||
|
}
|
||||||
|
blogheading table {
|
||||||
|
padding:0px;
|
||||||
|
margin:0px;
|
||||||
|
margin-bottom:-18px; /* Cheap fix to correct some spacing */
|
||||||
|
margin-left:-2px;
|
||||||
|
margin-top:-2px;
|
||||||
|
border: 0px;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
blogheading td {
|
||||||
|
margin:0px;
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
blogheading tr {
|
||||||
|
margin:0px;
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
blogheading a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
.postSeparator {
|
||||||
|
border:0px;
|
||||||
|
margin-top: 20px;
|
||||||
|
/*border-bottom: 1px solid black;
|
||||||
|
box-shadow: 1px 1px 2px black;*/
|
||||||
|
}
|
||||||
|
.blognav {
|
||||||
|
margin-top:30px;
|
||||||
|
margin-left:10px;
|
||||||
|
margin-right:10px;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
.blognav a {
|
||||||
|
background-color: #cccccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid black;
|
||||||
|
transition-duration: .3s;
|
||||||
|
}
|
||||||
|
.blognav a:hover {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
.post {
|
||||||
|
background-color:#121212; /* #f2f2f2 */
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
margin: -2px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.sourceSeparator {
|
||||||
|
border:none;
|
||||||
|
border-bottom: 1px solid #bbbbbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- Entries ------------- */
|
||||||
|
.pageDetailsHeading {
|
||||||
|
font-size:1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0.3em;
|
||||||
|
margin-left: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.listing {
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
.listing td {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.listing .previewImage {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- Generic elements ------------- */
|
||||||
|
.code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: #080808;
|
||||||
|
border: white dashed 1px;
|
||||||
|
padding: 3px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
text-align: left;
|
||||||
|
/*white-space: pre-wrap;*/
|
||||||
|
/*line-height: 1.8em;*/
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: #080808;
|
||||||
|
border: white dashed 1px;
|
||||||
|
padding: 2px;
|
||||||
|
line-height: 1.8em;
|
||||||
|
}
|
||||||
|
|
0
userstyle.css
Normal file
0
userstyle.css
Normal file
Loading…
Reference in New Issue
Block a user