a number of changes too great to list here, but basically it's a ton of (currently incomplete) refactoring
This commit is contained in:
parent
99ffa3676c
commit
a589ec7ed9
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
|
||||
use App::BlogAlba;
|
||||
App::BlogAlba->to_app;
|
21
config.yml
21
config.yml
|
@ -1,22 +1,27 @@
|
|||
appname: "BlogAlba"
|
||||
appname: "App::BlogAlba"
|
||||
layout: "main"
|
||||
charset: "UTF-8"
|
||||
tz: "Europe/London"
|
||||
locale: "en_GB"
|
||||
|
||||
logger: "console"
|
||||
log: "debug"
|
||||
template: "template_toolkit"
|
||||
engines:
|
||||
template:
|
||||
template_toolkit:
|
||||
encoding: 'utf8'
|
||||
start_tag: '[%'
|
||||
end_tag: '%]'
|
||||
|
||||
# Uncomment for debugging
|
||||
#show_errors: 1
|
||||
|
||||
#Blog configuration
|
||||
url: https://maff.scot/
|
||||
#Site configuration
|
||||
|
||||
#url: https://maff.scot/
|
||||
name: colourful words and phrases
|
||||
tagline: techy tangents and general life chatter from a tired sysadmin.
|
||||
author: Maff
|
||||
about: A dorky dude who spends too much time with perl.
|
||||
keywords: "sysadmin,unix,bsd,freebsd,linux,software,web,developer,perl,bash,shell,gentoo,maff,matthew,connelly,dundee,scotland,furry,blog"
|
||||
posturlprepend: wrote/
|
||||
blog_prepend: wrote
|
||||
conf:
|
||||
indexable: 1
|
||||
per_page: 6
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
logger: "console"
|
||||
log: "core"
|
||||
warnings: 1
|
||||
show_errors: 1
|
||||
startup_info: 1
|
|
@ -0,0 +1,5 @@
|
|||
log: "warning"
|
||||
logger: "file"
|
||||
warnings: 0
|
||||
show_errors: 0
|
||||
no_server_tokens: 1
|
|
@ -1,13 +0,0 @@
|
|||
<TMPL_IF NAME="SOURCEVIEW">
|
||||
<TMPL_VAR NAME="mdsource">
|
||||
<TMPL_ELSE>
|
||||
<TMPL_INCLUDE NAME="head.inc">
|
||||
<TMPL_IF NAME="INDEX">
|
||||
<TMPL_LOOP NAME="POSTS">
|
||||
<TMPL_INCLUDE NAME="post.inc">
|
||||
</TMPL_LOOP>
|
||||
<TMPL_ELSE>
|
||||
<TMPL_INCLUDE NAME="post.inc">
|
||||
</TMPL_IF>
|
||||
<TMPL_INCLUDE NAME="foot.inc">
|
||||
</TMPL_IF>
|
|
@ -3,86 +3,20 @@ package App::BlogAlba;
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Cwd;
|
||||
# TODO: maybe swap this out for templating stuff through dancer, would be cleaner.
|
||||
use HTML::Template;
|
||||
use Text::Markdown::Hoedown;
|
||||
use App::BlogAlba::Article;
|
||||
use Data::Paginated;
|
||||
|
||||
use POSIX qw/strftime/;
|
||||
use Date::Parse qw/str2time/; #Required for converting the date field in posts to something strftime can work with
|
||||
use Time::HiRes qw/gettimeofday tv_interval/;
|
||||
use XML::RSS;
|
||||
use Unicode::Normalize;
|
||||
use YAML; #Required for loading post-specific information from posts
|
||||
|
||||
use Sys::Hostname;
|
||||
my $HOST = hostname; $HOST =~ s/\..*$//;
|
||||
|
||||
use Dancer2;
|
||||
use Dancer2::Plugin::Feed;
|
||||
|
||||
my $HOST = `hostname -s`; chomp $HOST;
|
||||
#Sanitise our base URL
|
||||
config->{url} =~ /\/$/ or config->{url} .= '/';
|
||||
|
||||
my $basedir=$ENV{BASE}."/".$ENV{APP} || cwd();
|
||||
config->{url} .= '/' unless config->{url} =~ /\/$/;
|
||||
|
||||
my ($page,@posts,@pages,%defparams);
|
||||
my $nposts=0;my $npages=1;my $lastcache=0;
|
||||
|
||||
sub readpost {
|
||||
my $file = shift;my $psh = shift || 1;
|
||||
my %post;
|
||||
my $postb = ""; my $postmm = "";
|
||||
open POST, $file or warn "Couldn't open $file!" and return 0;
|
||||
my $status = 0;
|
||||
while (<POST>) {
|
||||
$postb .= $_ if $status==2;
|
||||
/^-{3,}$/ and not $status==2 and $status = $status==1? 2 : 1;
|
||||
$postmm .= $_ if $status==1;
|
||||
}
|
||||
close POST; undef $status;
|
||||
%post = %{YAML::Load($postmm)}; undef $postmm;
|
||||
$post{filename} = $1 if $file =~ /(?:^|\/)([a-zA-Z0-9\-]*)\.md$/;
|
||||
$post{body} = markdown(
|
||||
$postb,
|
||||
extensions => HOEDOWN_EXT_TABLES
|
||||
| HOEDOWN_EXT_FENCED_CODE
|
||||
| HOEDOWN_EXT_FOOTNOTES
|
||||
| HOEDOWN_EXT_AUTOLINK
|
||||
| HOEDOWN_EXT_STRIKETHROUGH
|
||||
| HOEDOWN_EXT_UNDERLINE
|
||||
| HOEDOWN_EXT_HIGHLIGHT
|
||||
| HOEDOWN_EXT_SUPERSCRIPT
|
||||
| HOEDOWN_EXT_NO_INTRA_EMPHASIS);
|
||||
$post{mdsource} = $postb;
|
||||
undef $postb;
|
||||
if (defined $post{date}) {
|
||||
$post{slug} = slugify($post{title}) unless $post{slug}; #we allow custom slugs to be defined
|
||||
$post{hastags} = 1 unless not defined $post{tags};
|
||||
$post{excerpt} = $1 if $post{body} =~ /(<p>.*?<\/p>)/s;
|
||||
$post{time} = str2time($post{date});
|
||||
$post{fancy} = timefmt($post{time},'fancydate');
|
||||
$post{datetime} = timefmt($post{date},'datetime');
|
||||
$post{permaurl} = config->{url}.config->{posturlprepend}.timefmt($post{time},'permalink').$post{slug};
|
||||
}
|
||||
push @posts,{%post} if $psh==1; push @pages,{%post} if $psh==2;return %post;
|
||||
}
|
||||
sub slugify {
|
||||
my $t = shift;
|
||||
$t = lc NFKD($t); #Unicode::Normalize
|
||||
$t =~ tr/\000-\177//cd; #Strip non-ascii
|
||||
$t =~ s/[^\w\s-]//g; #Strip non-words
|
||||
chomp $t;
|
||||
$t =~ s/[-\s]+/-/g; #Prevent multiple hyphens or any spaces
|
||||
return $t;
|
||||
}
|
||||
sub timefmt {
|
||||
my ($epoch,$context)=@_;
|
||||
$epoch=str2time $epoch if $epoch !~ /^[0-9]{10}$/;
|
||||
my $dsuffix = 'th'; $dsuffix = 'st' if strftime("%d",localtime $epoch) =~ /1$/; $dsuffix = 'nd' if strftime("%d",localtime $epoch) =~ /2$/;
|
||||
return strftime "%A, %e$dsuffix %b. %Y", localtime $epoch if $context eq 'fancydate';
|
||||
return strftime "%Y-%m-%dT%H:%M%z",localtime $epoch if $context eq 'datetime';
|
||||
return strftime "%Y-%m",localtime $epoch if $context eq 'writepost';
|
||||
return strftime "%Y/%m/",localtime $epoch if $context eq 'permalink';
|
||||
return strftime $context, localtime $epoch if $context;
|
||||
return strftime config->{conf}->{date_format},localtime $epoch;
|
||||
}
|
||||
sub pagination_calc {
|
||||
my $rem=$nposts % config->{conf}->{per_page};
|
||||
$npages=($nposts-$rem)/config->{conf}->{per_page};
|
||||
|
@ -99,10 +33,6 @@ sub paginate {
|
|||
$page->param(PAGINATED => 1, prevlink => ($pagenum>1? 1 : 0), prevpage => $pagenum-1, nextlink => ($pagenum<$npages? 1 : 0), nextpage => $pagenum+1);
|
||||
return get_index @posts[$offset..(($offset+config->{conf}->{per_page})>$#posts? $#posts : ($offset+(config->{conf}->{per_page}-1)))];
|
||||
}
|
||||
sub page_init {
|
||||
$page = HTML::Template->new(filename => "$basedir/layout/base.html",die_on_bad_params => 0,utf8 => 1,global_vars => 1);
|
||||
$page->param(%defparams);
|
||||
}
|
||||
sub get_post {
|
||||
my ($y,$m,$slug) = @_;
|
||||
for my $r (@posts) {
|
||||
|
@ -123,31 +53,6 @@ sub get_page {
|
|||
}
|
||||
return undef;
|
||||
}
|
||||
sub generate_feed {
|
||||
return unless config->{conf}->{rss_publish};
|
||||
my $feed = new XML::RSS(version => '2.0');
|
||||
$feed->channel (
|
||||
title => config->{name},
|
||||
link => config->{url},
|
||||
description => config->{tagline},
|
||||
dc => {
|
||||
creator => config->{author},
|
||||
language => config->{locale},
|
||||
},
|
||||
syn => {
|
||||
updatePeriod => "daily",
|
||||
updateFrequency => "1",
|
||||
updateBase => "1970-01-01T00:00+00:00",
|
||||
},
|
||||
);
|
||||
$feed->add_item (
|
||||
title => $_->{title},
|
||||
link => $_->{permaurl},
|
||||
description => (config->{conf}->{rss_excerpt}? $_->{excerpt} : $_->{body}),
|
||||
dc => { creator => config->{author}, },
|
||||
) for @posts[0 .. ($#posts > (config->{conf}->{recent_posts}-1)? (config->{conf}->{recent_posts}-1) : $#posts)];
|
||||
$feed->save("$basedir/public/feed-rss2.xml");
|
||||
}
|
||||
sub do_cache {
|
||||
return if $lastcache > (time - 3600);
|
||||
$lastcache = time;my $st=[gettimeofday];
|
||||
|
@ -181,37 +86,69 @@ sub do_cache {
|
|||
pagination_calc;
|
||||
}
|
||||
|
||||
sub GetPost {
|
||||
my $params = shift or return undef;
|
||||
return undef unless
|
||||
$params->{year} =~ /^[0-9]{4}$/ and
|
||||
$params->{month} =~ /^(0[1-9]|1[0-2])$/ and
|
||||
$params->{slug};
|
||||
|
||||
for my $article (@articles) {
|
||||
next unless
|
||||
$article->{slug} eq lc $params->{slug} and
|
||||
$article->{yyyymm} eq $params->{year}.$params->{month};
|
||||
return $article;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
hook 'before' => sub {
|
||||
do_cache;
|
||||
page_init;
|
||||
};
|
||||
|
||||
#Indexes
|
||||
get '/' => sub {
|
||||
return get_index @posts if $npages==1;
|
||||
return paginate 1;
|
||||
};
|
||||
get '/page/:id' => sub {
|
||||
get '/page/:num' => sub {
|
||||
pass unless params->{id} =~ /^[0-9]+$/ and params->{id} <= $npages;
|
||||
return redirect '/' unless $npages > 1 and params->{id} > 1;
|
||||
return paginate params->{id};
|
||||
};
|
||||
get '/wrote/:yyyy/:mm/:slug' => sub {
|
||||
pass unless params->{yyyy} =~ /^[0-9]{4}$/ and params->{mm} =~ /^(?:0[1-9]|1[0-2])$/ and params->{slug} =~ /^[a-z0-9\-]+(?:\.md)?$/i;
|
||||
if (params->{slug} =~ s/\.md$//) { $page->param(SOURCEVIEW => 1); header('Content-Type' => 'text/plain'); }
|
||||
$page->param(ISPOST => 1);
|
||||
get_post params->{yyyy}, params->{mm}, params->{slug} or pass;
|
||||
return $page->output;
|
||||
};
|
||||
get '/:extpage' => sub {
|
||||
|
||||
#Published articles
|
||||
get '/:page' => sub {
|
||||
pass unless params->{extpage} =~ /^[a-z0-9\-]+(?:\.md)?$/i;
|
||||
if (params->{extpage} =~ s/\.md$//) { $page->param(SOURCEVIEW => 1); header('Content-Type' => 'text/plain'); }
|
||||
$page->param(ISPOST => 0);
|
||||
get_page params->{extpage} or pass;
|
||||
return $page->output;
|
||||
};
|
||||
get '/wrote/:yyyy/:mm/:slug' => sub {
|
||||
my $article = GetPost scalar params 'route';
|
||||
pass unless $article;
|
||||
content_tyle 'text/plain' and return $article->{raw}
|
||||
if params->{slug} =~ /\.md$/;
|
||||
|
||||
return template 'post', { page => $article };
|
||||
};
|
||||
|
||||
#Feeds
|
||||
get '/feed/:type' => sub {
|
||||
pass unless params->{type} =~ /^(rss|atom)$/i;
|
||||
create_feed
|
||||
format => lc params->{type},
|
||||
title => config->{blogtitle},
|
||||
link => request->base,
|
||||
entries => \@articles;
|
||||
}
|
||||
get qr{/feed-(atom|rss2).xml} => sub { redirect '/feed/'.splat; }
|
||||
|
||||
# 404
|
||||
any qr/.*/ => sub {
|
||||
return redirect '/' if request->path =~ /index(?:\.(?:html?|pl)?)?$/;
|
||||
redirect '/' if request->path =~ /index\.(html?|pl)$/;
|
||||
return send_error('The page you seek cannot be found.', 404);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||
<meta name="keywords" content="<TMPL_VAR NAME="keywords">" />
|
||||
<meta name="author" content="<TMPL_VAR NAME="author">" />
|
||||
<meta name="description" content="<TMPL_VAR NAME="tagline">" />
|
||||
<meta property="og:title" content="<TMPL_VAR NAME="pagetitle">" />
|
||||
<meta property="og:site_name" content="<TMPL_VAR NAME="name">" />
|
||||
<meta property="og:description" content="<TMPL_VAR NAME="tagline">" />
|
||||
<title><TMPL_VAR NAME="pagetitle"></title>
|
||||
<TMPL_VAR NAME="robots">
|
||||
<meta name="google-site-verification" content="QtCkt_CI6tmKhrNsw0KvEpScFjDRQQPvmwHGdSTYnGE" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<TMPL_IF NAME="rss_enabled">
|
||||
<link rel="alternate" type="application/rss+xml" title="<TMPL_VAR NAME="title"> (RSS 2.0)" href="<TMPL_VAR NAME="url">/feed-rss2.xml" />
|
||||
</TMPL_IF>
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.2/custom/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/main.css" />
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/solarized_dark.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-static">
|
||||
<div class="container">
|
||||
<a class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
|
||||
</a>
|
||||
<div class="nav-collapse collase">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="<TMPL_VAR NAME="url">">Home</a></li>
|
||||
<TMPL_LOOP NAME="NAV">
|
||||
<li><a href="<TMPL_VAR NAME="navurl">"><TMPL_VAR NAME="navname"></a></li>
|
||||
</TMPL_LOOP>
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="masthead">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col col-sm-6">
|
||||
<h1><a href="<TMPL_VAR NAME="url">" title="<TMPL_VAR NAME="name">"><TMPL_VAR NAME="name"></a>
|
||||
<p class="lead"><TMPL_VAR NAME="tagline"></p></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col col-sm-3">
|
||||
<div id="sidebar">
|
||||
<ul class="nav nav-stacked">
|
||||
<TMPL_IF NAME="ISPOST">
|
||||
<li><h3 class="highlight">This Post</h3></li>
|
||||
<li>Published: <span class="highlight"><TMPL_VAR NAME="fancy"></span></li>
|
||||
<TMPL_IF NAME="hastags">
|
||||
<li>Tags: <span class="highlight"><TMPL_VAR NAME="tags"></span></li>
|
||||
</TMPL_IF>
|
||||
</TMPL_IF>
|
||||
<li><h3 class="highlight">Posts</h3></li>
|
||||
<TMPL_LOOP NAME="recent">
|
||||
<li><a href="<TMPL_VAR NAME="permaurl">"><TMPL_VAR NAME="title"></a></li>
|
||||
</TMPL_LOOP>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME="PAGINATED">
|
||||
<div class='col col-sm-9'><div id='pagination'>
|
||||
<TMPL_IF NAME="prevlink"><p style='float: left'><a href="/page/<TMPL_VAR NAME="prevpage">">Go forth in time (page <TMPL_VAR NAME="prevpage">)</a></p></TMPL_IF>
|
||||
<TMPL_IF NAME="nextlink"><p style='float: right'><a href="/page/<TMPL_VAR NAME="nextpage">">Plunge further into posts of yore (page <TMPL_VAR NAME="nextpage">)</a></p></TMPL_IF>
|
||||
</div></div>
|
||||
</TMPL_IF>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="https://github.com/Maffsie/BlogAlba">BlogAlba</a> + <a href="http://nginx.org">nginx</a> + <a href="http://freebsd.org">freebsd</a> + <a href="https://ip-projects.de">ip-projects servers</a><br />
|
||||
<span id="small">Served by <TMPL_VAR NAME="host">, site generated at <TMPL_VAR NAME="gentime"> (took <TMPL_VAR NAME="genworktime">)</span>
|
||||
</footer>
|
||||
<!-- Code block syntax highlighting. -->
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
<!--Begin google analytics-->
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-30268654-2', 'auto');
|
||||
ga('require', 'displayfeatures');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<!--End google analytics-->
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue