a number of changes too great to list here, but basically it's a ton of (currently incomplete) refactoring

This commit is contained in:
Matthew Connelly 2015-12-28 20:09:51 +00:00
parent 99ffa3676c
commit a589ec7ed9
11 changed files with 180 additions and 134 deletions

9
bin/app.psgi Normal file
View File

@ -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;

View File

@ -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

5
environments/dev.yml Normal file
View File

@ -0,0 +1,5 @@
logger: "console"
log: "core"
warnings: 1
show_errors: 1
startup_info: 1

5
environments/prod.yml Normal file
View File

@ -0,0 +1,5 @@
log: "warning"
logger: "file"
warnings: 0
show_errors: 0
no_server_tokens: 1

View File

@ -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>

View File

@ -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);
};

View File

98
views/layouts/main.tt Normal file
View File

@ -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>