Created
December 6, 2019 22:54
-
-
Save jes/88e3e587413d5794f69c281bdd14a27f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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