Location: PHPKode > projects > Second Crack > marcoarment-secondcrack-458f5aa/engine/Post.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;
    }
}
Return current item: Second Crack