Skip to content

Instantly share code, notes, and snippets.

@migurski
Last active June 18, 2016 00:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save migurski/76484b2df82a90838ea69c7b3bfe623b to your computer and use it in GitHub Desktop.
Save migurski/76484b2df82a90838ea69c7b3bfe623b to your computer and use it in GitHub Desktop.
<?php
// What's this blog's title?
$blog_title = "tecznotes";
// What's this blog's description (for outgoing RSS feed)?
$blog_description = "Michal Migurski's notebook, listening post, and soapbox.";
// What's this blog's primary language (for outgoing RSS feed)?
$blog_language = "en";
// Where are this blog's entries kept?
$datadir = "/Users/migurski/Sites/mike.teczno.com/blosxom";
// Where are this blog's comments kept?
$commentsdb = 'comments/comments3.db';
// What's my preferred base URL for this blog (leave blank for automatic)?
// $url = "http://mike.teczno.com/blosxom/blosxom.php?";
// Should I stick only to the datadir for items or travel down the
// directory hierarchy looking for items? If so, to what depth?
// 0 = infinite depth (aka grab everything), 1 = datadir only, n = n levels down
$depth = 0;
// How many entries should I show on the home page?
$num_entries = 5;
// What file extension signifies a blosxom entry?
$file_extension = 'txt';
// What is the default flavour?
$default_flavour = "html";
// What's this blog's timezone?
putenv('TZ=America/Los_Angeles');
// For the comment captchas
require 'recaptcha/recaptchalib.php';
$recaptcha_public_key = '...';
$recaptcha_private_key = '...';
// For the short URLs
require_once 'shlong/shlong.php';
function wikisyntax($body, $new_img=false)
{
// old, nano-blogger style naming
$body = preg_replace('/^DATE: .+\n/', '\1', $body);
if(!preg_match('/<\w+[^>]*>/', $body)) {
if($new_img) {
// links with thumbnails: [http://example.com/img.gif http://example.com/thumb.gif]
$body = preg_replace('#\[((http|https|mailto):\S+)\s+(http:\S+\.(gif|jpg|png))(\s+\.(\w+))?\]#i', '<a href="\1" target="_blank"><img src="\3" class="\6" border="1" alt=""/></a>', $body);
// just a picture
$body = preg_replace('#\[(http:\S+\.(gif|jpg|png))(\s+\.(\w+))?\]#', '<img src="\1" class="\4" border="1" alt=""/>', $body);
} else {
// links with thumbnails: [http://example.com/img.gif http://example.com/thumb.gif]
$body = preg_replace('#\[((http|https|mailto):\S+)\s+(http:\S+\.(gif|jpg|png))\]#i', '<a href="\1" target="_blank"><img class="old" src="\3" border="1" alt=""/></a>', $body);
// just a picture
$body = preg_replace('#\[(http:\S+\.(gif|jpg|png))\]#', '<img class="old" src="\1" border="1" alt=""/>', $body);
}
// pictures linked from text: [http://example.com/img.gif Text]
$body = preg_replace('#\[(http:\S+\.(gif|jpg|png))\s+(\S[^\]]*)\]#', '<a href="\1" target="_blank">\3</a>', $body);
// links with text: [http://example.com Text]
$body = preg_replace('#\[((http|https|mailto):\S+)\s+(\S[^\]]*)\]#', '<a href="\1">\3</a>', $body);
// //italic// and **bold**
$body = preg_replace('#([^:])//(.+?)//#is', '\1<em>\2</em>', $body);
$body = preg_replace('#\*\*(.+?)\*\*#is', '<strong>\1</strong>', $body);
$body = preg_replace('#--(.+?)--#is', '<span style="text-decoration: line-through;">\1</span>', $body);
$body = preg_replace('#__(.+?)__#is', '<span style="text-decoration: underline;">\1</span>', $body);
$new_body = '';
$in = '';
foreach(preg_split("/\n|\r|\r\n/", $body) as $line) {
if(preg_match('/^$/', $line)) {
if($in != '') {
$new_body .= "</${in}>";
$in = '';
}
} elseif(preg_match('/^\|\s*(.*)$/', $line, $m)) {
if($in == '') {
$in = 'blockquote';
$new_body .= '<blockquote>';
} elseif($in != 'blockquote') {
$new_body .= "</${in}><blockquote>";
$in = 'blockquote';
}
$new_body .= "${m[1]} ";
} elseif(preg_match('/^\*\s*(.*)$/', $line, $m)) {
if($in == '') {
$in = 'ul';
$new_body .= '<ul>';
} elseif($in != 'ul') {
$new_body .= "</${in}><ul>";
$in = 'ul';
}
$new_body .= "<li>${m[1]}</li> ";
} elseif(preg_match('/^#\s*(.*)$/', $line, $m)) {
if($in == '') {
$in = 'ol';
$new_body .= '<ol>';
} elseif($in != 'ol') {
$new_body .= "</${in}><ol>";
$in = 'ol';
}
$new_body .= "<li>${m[1]}</li> ";
} else {
if($in == '') {
$in = 'p';
$new_body .= '<p>';
} elseif($in != 'p') {
$new_body .= "</${in}><p>";
$in = 'p';
}
$new_body .= "${line} ";
}
}
$body = $new_body;
}
return $body;
}
function scrub_comment($content)
{
// handle special characters
$content = htmlspecialchars($content);
// link up URLs
$content = preg_replace('#(https?://\S+\w)#i', '<a href="\1">\1</a>', $content);
// take advantage of white-space: pre-wrap
return $content;
// convert various weird line breaks
$content = str_replace("\r\n", "\n", $content);
$content = str_replace("\r", "\n", $content);
// cut up into paragraphs
$content = preg_replace("#\n\n+#", '</p><p>', $content);
// replace remaining breaks
$content = str_replace("\n", '<br/>', $content);
return "<p>{$content}</p>";
}
//$script_dir = dirname($_SERVER['SCRIPT_NAME']);
//$request_dir = $_SERVER['SCRIPT_URL'];
//chdir(trim(substr($request_dir, strlen($script_dir)), '/'));
/**
* Pull one post by name.
*/
function get_post_by_name($post_path)
{
return array('name' => substr(basename($post_path), 0, -4),
'path' => preg_replace('#^(\./)*#', '', substr($post_path, 0, -4)),
'file' => $post_path,
'type' => 'file',
'time' => filemtime($post_path));
}
/**
* Pull posts out of a directory in reverse-chronological order.
*/
function get_posts_recursively($path='.')
{
static $post_lists;
if(!isset($post_lists))
$post_lists = array();
if(is_array($post_lists[$path]))
return $post_lists[$path];
$posts = array();
if($dir = opendir($path)) {
while(($file = readdir($dir)) !== false) {
$file_path = implode(DIRECTORY_SEPARATOR, array($path, $file));
switch(true) {
case substr($file, 0, 1) == '.':
// do nothing
break;
case is_dir($file):
$posts = array_merge($posts, get_posts_recursively($file_path));
break;
case substr($file, -4) == ".{$GLOBALS['file_extension']}":
$posts[] = get_post_by_name($file_path);
break;
}
}
usort($posts, create_function('$f1, $f2', 'return $f1["time"] < $f2["time"];'));
}
$post_lists[$path] = $posts;
return $post_lists[$path];
}
/**
* Pull posts by date in reverse-chronological order.
*/
function get_posts_dy_date($date_string)
{
$date_parts = explode('/', $date_string);
$year = intval($date_parts[0]);
$month = isset($date_parts[1]) ? intval($date_parts[1]) : 1;
$day = isset($date_parts[2]) ? intval($date_parts[2]) : 1;
if(!checkdate($month, $day, $year))
return array();
switch(count($date_parts)) {
case 1: // year
$compare = sprintf('%04d-', $year);
break;
case 2: // month
$compare = sprintf('%04d-%02d-', $year, $month);
break;
case 3: // day
$compare = sprintf('%04d-%02d-%02d ', $year, $month, $day);
break;
default: // uh...
return array();
}
$posts = array();
foreach(get_posts_recursively() as $post)
if(substr(date('Y-m-d ', $post['time']), 0, strlen($compare)) == $compare)
$posts[] = $post;
return $posts;
}
function get_page_title($path_info)
{
$path_parts = explode('/', $path_info);
$first_parts = array_slice($path_parts, 0, -1);
$last_part = end($path_parts);
$is_front = in_array($path_info, array('', 'index'));
$is_index = in_array($last_part, array('', 'index'));
switch(true) {
case $is_front:
return $GLOBALS['blog_title'];
case !$is_index && is_file("./{$path_info}.{$GLOBALS['file_extension']}"):
list($post_title, $post_body) = get_post_title_body("./{$path_info}.{$GLOBALS['file_extension']}");
return "{$post_title} ({$GLOBALS['blog_title']})";
case $is_index && is_dir(join(DIRECTORY_SEPARATOR, $first_parts)):
case $is_index && !in_array(0, array_map('is_numeric', $first_parts)):
return "posts ".(count($first_parts) <= 2 ? 'in' : 'on')." ".join(DIRECTORY_SEPARATOR, $first_parts)." ({$GLOBALS['blog_title']})";
default:
return "? ({$GLOBALS['blog_title']})";
}
}
function get_posts($path_info)
{
$path_parts = explode('/', $path_info);
$first_parts = array_slice($path_parts, 0, -1);
$last_part = end($path_parts);
$is_front = in_array($path_info, array('', 'index'));
$is_index = in_array($last_part, array('', 'index'));
switch(true) {
case $is_front:
return array_slice(get_posts_recursively(), 0, $GLOBALS['num_entries']);
case !$is_index && is_file("./{$path_info}.{$GLOBALS['file_extension']}"):
return array(get_post_by_name("./{$path_info}.{$GLOBALS['file_extension']}"));
case $is_index && is_dir(join(DIRECTORY_SEPARATOR, $first_parts)):
return array_slice(get_posts_recursively(join(DIRECTORY_SEPARATOR, $first_parts)), 0, $GLOBALS['num_entries']);
case $is_index && !in_array(0, array_map('is_numeric', $first_parts)):
return get_posts_dy_date(join(DIRECTORY_SEPARATOR, $first_parts));
default:
return array();
}
}
function get_post_title_body($post_path)
{
static $post_titles_bodies;
if(!isset($post_titles_bodies))
$post_titles_bodies = array();
if(is_array($post_titles_bodies[$post_path]))
return $post_titles_bodies[$post_path];
$post_titles_bodies[$post_path] = preg_split("/\n|\r|\r\n/", file_get_contents($post_path), 2);
return $post_titles_bodies[$post_path];
}
function get_post_comments($path)
{
$comments = array();
if($dbh = new SQLite3($GLOBALS['commentsdb'], SQLITE3_OPEN_READONLY))
{
$q = sprintf("SELECT post, author, url, content, timestamp
FROM comments
WHERE post = '%s'
ORDER BY timestamp ASC",
$dbh->escapeString($path));
$res = $dbh->query($q);
while($comment = $res->fetchArray(SQLITE3_ASSOC))
$comments[] = $comment;
$dbh->close();
}
return $comments;
}
function add_post_comment($path, $author, $url, $content)
{
if($dbh = new SQLite3($GLOBALS['commentsdb'], SQLITE3_OPEN_READWRITE))
{
$q = sprintf("INSERT INTO comments
(post, author, url, content, timestamp)
VALUES('%s', '%s', '%s', '%s', %d)",
$dbh->escapeString($path),
$dbh->escapeString($author),
$dbh->escapeString($url),
$dbh->escapeString($content),
time());
$res = $dbh->exec($q);
$dbh->close();
}
return $res;
}
function render_head($flavour, $page_title)
{
ob_start();
include "head.{$flavour}.php";
return ob_get_clean();
}
function render_post($post, $flavour)
{
extract($post); // -> name, path, type, time, file
list($title, $body) = get_post_title_body($file);
$comments = get_post_comments($path);
$captcha_html = recaptcha_get_html($GLOBALS['recaptcha_public_key'], null, false, 'Poof.');
$at_permalink = ("http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}" == "{$GLOBALS['url']}{$path}.{$GLOBALS['flavour']}");
$comments_ok = ($post['time'] > time() - 90*86400); // && ($post['time'] > strtotime("February 3 2008"));
ob_start();
include "post.{$flavour}.php";
return ob_get_clean();
}
function render_foot($flavour)
{
ob_start();
include "foot.{$flavour}.php";
return ob_get_clean();
}
function render_calendar($t=null)
{
$t = isset($t) ? $t : time();
$post_counts = array();
/*
foreach(get_posts_dy_date(date('Y/m', $t)) as $post) {
$post_day = intval(date('j', $post['time']));
$post_counts[$post_day] = isset($post_counts[$post_day]) ? $post_counts[$post_day] + 1 : 1;
}
*/
foreach(get_posts_dy_date(date('Y/m', $t)) as $post)
$post_counts[intval(date('j', $post['time']))] = true;
$calendar = '<table class="calendar">';
$calendar .= '<tr><th colspan="7" class="calendar_month_head"><a href="'.$GLOBALS['url'].date('Y/m', $t).'/">'.date('F Y', $t).'</a></th></tr>';
$calendar .= '<tr>
<th class="calendar_day_head">Su</th>
<th class="calendar_day_head">M</th>
<th class="calendar_day_head">Tu</th>
<th class="calendar_day_head">W</th>
<th class="calendar_day_head">Th</th>
<th class="calendar_day_head">F</th>
<th class="calendar_day_head">Sa</th>
</tr>';
$month_days = intval(date('t', $t));
$month_day = intval(date('j', $t));
$week_day = intval(date('w', $t));
for($start_day = $month_day - $week_day; $start_day > 1; $start_day -= 7) {
// keep decrementing...
}
// $d: day of month, $w: day of week
// increment both until we roll past the last day of the month and reach a saturday
for($d = $start_day, $w = 0; $d <= $month_days || $w % 7; $d++, $w = ($w + 1) % 7) {
if($w == 0)
$calendar .= '<tr>';
if($post_counts[$d]) {
$time = mktime(0, 0, 0, intval(date('m', $t)), $d, intval(date('Y', $t)));
$calendar .= '<td class="calendar_day_link"><a href="'.$GLOBALS['url'].date('Y/m/d', $time).'/">'.$d.'</a></td>';
} elseif($d >= 1 && $d <= $month_days) {
$calendar .= '<td class="calendar_day_nolink">'.$d.'</td>';
} else {
$calendar .= '<td class="calendar_day_noday">&nbsp;</td>';
}
if($w == 6)
$calendar .= '</tr>';
}
$calendar .= '</table>';
return $calendar;
}
function render_recentposts()
{
if($GLOBALS['flavour'] != 'html')
return '';
$recent = '<ol class="recentstories">';
foreach(array_slice(get_posts_recursively(), 0, 20) as $post) {
extract($post); // -> name, path, type, time, file
list($title, $body) = get_post_title_body($file);
$recent .= '<li><a href="'.$GLOBALS['url'].$path.'.'.$GLOBALS['flavour'].'">'.htmlspecialchars($title).'</a></li>';
}
$recent .= '</ol>';
return $recent;
}
function render_flatarchives()
{
if($GLOBALS['flavour'] != 'html')
return '';
$recent = '<ul class="flatarchives">';
$archives = array();
$year_ago = date('Y', time() - (86400 * 365));
foreach(get_posts_recursively() as $post) {
extract($post); // -> name, path, type, time, file
if(date('Y', $time) >= $year_ago) {
$m = date('Y-m', $time);
$posts = $archives[$m]['posts'] ? ($archives[$m]['posts'] + 1) : 1;
$name = date('F', $time);
$year = date('Y', $time);
$path = date('Y/m', $time);
$archives[$m] = compact('posts', 'name', 'year', 'path');
} else {
$y = date('Y', $time);
$posts = $archives[$y]['posts'] ? ($archives[$y]['posts'] + 1) : 1;
$name = date('Y', $time);
$year = ''; // not here
$path = date('Y', $time);
$archives[$y] = compact('posts', 'name', 'year', 'path');
}
}
foreach($archives as $archive)
$recent .= '<li><a href="'.$GLOBALS['url'].$archive['path'].'/">'.$archive['name'].'</a> '.$archive['year'].' ('.$archive['posts'].')</li>';
$recent .= '</ul>';
return $recent;
}
/*
function path_dates($path_info)
{
if(preg_match('#^(\d{4})(/(\d{1,2}))?(/(\d{1,2}))?$#', $path_info, $matches)) {
$y = intval($matches[1]);
$m = 1;
$d = 1;
$p = 'year';
if(isset($matches[3])) {
$m = intval($matches[3]);
$p = 'month';
}
if(isset($matches[5])) {
$d = intval($matches[5]);
$p = 'day';
}
if(checkdate($m, $d, $y)) {
$start = mktime(0, 0, 0, $m, $d, $y);
switch($p) {
case 'day':
$end = mktime(23, 59, 59, $m, $d, $y);
break;
case 'month':
$end = mktime(23, 59, 59, $m, intval(date('t', $start)), $y);
break;
case 'year':
$end = mktime(23, 59, 59, 12, 31, $y);
break;
}
}
return array($start, $end);
}
return false;
}
*/
if(empty($url))
$url = isset($_SERVER['REDIRECT_URL'])
? "http://{$_SERVER['HTTP_HOST']}".substr($_SERVER['REDIRECT_URL'], 0, strlen($_SERVER['REDIRECT_URL']) - strlen($_SERVER['REDIRECT_QUERY_STRING'])).'/'
: "http://{$_SERVER['HTTP_HOST']}{$_SERVER['SCRIPT_NAME']}?";
list($path_info, $flavour) = preg_split('/\.(\w+)$/', ltrim($_SERVER['QUERY_STRING'], '/'), 2, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
$flavour = $flavour ? $flavour : $default_flavour;
if($_SERVER['REQUEST_METHOD'] == 'POST') {
switch($_POST['action']) {
case 'comment':
$res = recaptcha_check_answer($GLOBALS['recaptcha_private_key'], $_SERVER['REMOTE_ADDR'], $_POST['recaptcha_challenge_field'], $_POST['recaptcha_response_field']);
if($res->is_valid && add_post_comment($path_info, $_POST['author'], $_POST['url'], $_POST['content'])) {
unset($_POST['author']);
unset($_POST['url']);
unset($_POST['content']);
} else {
$GLOBALS['comment_post_failed'] = true;
}
break;
}
}
switch($flavour) {
case 'html':
case 'htmlf':
case 'senate':
case 'congress':
header('Content-type: text/html');
break;
case 'rss':
header('Content-type: application/rss+xml');
break;
case 'xml':
header('Content-type: application/atom+xml');
break;
}
print render_head($flavour, get_page_title($path_info));
foreach(get_posts($path_info) as $post)
print render_post($post, $flavour);
print render_foot($flavour);
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment