site/_site/tmp/var/www/chaospott.de/engine/Post.php
2014-10-26 19:35:05 +01:00

327 lines
14 KiB
PHP

<?php
$fdir = dirname(__FILE__);
require_once($fdir . '/PHPMarkdownExtra/markdown.php');
require_once($fdir . '/PHPSmartyPants/smartypants.php');
require_once($fdir . '/Updater.php');
require_once($fdir . '/Template.php');
require_once($fdir . '/Utils.php');
class Post
{
public static $blog_title = 'Untitled Blog';
public static $blog_url = 'http://no-idea.com/';
public static $blog_description = 'About my blog.';
public $source_filename = '';
public $title = '';
public $is_draft = false;
public $publish_now = false;
public $timestamp = 0;
public $slug = '';
public $type = '';
public $headers = array();
public $tags = array();
public $body = '';
public $year;
public $month;
public $day;
public $offset_in_day;
public $is_first_post_on_this_date = false; // Used for index-page rendering
public static function from_files(array $filenames)
{
$posts = array();
$last_date = false;
foreach ($filenames as $f) {
if (! file_exists($f)) continue;
$post = new Post($f);
$date = $post->year . $post->month . $post->day;
if ($date != $last_date) {
$post->is_first_post_on_this_date = true;
$last_date = $date;
}
$posts[] = $post;
}
return $posts;
}
public static function next_sequence_number_for_day($filename_prefix)
{
$max = 0;
foreach (glob($filename_prefix . '*' . Updater::$post_extension) as $filename) {
$n = intval(substr(substring_after($filename, $filename_prefix), 0, 2));
if ($n > $max) $max = $n;
}
return str_pad($max + 1, 2, '0', STR_PAD_LEFT);
}
public function __construct($source_filename, $is_draft = -1 /* auto */)
{
$this->source_filename = $source_filename;
$this->is_draft = ($is_draft === -1 ? (false !== strpos($source_filename, 'drafts/') || false !== strpos($source_filename, 'pages/')) : $is_draft);
$this->timestamp = filemtime($source_filename);
$segments = preg_split( '/\R\R/', trim(file_get_contents($source_filename)), 2);
if (! isset($segments[1])) $segments[1] = '';
if (count($segments) > 1) {
// Read headers for Tag, Type values
$headers = explode("\n", $segments[0]);
$has_title_yet = false;
foreach ($headers as $header) {
if (isset($header[0]) && $header[0] == '=') {
$has_title_yet = true;
continue;
}
if (! $has_title_yet) {
$has_title_yet = true;
$this->title = $header;
continue;
}
if ($this->is_draft && strtolower($header) == 'publish-now') {
$this->publish_now = true;
continue;
}
$fields = explode(':', $header, 2);
if (count($fields) < 2) continue;
$fname = strtolower($fields[0]);
$fields[1] = trim($fields[1]);
if ($fname == 'tags') {
$this->tags = self::parse_tag_str($fields[1]);
} else if ($fname == 'type') {
$this->type = str_replace('|', ' ', $fields[1]);
} else if ($fname == 'published') {
$this->timestamp = strtotime($fields[1]);
} else {
$this->headers[$fname] = $fields[1];
}
if (isset($this->headers['link'])) $this->type = 'link';
}
array_shift($segments);
}
$this->body = isset($segments[0]) ? $segments[0] : '';
$filename = basename($source_filename);
$filename_datestr = substr($filename, 0, 8);
if (is_numeric($filename_datestr)) {
$this->year = intval(substr($filename_datestr, 0, 4));
$this->month = intval(substr($filename_datestr, 4, 2));
$this->day = intval(substr($filename_datestr, 6, 2));
$this->offset_in_day = intval(substr($filename, 9, 2));
$this->slug = ltrim(substr($filename, 11, -(strlen(Updater::$post_extension))), '-');
} else {
$this->year = intval(idate('Y', $this->timestamp));
$this->month = intval(idate('m', $this->timestamp));
$this->day = intval(idate('d', $this->timestamp));
$posts_in_ym = Updater::posts_in_year_month($this->year, $this->month);
$this->offset_in_day = isset($posts_in_ym[$this->day]) ? count($posts_in_ym[$this->day]) + 1 : 1;
$this->slug = substring_before($filename, '.');
}
if (! $this->is_draft && $source_filename != $this->expected_source_filename()) {
error_log("Expected filename mismatch:\nfilename: $source_filename\nexpected: " . $this->expected_source_filename());
}
}
public function expected_source_filename($for_new_file = false)
{
$padded_month = str_pad($this->month, 2, '0', STR_PAD_LEFT);
$padded_day = str_pad($this->day, 2, '0', STR_PAD_LEFT);
$prefix =
Updater::$source_path . '/posts/' .
$this->year . '/' . $padded_month . '/' .
$this->year . $padded_month . $padded_day . '-'
;
if ($for_new_file) $padded_offset = self::next_sequence_number_for_day($prefix);
else $padded_offset = str_pad($this->offset_in_day, 2, '0', STR_PAD_LEFT);
if ($for_new_file) {
$slug = trim(preg_replace('/[^a-z0-9-]+/ms', '-', strtolower($this->slug)), '-');
if (! $slug) $slug = 'post';
} else {
$slug = $this->slug;
}
return $prefix . $padded_offset . '-' . strtolower($slug) . Updater::$post_extension;
}
public function normalized_source()
{
$out_headers = $this->headers;
if ($this->is_draft) unset($out_headers['published']);
else $out_headers['published'] = date('Y-m-d h:i:sa', $this->timestamp);
$source = $this->title . "\n" . str_repeat('=', max(10, min(40, strlen($this->title)))) . "\n";
if ($this->type) $out_headers['type'] = $this->type;
if ($this->tags) $out_headers['tags'] = implode(', ', $this->tags);
foreach ($out_headers as $k => $v) $source .= ucfirst($k) . ': ' . $v . "\n";
$source .= "\n" . $this->body;
return $source;
}
public function array_for_template()
{
$tags = array();
foreach ($this->tags as $tag) { $tags[$tag] = array('post-tag' => $tag); }
// Convert relative image references to absolute so index pages work
$base_uri = '/' . $this->year . '/' . str_pad($this->month, 2, '0', STR_PAD_LEFT) . '/' . str_pad($this->day, 2, '0', STR_PAD_LEFT);
return array_merge(
$this->headers,
array(
'post-title' => html_entity_decode(SmartyPants($this->title), ENT_QUOTES, 'UTF-8'),
'post-slug' => $this->slug,
'post-timestamp' => $this->timestamp,
'post-rss-date' => date('D, d M Y H:i:s T', $this->timestamp),
'post-body' => SmartyPants(Markdown($this->body)),
'post-tags' => $tags,
'post-type' => $this->type,
'post-permalink' => $base_uri . '/' . $this->slug,
'post-permalink-or-link' => isset($this->headers['link']) && $this->headers['link'] ? $this->headers['link'] : $base_uri . '/' . $this->slug,
'post-absolute-permalink' => rtrim(self::$blog_url, '/') . $base_uri . '/' . $this->slug,
'post-absolute-permalink-or-link' => rtrim(self::$blog_url, '/') . (isset($this->headers['link']) && $this->headers['link'] ? $this->headers['link'] : $base_uri . '/' . $this->slug),
'post-is-first-on-date' => $this->is_first_post_on_this_date ? 'yes' : '',
)
);
}
public function write_page()
{
$t = new Template(Updater::$page_template);
$t->content = array(
'page-title' => html_entity_decode(SmartyPants($this->title), ENT_QUOTES, 'UTF-8'),
'page-body' => SmartyPants(Markdown($this->body)),
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
'blog-url' => self::$blog_url,
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
'page-type' => 'page',
'archives' => array(),
'previous_page_url' => false,
'next_page_url' => false,
);
$output_html = $t->outputHTML();
if (! file_exists(Updater::$dest_path)) mkdir_as_parent_owner(Updater::$dest_path, 0755, true);
file_put_contents_as_dir_owner(Updater::$dest_path . '/' . $this->slug, $output_html);
}
public function write_permalink_page($draft = false)
{
$post_data = $this->array_for_template();
$post_data['post-is-first-on-date'] = true;
$t = new Template(Updater::$permalink_template);
$t->content = array_merge(
$post_data,
array(
'page-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
'blog-url' => self::$blog_url,
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
'page-type' => 'post',
'posts' => array($post_data),
'post' => $post_data,
'archives' => array(),
'previous_page_url' => false,
'next_page_url' => false,
)
);
$output_html = $t->outputHTML();
if (! $draft || Updater::$write_public_drafts) {
$output_www_path = $draft ? '/drafts' : dirname($post_data['post-permalink']);
$output_path = Updater::$dest_path . $output_www_path;
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
file_put_contents_as_dir_owner(Updater::$dest_path . ($draft ? '/drafts/' . $this->slug : $post_data['post-permalink']), $output_html);
}
if ($draft) file_put_contents_as_dir_owner(Updater::$source_path . '/drafts/_previews/' . $this->slug . '.html', $output_html);
}
public static function write_index($dest_path, $title, $type, array $posts, $template = 'main.html', $archive_array = false, $seq_count = 0)
{
$posts_data = array();
foreach ($posts as $p) $posts_data[] = $p->array_for_template();
$t = new Template($template);
$t->content = array(
'page-title' => html_entity_decode(SmartyPants($title), ENT_QUOTES, 'UTF-8'),
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
'blog-url' => self::$blog_url,
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
'page-type' => $type,
'posts' => $posts_data,
'previous_page_url' => false,
'next_page_url' => $seq_count > 1 ? substring_after(substring_before($dest_path, '.', true), Updater::$dest_path) . '-2' : false,
'archives' => $archive_array ? $archive_array : array(),
);
$output_html = $t->outputHTML();
$output_path = dirname($dest_path);
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
file_put_contents_as_dir_owner($dest_path, $output_html);
}
public function write_index_sequence($dest_path, $title, $type, array $posts, $template = 'main.html', $archive_array = false, $posts_per_page = 20)
{
$sequence = 0;
$new_dest_path = $dest_path;
// $dest_uri = substring_after(substring_before($dest_path, '.', true), Updater::$dest_path);
$dest_uri = substring_after(substring_after($dest_path, '/', true), Updater::$dest_path);
$total_sequences = ceil(count($posts) / $posts_per_page);
while ($posts) {
$sequence++;
$seq_array = array_splice($posts, 0, $posts_per_page);
if ($sequence == 1) continue; // skip writing the redundant "-1" page
$new_dest_path = $dest_path . '-' . $sequence . '.html';
$posts_data = array();
foreach ($seq_array as $p) $posts_data[] = $p->array_for_template();
$t = new Template($template);
$t->content = array(
'page-title' => html_entity_decode(SmartyPants($title), ENT_QUOTES, 'UTF-8'),
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
'blog-url' => self::$blog_url,
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
'page-type' => $type,
'posts' => $posts_data,
'previous_page_url' => $sequence != 1 ? ($sequence == 2 ? $dest_uri : $dest_uri . '-' . ($sequence - 1)) : false,
'next_page_url' => $sequence < $total_sequences ? $dest_uri . '-' . ($sequence + 1) : false,
'archives' => $archive_array ? $archive_array : array(),
);
$output_html = $t->outputHTML();
$output_path = dirname($new_dest_path);
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
file_put_contents_as_dir_owner($new_dest_path, $output_html);
}
return $total_sequences;
}
public static function parse_tag_str($tag_str)
{
// Tags are comma-separated, and any spaces between multiple words in a tag will be converted to underscores for URLs
if (! strlen($tag_str)) return array();
$tags = array_map('trim', explode(',', strtolower($tag_str)));
$tags = str_replace(' ', '_', $tags);
$tags = array_unique(array_filter($tags, 'strlen'));
sort($tags);
return $tags;
}
}