Skip to content

Instantly share code, notes, and snippets.

@jes
Created December 6, 2019 22:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jes/88e3e587413d5794f69c281bdd14a27f to your computer and use it in GitHub Desktop.
Save jes/88e3e587413d5794f69c281bdd14a27f to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
# jes's crappy blog generator
use strict;
use warnings;
use DateTime;
use DateTime::Format::Strptime;
use File::Copy;
use JSON qw(decode_json);
use POSIX;
my $PUBLIC_DIR = "public";
my $REL_PUBLIC_DIR = "..";
my $TAGDIR = "public/tags";
my $INDEXHTML = "public/index.html";
my $RSSFILE = "public/rss.xml";
my $SITEMAPFILE = "public/sitemap.xml";
my $HOST_PATH = "http://incoherency.co.uk/blog";
my $HEADERHTML = read_file('templates/header');
my $FOOTERHTML = read_file('templates/footer');
my $STORYFOOTERHTML = read_file('templates/storyfooter');
my $INDEXINTROHTML = read_file('templates/index');
my $STORYDESCRHTML = read_file('templates/storydescr');
my $RSSHEADER = read_file('templates/rssheader');
my $RSSITEM = read_file('templates/rssitem');
my $RSSFOOTER = read_file('templates/rssfooter');
my $SITEMAPHEADER = read_file('templates/sitemapheader');
my $SITEMAPITEM = read_file('templates/sitemapitem');
my $SITEMAPFOOTER = read_file('templates/sitemapfooter');
my $date_parser = DateTime::Format::Strptime->new(
pattern => "%Y%m%d",
);
my @stories;
my @pages;
push @stories, read_story($_, 'stories/') for sort glob("stories/*");
push @pages, read_story($_, 'pages/') for sort glob("pages/*");
@stories = sort { $b->{date} cmp $a->{date} } @stories;
my $LATEST_DATE = $stories[0]->{datetime}->strftime("%a, %d %b %Y 12:00:00 GMT");
my $LATEST_SITEMAP = $stories[0]->{datetime}->strftime("%Y-%m-%d");
copy("templates/bootstrap.min.css", "public/bootstrap.min.css");
copy("templates/bootstrap.min.css", "public/stories/bootstrap.min.css");
copy("templates/bootstrap.min.css", "public/pages/bootstrap.min.css");
copy("templates/bootstrap.min.css", "public/tags/bootstrap.min.css");
write_story($_) for @stories;
write_story($_) for @pages;
write_index(\@stories);
write_rss(@stories);
write_sitemap(@stories, @pages);
write_tags(@stories);
sub read_story {
my ($file, $prefix) = @_;
return if ! -f $file;
local $SIG{__DIE__} = sub { die "$file: ", @_ };
open( my $fh, '<', $file )
or die "can't read $file: $!";
my $json = <$fh>;
my $story = decode_json($json);
return () if $story->{draft};
my $text = '';
while (<$fh>) {
$text .= $_;
}
close $fh;
$story->{text} = $text;
my @lines = split /\n\n/, $text;
$story->{html} = $story->{htmlpage} ? $text : join("\n", map { "<p>$_</p>" } @lines);
if ($story->{tags}) {
$story->{tagshtml} = "<p><i style=\"font-size:14px\">Tagged: " . join(', ', map { tag_link_html($_,'') } split /,/, $story->{tags}) . "</i></p>";
}
$story->{tags} = join(',', ($story->{tags}||()), 'all');
(my $stub = $file) =~ s!$prefix!!;
$story->{stub} = $stub;
$story->{descr} = $lines[0];
$story->{rss_descr} = $story->{descr};
$story->{rss_descr} =~ s/<[^>]*>//g;
my $d = $story->{date} ? $date_parser->parse_datetime($story->{date}) : DateTime->now();
$story->{datetime} = $d;
$story->{textdate} = $d->strftime("%a %e %B %Y") if $story->{date};
$story->{textdate_rfc822} = $d->strftime("%a, %d %b %Y 12:00:00 GMT") if $story->{date};
$story->{textdate_sitemap} = $d->strftime("%Y-%m-%d");
$story->{outputfile} = "$PUBLIC_DIR/$prefix$stub.html";
$story->{relpath} = "$REL_PUBLIC_DIR/$prefix$stub.html";
$story->{abspath} = "$HOST_PATH/$prefix$stub.html";
return $story;
}
sub write_story {
my ($story) = @_;
open( my $fh, '>', $story->{outputfile} )
or die "can't write $story->{outputfile}: $!";
print $fh template($HEADERHTML, $story);
print $fh $story->{html};
print $fh template($STORYFOOTERHTML, $story) if $story->{date};
print $fh template($FOOTERHTML, $story);
close $fh;
}
sub tag_link_html {
my ($tag, $unlink_tag, $is_root) = @_;
return $tag if $tag eq $unlink_tag;
my $url = $is_root ? "tags/$tag.html" : "../tags/$tag.html";
return "<a style=\"font-size: 14px\" href=\"$url\">$tag</a>";
}
sub write_index {
my ($stories_r, $title, $filename, $is_root, $cur_tag) = @_;
# get list of all tags
my %tag;
for my $story (@stories) {
next if !$story->{tags};
$tag{$_}++ for split /,/, $story->{tags};
}
my @all_tags = sort { $tag{$b} <=> $tag{$a} || $a cmp $b } keys %tag;
my @stories = @$stories_r;
$title = 'Blog' if !defined $title;
$filename = $INDEXHTML if !defined $filename;
$is_root = 1 if !defined $is_root;
$cur_tag = 'all' if !defined $cur_tag;
open( my $fh, '>', $filename )
or die "can't write $filename: $!";
print $fh template($HEADERHTML, {title => $title, is_root => $is_root});
print $fh $INDEXINTROHTML if $is_root;
print $fh "<i style=\"font-size:14px\">Tagged: " . join(' | ', (map { tag_link_html($_, $cur_tag, $is_root) } @all_tags)) . "</i>";
for my $story (@stories) {
next if $story->{hide};
print $fh template($STORYDESCRHTML, {%$story, is_root => $is_root});
}
print $fh template($FOOTERHTML, {is_root => $is_root});
close $fh;
}
sub write_rss {
my (@stories) = @_;
open( my $fh, '>', $RSSFILE )
or die "can't write $RSSFILE: $!";
print $fh template($RSSHEADER, {});
for my $story (@stories) {
next if $story->{hide};
print $fh template($RSSITEM, {%$story, is_rss => 1});
}
print $fh template($RSSFOOTER, {});
close $fh;
}
sub write_tags {
my (@stories) = @_;
my %tag;
for my $story (@stories) {
next if !$story->{tags};
push @{ $tag{$_} }, $story for split /,/, $story->{tags};
}
for my $t (keys %tag) {
write_index($tag{$t}, "Posts tagged '$t'", "$TAGDIR/$t.html", 0, $t);
}
}
sub write_sitemap {
my (@stories) = @_;
open( my $fh, '>', $SITEMAPFILE )
or die "can't write $SITEMAPFILE :$!";
print $fh template($SITEMAPHEADER, {});
for my $story (@stories) {
next if $story->{hide};
print $fh template($SITEMAPITEM, $story);
}
print $fh template($SITEMAPFOOTER, {});
close $fh;
}
sub read_file {
my ($file) = @_;
open( my $fh, '<', $file )
or die "can't read $file: $!";
my $text = '';
$text .= $_ while (<$fh>);
close $fh;
return $text;
}
sub template {
my ($html, $story) = @_;
$story->{title} ||= '';
$html =~ s/__TITLE__/$story->{title}/g;
$story->{tagshtml} ||= '';
$html =~ s/__TAGSHTML__/$story->{tagshtml}/g;
$story->{textdate} ||= '';
$html =~ s/__DATE__/$story->{textdate}/g;
$story->{textdate_rfc822} ||= '';
$html =~ s/__DATE_RFC822__/$story->{textdate_rfc822}/g;
$story->{textdate_sitemap} ||= '';
$html =~ s/__DATE_SITEMAP__/$story->{textdate_sitemap}/g;
$story->{relpath} ||= '';
$html =~ s/__RELPATH__/$story->{relpath}/g;
$story->{abspath} ||= '';
$html =~ s/__ABSPATH__/$story->{abspath}/g;
$story->{descr} ||= '';
$html =~ s/__DESCR__/$story->{descr}/g;
$story->{rss_descr} ||= '';
$html =~ s/__RSS_DESCR__/$story->{rss_descr}/g;
$html =~ s/__RSS_CONTENT__/$story->{html}/g;
$html =~ s/__LATEST_DATE_RFC822__/$LATEST_DATE/g;
$html =~ s/__LATEST_SITEMAP__/$LATEST_SITEMAP/g;
$html =~ s/href="\.\.\/?/href="/g if $story->{is_root};
$html =~ s/href="\.\.\/?/href="$HOST_PATH\//g if $story->{is_rss};
return $html;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment