├── .github ├── FUNDING.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .perltidyrc ├── COMMENTS.md ├── ChangeLog ├── LICENSE ├── MANIFEST ├── MANIFEST.SKIP ├── Makefile.PL ├── README.md ├── THEMES.md ├── bin └── chronicle ├── cgi-bin └── comments.cgi ├── debian ├── changelog ├── compat ├── control ├── copyright ├── libchronicle-perl.docs ├── rules └── source │ └── format ├── etc └── config.sample ├── lib ├── App │ └── Chronicle.pm └── Chronicle │ ├── Config │ └── Reader.pm │ ├── Plugin │ ├── Archived.pm │ ├── DBTweak.pm │ ├── Filter.pm │ ├── Generate │ │ ├── Archive.pm │ │ ├── Index.pm │ │ ├── LowerCase.pm │ │ ├── Pages.pm │ │ ├── RSS.pm │ │ ├── Sitemap.pm │ │ └── Tags.pm │ ├── InPlacePosts.pm │ ├── Markdown.pm │ ├── MultiMarkdown.pm │ ├── PostBuild.pm │ ├── PostSpooler.pm │ ├── PreBuild.pm │ ├── SkipDrafts.pm │ ├── Snippets │ │ ├── AllTags.pm │ │ ├── Archives.pm │ │ ├── Meta.pm │ │ ├── RecentPosts.pm │ │ └── RecentTags.pm │ ├── StaticPages.pm │ ├── Textile.pm │ ├── Tidy.pm │ ├── TruncatedBody.pm │ └── YouTube.pm │ ├── Template.pm │ ├── Template │ ├── GenericXslate.pm │ ├── HTMLTemplate.pm │ ├── Xslate.pm │ └── XslateTT.pm │ ├── URI.pm │ └── Utils.pm ├── t ├── load-modules.t ├── manifest.t ├── pod-coverage.t ├── pod.t ├── style-no-tabs.t ├── test-archive-links.t ├── test-config-reader.t ├── test-html-tidy.t ├── test-in-place-post.t ├── test-markdown-formatter.t ├── test-multimarkdown-formatter.t ├── test-publish-future.t ├── test-skip-drafts.t ├── test-strict.t ├── test-textile-formatter.t ├── test-truncatedbody.t ├── test-version-insertion.t └── test-youtube.t └── themes ├── blog.steve.org.uk ├── archive.tmpl ├── archive_index.tmpl ├── entry.tmpl ├── inc │ ├── add-comment.inc │ ├── blog-post-truncated.inc │ ├── blog-post.inc │ ├── comment-loop.inc │ ├── footer.inc │ ├── sidebar.inc │ └── style.inc ├── index.tmpl ├── page.tmpl ├── static │ ├── progress.gif │ ├── spiral.gif │ └── xml.gif ├── tag.tmpl └── tag_index.tmpl ├── bs2 ├── archive.tmpl ├── archive_index.tmpl ├── entry.tmpl ├── inc │ ├── add-comment.inc │ ├── blog-post-truncated.inc │ ├── blog-post.inc │ ├── comment-loop.inc │ ├── footer.inc │ └── sidebar.inc ├── index.tmpl ├── page.tmpl ├── static │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap.css │ │ └── mezzanine.css │ ├── favicon.ico │ ├── js │ │ ├── bootstrap-extras.js │ │ ├── bootstrap.js │ │ ├── html5shiv.js │ │ └── jquery-1.7.1.min.js │ └── progress.gif ├── tag.tmpl └── tag_index.tmpl ├── default ├── archive.tmpl ├── archive_index.tmpl ├── comment-form.inc ├── comment-loop.inc ├── entry.tmpl ├── footer.inc ├── header.inc ├── index.tmpl ├── page.tmpl ├── sidebar.inc ├── static │ ├── style.css │ └── xml.gif ├── tag.tmpl └── tag_index.tmpl └── leftbar ├── archive.tmpl ├── archive_index.tmpl ├── entry.tmpl ├── inc ├── add-comment.inc ├── analytics.inc ├── blog-post-truncated.inc ├── blog-post.inc ├── comment-loop.inc ├── sidebar.inc └── style.inc ├── index.tmpl ├── page.tmpl ├── static ├── spiral.gif └── xml.gif ├── tag.tmpl └── tag_index.tmpl /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: skx 3 | custom: https://steve.fi/donate/ 4 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | = Quick Checklist = 2 | 3 | I welcome the submission of additional features, and improvements to 4 | the codebase. This template-file contains just a few last minute checks 5 | to complete before you submit your pull-request: 6 | 7 | [ ] Did you run the test-cases? 8 | - If you did, great! 9 | 10 | [ ] Did you add any new modules? 11 | - If so please remember to make sure your name is listed as the AUTHOR. 12 | - Don't forget to write the documentation, at the top of the module. 13 | 14 | [ ] Did you reformat the code? 15 | - There is a `.perltidyrc` file in the repository, so just run this: 16 | perltidy $(find . -name '*.pm') 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | Makefile.old 3 | MYMETA.json 4 | MYMETA.yml 5 | Makefile 6 | blib/ 7 | pm_to_blib 8 | blog.db 9 | data/ 10 | comments/ 11 | -------------------------------------------------------------------------------- /.perltidyrc: -------------------------------------------------------------------------------- 1 | # Final parameter set for this run. 2 | # See utility 'perltidyrc_dump.pl' for nicer formatting. 3 | --add-newlines 4 | --add-semicolons 5 | --add-whitespace 6 | --backup-and-modify-in-place 7 | --backup-file-extension="bak" 8 | --blanks-before-blocks 9 | --blanks-before-comments 10 | --blanks-before-subs 11 | --block-brace-tightness=2 12 | --block-brace-vertical-tightness=0 13 | --nobrace-left-and-indent 14 | --brace-tightness=0 15 | --brace-vertical-tightness=1 16 | --brace-vertical-tightness-closing=0 17 | --break-after-all-operators 18 | --break-at-old-keyword-breakpoints 19 | --break-at-old-logical-breakpoints 20 | --break-at-old-ternary-breakpoints 21 | --check-syntax 22 | --closing-brace-indentation=1 23 | --closing-paren-indentation=1 24 | --closing-side-comment-else-flag=0 25 | --closing-side-comment-interval=6 26 | --closing-side-comment-maximum-text=20 27 | --closing-side-comments-balanced 28 | --closing-square-bracket-indentation=1 29 | --comma-arrow-breakpoints=1 30 | --continuation-indentation=2 31 | --nocuddled-else 32 | --delete-old-newlines 33 | --nodelete-old-whitespace 34 | --delete-semicolons 35 | --format="tidy" 36 | --format-skipping 37 | --fuzzy-line-length 38 | --hanging-side-comments 39 | --nohtml 40 | --html-entities 41 | --html-table-of-contents 42 | --indent-block-comments 43 | --indent-columns=4 44 | --keep-old-blank-lines=1 45 | --line-up-parentheses 46 | --nologfile 47 | --long-block-line-count=8 48 | --look-for-autoloader 49 | --look-for-selfloader 50 | --maximum-consecutive-blank-lines=4 51 | --maximum-fields-per-table=0 52 | --maximum-line-length=80 53 | --minimum-space-to-comment=4 54 | --npro 55 | --opening-brace-on-new-line 56 | --opening-sub-brace-on-new-line 57 | --outdent-labels 58 | --nooutdent-long-comments 59 | --nooutdent-long-quotes 60 | --paren-tightness=1 61 | --paren-vertical-tightness=1 62 | --paren-vertical-tightness-closing=0 63 | --pass-version-line 64 | --perl-syntax-check-flags="-c -T" 65 | --pod2html 66 | --preserve-line-endings 67 | --noquiet 68 | --recombine 69 | --short-concatenation-item-length=8 70 | --noshow-options 71 | --space-for-semicolon 72 | --square-bracket-tightness=2 73 | --square-bracket-vertical-tightness=1 74 | --square-bracket-vertical-tightness-closing=0 75 | --stack-closing-hash-brace 76 | --stack-closing-paren 77 | --stack-closing-square-bracket 78 | --stack-opening-hash-brace 79 | --stack-opening-paren 80 | --stack-opening-square-bracket 81 | --static-block-comments 82 | --nostatic-side-comments 83 | --notabs 84 | --trim-qw 85 | --valign 86 | --nowarning-output 87 | -------------------------------------------------------------------------------- /COMMENTS.md: -------------------------------------------------------------------------------- 1 | 2 | Chronicle supports the submission of comments upon published posts, via an optional CGI script. 3 | 4 | This document describes how you would go about enabling this support. 5 | 6 | 7 | Introduction 8 | ------------ 9 | 10 | The basic use of chronicle is to convert a collection of text files into a HTML & RSS blog. 11 | 12 | There are two ways this software is typically used: 13 | 14 | * On a single host. 15 | * The blog input is stored upon your web-server and you generate the output directly to a HTTP-accessible directory upon that machine. 16 | * With multiple hosts. 17 | * The blog input lives upon one machine, and once you've generated the output you copy it over to a remote web-server where it may be viewed. 18 | 19 | Depending upon which of these ways you use the software the comment support will need to be handled differently. 20 | 21 | 22 | 23 | Common Setup 24 | ------------ 25 | 26 | Install the included file `cgi-bin/comments.cgi` upon the web-server which hosts the blog, and adjust the settings at the start of that file to specify the basic configuration: 27 | 28 | * The local directory to save the comments within. 29 | 30 | * The source and destination email addresses to use for notification purposes. 31 | 32 | From here the configuration varies depending on how you're going to run the software. 33 | 34 | 35 | Single Machine 36 | -------------- 37 | 38 | If you have only a single machine then you may configure the `comments.cgi` script to save the comments in text files directly within your blog tree. 39 | 40 | Assuming you have something like this: 41 | 42 | * `comments/` 43 | * The directory to contain the comments. 44 | * `data/` 45 | * The directory where your blog posts are loaded from. 46 | 47 | You may then regenerate your blog via: 48 | 49 | chronicle --input=./date/ --comments=./comments/ --output=/var/www/blog/ 50 | 51 | This will ensure that the comments saved by your web-server into the comments directory are included in the (re)generated blog. 52 | 53 | 54 | 55 | Multiple Machines 56 | ----------------- 57 | 58 | If you have the blog input files upon machine "`local`" and the hosted blog upon the machine "`remote`" then you will run into problems: 59 | 60 | * The comments are saved by your web-server to a local directory upon the machine "`remote`". 61 | * To rebuild the blog upon your local machine, "`local`", you must have those files. 62 | 63 | The solution is to generate your blog in a three-step process: 64 | 65 | 1. Copy the comment files, if any, from "remote" to "local". 66 | 2. Rebuild the blog. 67 | 3. Upload the generated blog. 68 | 69 | I'd recommend using `rsync` for steps 1 & 3. 70 | 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This module is free software; you can redistribute it and/or modify it 2 | under the terms of either: 3 | 4 | a) the GNU General Public License as published by the Free Software 5 | Foundation; either version 2, or (at your option) any later version, 6 | or 7 | 8 | b) the Perl "Artistic License". 9 | 10 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | bin/chronicle 2 | cgi-bin/comments.cgi 3 | ChangeLog 4 | COMMENTS.md 5 | etc/config.sample 6 | lib/App/Chronicle.pm 7 | lib/Chronicle/Config/Reader.pm 8 | lib/Chronicle/Plugin/Archived.pm 9 | lib/Chronicle/Plugin/DBTweak.pm 10 | lib/Chronicle/Plugin/Filter.pm 11 | lib/Chronicle/Plugin/Generate/Archive.pm 12 | lib/Chronicle/Plugin/Generate/Index.pm 13 | lib/Chronicle/Plugin/Generate/LowerCase.pm 14 | lib/Chronicle/Plugin/Generate/Pages.pm 15 | lib/Chronicle/Plugin/Generate/RSS.pm 16 | lib/Chronicle/Plugin/Generate/Sitemap.pm 17 | lib/Chronicle/Plugin/Generate/Tags.pm 18 | lib/Chronicle/Plugin/InPlacePosts.pm 19 | lib/Chronicle/Plugin/Markdown.pm 20 | lib/Chronicle/Plugin/MultiMarkdown.pm 21 | lib/Chronicle/Plugin/PostBuild.pm 22 | lib/Chronicle/Plugin/PostSpooler.pm 23 | lib/Chronicle/Plugin/PreBuild.pm 24 | lib/Chronicle/Plugin/SkipDrafts.pm 25 | lib/Chronicle/Plugin/StaticPages.pm 26 | lib/Chronicle/Plugin/Snippets/AllTags.pm 27 | lib/Chronicle/Plugin/Snippets/Archives.pm 28 | lib/Chronicle/Plugin/Snippets/Meta.pm 29 | lib/Chronicle/Plugin/Snippets/RecentPosts.pm 30 | lib/Chronicle/Plugin/Snippets/RecentTags.pm 31 | lib/Chronicle/Plugin/Textile.pm 32 | lib/Chronicle/Plugin/Tidy.pm 33 | lib/Chronicle/Plugin/TruncatedBody.pm 34 | lib/Chronicle/Plugin/YouTube.pm 35 | lib/Chronicle/Template.pm 36 | lib/Chronicle/Template/GenericXslate.pm 37 | lib/Chronicle/Template/HTMLTemplate.pm 38 | lib/Chronicle/Template/Xslate.pm 39 | lib/Chronicle/Template/XslateTT.pm 40 | lib/Chronicle/URI.pm 41 | lib/Chronicle/Utils.pm 42 | LICENSE 43 | Makefile.PL 44 | MANIFEST 45 | MANIFEST.SKIP 46 | README.md 47 | t/load-modules.t 48 | t/manifest.t 49 | t/pod-coverage.t 50 | t/pod.t 51 | t/style-no-tabs.t 52 | t/test-archive-links.t 53 | t/test-config-reader.t 54 | t/test-in-place-post.t 55 | t/test-html-tidy.t 56 | t/test-markdown-formatter.t 57 | t/test-multimarkdown-formatter.t 58 | t/test-publish-future.t 59 | t/test-skip-drafts.t 60 | t/test-strict.t 61 | t/test-textile-formatter.t 62 | t/test-truncatedbody.t 63 | t/test-version-insertion.t 64 | t/test-youtube.t 65 | THEMES.md 66 | themes/blog.steve.org.uk/archive.tmpl 67 | themes/blog.steve.org.uk/archive_index.tmpl 68 | themes/blog.steve.org.uk/entry.tmpl 69 | themes/blog.steve.org.uk/inc/add-comment.inc 70 | themes/blog.steve.org.uk/inc/blog-post.inc 71 | themes/blog.steve.org.uk/inc/blog-post-truncated.inc 72 | themes/blog.steve.org.uk/inc/comment-loop.inc 73 | themes/blog.steve.org.uk/inc/footer.inc 74 | themes/blog.steve.org.uk/inc/sidebar.inc 75 | themes/blog.steve.org.uk/inc/style.inc 76 | themes/blog.steve.org.uk/index.tmpl 77 | themes/blog.steve.org.uk/page.tmpl 78 | themes/blog.steve.org.uk/static/progress.gif 79 | themes/blog.steve.org.uk/static/spiral.gif 80 | themes/blog.steve.org.uk/static/xml.gif 81 | themes/blog.steve.org.uk/tag.tmpl 82 | themes/blog.steve.org.uk/tag_index.tmpl 83 | themes/bs2/archive.tmpl 84 | themes/bs2/archive_index.tmpl 85 | themes/bs2/entry.tmpl 86 | themes/bs2/inc/add-comment.inc 87 | themes/bs2/inc/blog-post.inc 88 | themes/bs2/inc/blog-post-truncated.inc 89 | themes/bs2/inc/comment-loop.inc 90 | themes/bs2/inc/footer.inc 91 | themes/bs2/inc/sidebar.inc 92 | themes/bs2/index.tmpl 93 | themes/bs2/page.tmpl 94 | themes/bs2/static/css/bootstrap-responsive.css 95 | themes/bs2/static/css/bootstrap.css 96 | themes/bs2/static/css/mezzanine.css 97 | themes/bs2/static/favicon.ico 98 | themes/bs2/static/js/bootstrap-extras.js 99 | themes/bs2/static/js/bootstrap.js 100 | themes/bs2/static/js/html5shiv.js 101 | themes/bs2/static/js/jquery-1.7.1.min.js 102 | themes/bs2/static/progress.gif 103 | themes/bs2/tag.tmpl 104 | themes/bs2/tag_index.tmpl 105 | themes/default/archive.tmpl 106 | themes/default/archive_index.tmpl 107 | themes/default/comment-form.inc 108 | themes/default/comment-loop.inc 109 | themes/default/entry.tmpl 110 | themes/default/footer.inc 111 | themes/default/header.inc 112 | themes/default/index.tmpl 113 | themes/default/page.tmpl 114 | themes/default/sidebar.inc 115 | themes/default/static/style.css 116 | themes/default/static/xml.gif 117 | themes/default/tag.tmpl 118 | themes/default/tag_index.tmpl 119 | themes/leftbar/archive.tmpl 120 | themes/leftbar/archive_index.tmpl 121 | themes/leftbar/entry.tmpl 122 | themes/leftbar/inc/add-comment.inc 123 | themes/leftbar/inc/analytics.inc 124 | themes/leftbar/inc/blog-post.inc 125 | themes/leftbar/inc/blog-post-truncated.inc 126 | themes/leftbar/inc/comment-loop.inc 127 | themes/leftbar/inc/sidebar.inc 128 | themes/leftbar/inc/style.inc 129 | themes/leftbar/index.tmpl 130 | themes/leftbar/page.tmpl 131 | themes/leftbar/static/spiral.gif 132 | themes/leftbar/static/xml.gif 133 | themes/leftbar/tag.tmpl 134 | themes/leftbar/tag_index.tmpl 135 | -------------------------------------------------------------------------------- /MANIFEST.SKIP: -------------------------------------------------------------------------------- 1 | ^.git/ 2 | ^.gitignore 3 | output/ 4 | data/ 5 | Makefile$ 6 | Makefile.old 7 | debian/ 8 | .*.bak 9 | MYMETA.json 10 | MYMETA.yml 11 | pm_to_blib 12 | blib/ 13 | comments/ 14 | blog.db 15 | .perltidyrc 16 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | 2 | use strict; 3 | use warnings; 4 | 5 | use 5.008; 6 | 7 | use ExtUtils::MakeMaker 6.30; 8 | 9 | use File::ShareDir::Install; 10 | 11 | # 12 | # Install our themes 13 | # 14 | install_share dist => 'themes'; 15 | 16 | 17 | 18 | my %WriteMakefileArgs = ( 19 | NAME => 'App::Chronicle', 20 | EXE_FILES => ['bin/chronicle'], 21 | VERSION_FROM => 'bin/chronicle', 22 | PREREQ_PM => { 23 | 24 | 'DBI' => 0, 25 | 'Date::Format' => 0, 26 | 'Date::Language' => 0, 27 | 'Date::Parse' => 0, 28 | 'Digest::MD5' => 0, 29 | 'Encode' => 0, 30 | 'File::Basename' => 0, 31 | 'File::Find' => 0, 32 | 'File::Path' => 0, 33 | 'File::ShareDir' => 0, 34 | 'Getopt::Long' => 0, 35 | 'HTML::Template' => 0, 36 | 'HTML::Element' => 5, 37 | 'Module::Pluggable::Ordered' => 0, 38 | 'Path::Class' => 0, 39 | 'Pod::Usage' => 0, 40 | 41 | # optional formatters. 42 | 'Text::Markdown' => 0, 43 | 'Text::MultiMarkdown' => 0, 44 | 'Text::Textile' => 0, 45 | 'Unicode::Normalize' => 0, 46 | 'URI' => 0, 47 | }, 48 | 49 | TEST_REQUIRES => { 50 | 51 | # solely for the test-suite. 52 | 'HTML::Tree' => 0, 53 | 'Test::Exception' => 0, 54 | 'Test::More' => 0, 55 | 'Test::NoTabs' => 0, 56 | 'Test::Pod' => 0, 57 | 'Test::Pod::Coverage' => 0, 58 | 'Test::Strict' => 0, 59 | 60 | }, 61 | 62 | 63 | BUILD_REQUIRES => { 'File::ShareDir::Install' => 0, 64 | 'Test::More' => 0, 65 | 'Test::Pod' => 0, 66 | 'Test::Strict' => 0, 67 | 'Test::NoTabs' => 0, 68 | }, 69 | 70 | ABSTRACT => 'A static blog-compiler with minimal dependencies.', 71 | AUTHOR => 'Steve Kemp ', 72 | LICENSE => "perl", 73 | 74 | MIN_PERL_VERSION => '5.008', 75 | 76 | META_MERGE => { 77 | resources => { 78 | license => 'http://dev.perl.org/licenses/', 79 | homepage => 'https://github.com/skx/chronicle2/', 80 | bugtracker => 'https://github.com/skx/chronicle2/issues', 81 | repository => 'https://github.com/skx/chronicle2.git', 82 | }, 83 | }, 84 | ); 85 | 86 | 87 | 88 | unless ( eval {ExtUtils::MakeMaker->VERSION(6.56)} ) 89 | { 90 | my $br = delete $WriteMakefileArgs{ BUILD_REQUIRES }; 91 | my $pp = $WriteMakefileArgs{ PREREQ_PM }; 92 | for my $mod ( keys %$br ) 93 | { 94 | if ( exists $pp->{ $mod } ) 95 | { 96 | $pp->{ $mod } = $br->{ $mod } if $br->{ $mod } > $pp->{ $mod }; 97 | } 98 | else 99 | { 100 | $pp->{ $mod } = $br->{ $mod }; 101 | } 102 | } 103 | } 104 | 105 | delete $WriteMakefileArgs{ CONFIGURE_REQUIRES } 106 | unless eval {ExtUtils::MakeMaker->VERSION(6.52)}; 107 | 108 | WriteMakefile(%WriteMakefileArgs); 109 | 110 | 111 | 112 | package MY; 113 | use File::ShareDir::Install 'postamble'; 114 | -------------------------------------------------------------------------------- /THEMES.md: -------------------------------------------------------------------------------- 1 | Themes 2 | ------ 3 | 4 | Themes in Chronicle are simple collections of files which are populated 5 | and rendered via the 6 | [HTML::Template](http://search.cpan.org/perldoc?HTML%3A%3ATemplate) or 7 | [Text::Xslate](https://metacpan.org/pod/Text::Xslate) module. 8 | 9 | To create a new theme the simplest approach is to take an existing theme and modify it. Once you have a local theme you can cause it to be used like so: 10 | 11 | chronicle --theme-dir=./themes --theme=local 12 | 13 | This will ensure that your theme-templates are read from `./themes/local/`. 14 | 15 | If you would like to use a theme based on `Text::Xslate`, you have to specify 16 | `Xslate` or `XslateTT` as an argument to `--theme-engine` for the Kolon and 17 | TTerse syntax respectively. 18 | 19 | 20 | Theme Files 21 | ----------- 22 | 23 | Each theme should contain the following files: 24 | 25 | * `archive.tmpl` 26 | * This is used to build /archive/$year/$mon 27 | * `archive_index.tmpl` 28 | * Used to build /archive/ - A list of previous pages 29 | * `entry.tmpl` 30 | * This is used to write out the individual blog posts. 31 | * `index.tmpl` 32 | * This is used to build the front-page of your site. 33 | * `index.rss` 34 | * This is used to build the RSS-feed of your site. 35 | * `page.tmpl` 36 | * This is used to generate static-pages. 37 | * `tag.tmpl` 38 | * This is used to build the page /tags/$name/ - The list of posts with the given tag. 39 | * `tag_index.tmpl` 40 | * Used to build the page /tags/ - A list of previous tags 41 | 42 | Beyond that you can move common code to "include files", which can be inserted via: 43 | 44 | 45 | 46 | The supplied themes already make use of this facility to avoid repeating 47 | common look and feel items. 48 | 49 | Non-`HTML::Template` themes use the file extension `.tx` for Xslate/Kolon and 50 | `.ttx` for Xslate/TTerse. 51 | 52 | 53 | 54 | Static Resources 55 | ---------------- 56 | 57 | If your theme directory contains a `static/` subdirectory then the contents of that directory will be copied over to your generated site. 58 | 59 | This is designed to allow you to include your CSS files, images, and other static resources that are used by your theme. 60 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libchronicle-perl (5.1.7-1) unstable; urgency=low 2 | 3 | * New upstream release 5.1.7. 4 | 5 | -- Steve Kemp Mon, 20 Feb 2017 18:18:18 +0000 6 | 7 | libchronicle-perl (5.1.6-1) unstable; urgency=low 8 | 9 | * New upstream release 5.1.6. 10 | 11 | -- Steve Kemp Thu, 29 Dec 2016 06:11:06 +0000 12 | 13 | libchronicle-perl (5.1.5-1) unstable; urgency=low 14 | 15 | * Updated dependencies to add `libclass-path-perl`, as per report 16 | from Oliver Egginger 17 | 18 | -- Steve Kemp Thu, 29 Dec 2016 06:06:06 +0000 19 | 20 | libchronicle-perl (5.1.4-1) unstable; urgency=low 21 | 22 | * New upstream release: 5.1.4 23 | 24 | -- Steve Kemp Wed, 2 Mar 2016 05:33:05 +0000 25 | 26 | libchronicle-perl (5.1.3-1) unstable; urgency=low 27 | 28 | * New upstream release: 5.1.3 29 | 30 | -- Steve Kemp Fri, 12 Feb 2016 10:00:01 +0000 31 | 32 | libchronicle-perl (5.1.2-1) unstable; urgency=low 33 | 34 | * New upstream release: 5.1.1 35 | 36 | -- Steve Kemp Sat, 02 Jan 2016 09:00:09 +0000 37 | 38 | libchronicle-perl (5.1.1-1) unstable; urgency=low 39 | 40 | * New upstream release: 5.1.1 41 | 42 | -- Steve Kemp Sat, 04 Jul 2015 15:33:51 +0000 43 | 44 | libchronicle-perl (5.1.0-1) unstable; urgency=low 45 | 46 | * New upstream release : 5.1.0 47 | 48 | -- Steve Kemp Mon, 16 Jun 2015 09:33:09 +0000 49 | 50 | libchronicle-perl (5.0.8-1) unstable; urgency=low 51 | 52 | * New upstream release. 53 | 54 | -- Steve Kemp Mon, 29 Dec 2014 14:00:41 +0000 55 | 56 | libchronicle-perl (5.0.7-1) unstable; urgency=low 57 | 58 | * New upstream release. 59 | 60 | -- Steve Kemp Sat, 4 Oct 2014 13:14:15 +0000 61 | 62 | libchronicle-perl (5.0.6-1) unstable; urgency=low 63 | 64 | * New upstream release. 65 | 66 | -- Steve Kemp Wed, 24 Sep 2014 20:00:02 +0000 67 | 68 | libchronicle-perl (5.0.5-1) unstable; urgency=low 69 | 70 | * New upstream release. 71 | 72 | -- Steve Kemp Tue, 23 Sep 2014 13:11:31 +0000 73 | 74 | libchronicle-perl (5.0.4-1) unstable; urgency=low 75 | 76 | * New upstream release. 77 | 78 | -- Steve Kemp Tue, 23 Sep 2014 07:44:45 +0000 79 | 80 | libchronicle-perl (5.0.3-1) unstable; urgency=low 81 | 82 | * New upstream release. 83 | 84 | -- Steve Kemp Thu, 21 Sep 2014 14:00:41 +0000 85 | 86 | libchronicle-perl (5.0.2-1) unstable; urgency=low 87 | 88 | * New upstream release. 89 | 90 | -- Steve Kemp Thu, 21 Sep 2014 08:08:08 +0000 91 | 92 | libchronicle-perl (5.0.1-1) unstable; urgency=low 93 | 94 | * Initial Release. 95 | 96 | -- Steve Kemp Thu, 18 Sep 2014 22:37:27 +0100 97 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libchronicle-perl 2 | Section: perl 3 | Priority: optional 4 | Maintainer: Steve Kemp 5 | Build-Depends: debhelper (>= 8) 6 | Build-Depends-Indep: libdbi-perl, 7 | libfile-sharedir-install-perl, 8 | libfile-sharedir-perl, 9 | libhtml-template-perl, 10 | libmodule-pluggable-ordered-perl, 11 | libpath-class-perl, 12 | libtest-notabs-perl, 13 | libtest-pod-coverage-perl, 14 | libtest-pod-perl, 15 | libtest-strict-perl, 16 | libtimedate-perl, 17 | libtext-markdown-perl, 18 | libtext-multimarkdown-perl, 19 | libtext-textile-perl, 20 | libhtml-tree-perl, 21 | perl, 22 | perl-base (>= 5.14.2-21+deb7u1), 23 | perl-modules 24 | Standards-Version: 3.9.6 25 | Homepage: https://github.com/skx/chronicle2 26 | 27 | Package: libchronicle-perl 28 | Architecture: all 29 | Depends: ${misc:Depends}, ${perl:Depends}, 30 | libdbi-perl, 31 | libfile-sharedir-perl, 32 | libhtml-template-perl, 33 | libmodule-pluggable-ordered-perl, 34 | libpath-class-perl, 35 | libtimedate-perl, 36 | perl, 37 | perl-base (>= 5.14.2-21+deb7u1), 38 | perl-modules 39 | Recommends: libtext-markdown-perl, libtext-textile-perl 40 | Description: A static blog-compiler. 41 | Convert a directory of simple text files into a static HTML weblog, 42 | (or blog if you prefer). 43 | . 44 | The system is intentionally simple, but it supports: 45 | . 46 | * RSS feed creation. 47 | * Template based output. 48 | * The tagging of entries. 49 | * Notification of ping services upon blog rebuild. 50 | * User comments upon entries. 51 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format-Specification: http://anonscm.debian.org/viewvc/dep/web/deps/dep5.mdwn?view=markup&pathrev=135 2 | Maintainer: Steve Kemp 3 | Source: http://github.com/skx/chronicle2 4 | Name: Chronicle 5 | 6 | Files: * 7 | Copyright: Steve Kemp 8 | License: Artistic 9 | 10 | Files: debian/* 11 | Copyright: 2014, Steve Kemp 12 | License: GPL-2+ or Artistic 13 | 14 | License: GPL-2+ 15 | This program is free software; you can redistribute it and/or modify 16 | it under the terms of the GNU General Public License as published by 17 | the Free Software Foundation; either version 1, or (at your option) 18 | any later version. 19 | . 20 | On Debian systems, the complete text of version 1 of the GNU General 21 | Public License can be found in `/usr/share/common-licenses/GPL-1'. 22 | 23 | License: Artistic 24 | This program is free software; you can redistribute it and/or modify 25 | it under the terms of the Artistic License, which comes with Perl. 26 | . 27 | On Debian systems, the complete text of the Artistic License can be 28 | found in `/usr/share/common-licenses/Artistic'. 29 | -------------------------------------------------------------------------------- /debian/libchronicle-perl.docs: -------------------------------------------------------------------------------- 1 | COMMENTS.md 2 | README.md 3 | THEMES.md 4 | 5 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ 5 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /lib/App/Chronicle.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | App::Chronicle - Holder for our version number 5 | 6 | =head1 SYNOPSIS 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use App::Chronicle; 12 | 13 | print $App::Chronicle::Version; 14 | 15 | =cut 16 | 17 | =head1 DESCRIPTION 18 | 19 | This module is a mere placeholder for our version number. 20 | 21 | =cut 22 | 23 | 24 | 25 | package App::Chronicle; 26 | 27 | use strict; 28 | use warnings; 29 | 30 | 31 | our $VERSION = "5.1.7"; 32 | 33 | 34 | 35 | 1; 36 | 37 | 38 | =head1 LICENSE 39 | 40 | This module is free software; you can redistribute it and/or modify it 41 | under the terms of either: 42 | 43 | a) the GNU General Public License as published by the Free Software 44 | Foundation; either version 2, or (at your option) any later version, 45 | or 46 | 47 | b) the Perl "Artistic License". 48 | 49 | =cut 50 | 51 | =head1 AUTHOR 52 | 53 | Steve Kemp 54 | 55 | =cut 56 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Archived.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Archived - Generate dated-posts. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module is disabled by default, but if it is enabled your generated 9 | blog will contain links to dated posts. 10 | 11 | For example by default a blog entry might be generated with a URL such 12 | as C. With this module enabled 13 | that will change to C. 14 | 15 | B If you enable or disable this plugin you will need to regenerate 16 | your SQLite database. 17 | 18 | See also C. 19 | 20 | =cut 21 | 22 | =head1 METHODS 23 | 24 | Now follows documentation on the available methods. 25 | 26 | =cut 27 | 28 | package Chronicle::Plugin::Archived; 29 | 30 | use strict; 31 | use warnings; 32 | 33 | 34 | our $VERSION = "5.1.7"; 35 | 36 | 37 | use Date::Format; 38 | use Date::Parse; 39 | 40 | =head2 on_insert 41 | 42 | The C method is automatically invoked when a new blog post 43 | must be inserted into the SQLite database, that might be because a post 44 | is new, or because it has been updated. 45 | 46 | The method is designed to return an updated blog-post structure, 47 | after performing any massaging required. If the method returns undef 48 | then the post is not inserted. 49 | 50 | In this method we rewrite the link of the pending post such that it 51 | is prefixed with the year and month - turning the link into a dated 52 | one. 53 | 54 | =cut 55 | 56 | sub on_insert 57 | { 58 | my ( $self, %args ) = (@_); 59 | 60 | my $dbh = $args{ 'dbh' }; 61 | my $config = $args{ 'config' }; 62 | my $data = $args{ 'data' }; 63 | 64 | # 65 | # Convert the date of the post to a seconds past epoch. 66 | # 67 | my $date = str2time( $data->{ 'date' } ); 68 | 69 | # 70 | # Now build up a new prefix for the file 71 | # 72 | $date = time2str( "%Y/%m/", $date ); 73 | 74 | # 75 | # And prepend that to the link. 76 | # 77 | $data->{ 'link' }->path_prepend($date); 78 | 79 | return ($data); 80 | } 81 | 82 | 83 | 1; 84 | 85 | 86 | =head1 LICENSE 87 | 88 | This module is free software; you can redistribute it and/or modify it 89 | under the terms of either: 90 | 91 | a) the GNU General Public License as published by the Free Software 92 | Foundation; either version 2, or (at your option) any later version, 93 | or 94 | 95 | b) the Perl "Artistic License". 96 | 97 | =cut 98 | 99 | =head1 AUTHOR 100 | 101 | Steve Kemp 102 | 103 | =cut 104 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/DBTweak.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::DBTweak - Speedup the import process 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin is responsible for turning off database synchronization, 9 | which results in a significantly faster import process. 10 | 11 | The downside is that we're at risk of data-lass within the SQLite 12 | database because we're not relying upon the operating system to sync 13 | the database between inserts. 14 | 15 | For our use-case this is not a concern. 16 | 17 | =cut 18 | 19 | =head1 METHODS 20 | 21 | Now follows documentation on the available methods. 22 | 23 | =cut 24 | 25 | package Chronicle::Plugin::DBTweak; 26 | 27 | use strict; 28 | use warnings; 29 | 30 | 31 | our $VERSION = "5.1.7"; 32 | 33 | 34 | =head2 on_db_load 35 | 36 | This method is called when the database is opened, regardless of whether 37 | the database was created or already existed. 38 | 39 | Here we set the pragmas to speedup the insertion process of new entries. 40 | 41 | =cut 42 | 43 | sub on_db_load 44 | { 45 | my ( $self, %args ) = (@_); 46 | 47 | my $dbh = $args{ 'dbh' }; 48 | 49 | $dbh->do("PRAGMA synchronous = OFF"); 50 | $dbh->do("PRAGMA cache_size = 400000"); 51 | } 52 | 53 | 54 | 1; 55 | 56 | 57 | =head1 LICENSE 58 | 59 | This module is free software; you can redistribute it and/or modify it 60 | under the terms of either: 61 | 62 | a) the GNU General Public License as published by the Free Software 63 | Foundation; either version 2, or (at your option) any later version, 64 | or 65 | 66 | b) the Perl "Artistic License". 67 | 68 | =cut 69 | 70 | =head1 AUTHOR 71 | 72 | Steve Kemp 73 | 74 | =cut 75 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Filter.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Filter - Filter individual blog entries. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin is designed to allow blog entries to be filtered via 9 | external commands. 10 | 11 | This is achieved by opening the specified command and using it as 12 | a filter for the entry prior to the insertion into the database. 13 | 14 | As an example the following blog-post would be 100% upper-cased: 15 | 16 | =for example begin 17 | 18 | Title: My Title 19 | Date: 10th March 2015 20 | Filter: tr a-z A-Z 21 | 22 |

This is a line of text.

23 | 24 | =for example end 25 | 26 | =cut 27 | 28 | =head1 METHODS 29 | 30 | Now follows documentation on the available methods. 31 | 32 | =cut 33 | 34 | package Chronicle::Plugin::Filter; 35 | 36 | 37 | use strict; 38 | use warnings; 39 | 40 | 41 | our $VERSION = "5.1.7"; 42 | 43 | 44 | use IPC::Open2; 45 | use Symbol; 46 | 47 | 48 | =head2 on_insert 49 | 50 | The C method is automatically invoked when a new blog post 51 | must be inserted into the SQLite database, that might be because a post 52 | is new, or because it has been updated. 53 | 54 | The method is designed to return an updated blog-post structure, 55 | after performing any massaging required. If the method returns undef 56 | then the post is not inserted. 57 | 58 | This plugin will look for a series of headers in the blog-post: 59 | 60 | =over 8 61 | 62 | =item pre-filter 63 | 64 | This will be called first. 65 | 66 | =item filter 67 | 68 | This will be called in the middle. 69 | 70 | =item post-filter 71 | 72 | This will be called last. 73 | 74 | =back 75 | 76 | Any such header will be assumed to contain a command that the blog-post 77 | should be piped through. The post itself will be replaced with C 78 | from that command. 79 | 80 | Because only single headers are examined there can be no more than three 81 | filters per-post. This constraint exists for compability purposes. 82 | 83 | =cut 84 | 85 | sub on_insert 86 | { 87 | my ( $self, %args ) = (@_); 88 | 89 | my $conf = $args{ 'config' }; 90 | my $data = $args{ 'data' }; 91 | 92 | # 93 | # The filters we run. 94 | # 95 | my @filters; 96 | 97 | # 98 | # Look for the following keys in our entry 99 | # 100 | foreach my $filter (qw! pre-filter filter post-filter !) 101 | { 102 | push( @filters, $data->{ $filter } ) if ( $data->{ $filter } ); 103 | } 104 | 105 | # 106 | # No filters defined? Then return the unmodified post. 107 | # 108 | return ($data) unless ( scalar @filters ); 109 | 110 | 111 | foreach my $filter (@filters) 112 | { 113 | 114 | # 115 | # Get the post body 116 | # 117 | my $body = $data->{ 'body' }; 118 | 119 | # 120 | # Report what we're doing. 121 | # 122 | print "Filtering $data->{'file'} via $filter\n" 123 | if ( $conf->{ 'verbose' } ); 124 | 125 | 126 | # 127 | # Apply the filter. 128 | # 129 | my $WTR = gensym(); 130 | my $RDR = gensym(); 131 | my $pid = open2( $RDR, $WTR, $filter ); 132 | print $WTR $body; 133 | close($WTR); 134 | 135 | # 136 | # Get the output 137 | # 138 | my $result = ""; 139 | while (<$RDR>) 140 | { 141 | $result .= $_; 142 | } 143 | waitpid $pid, 0; 144 | 145 | # 146 | # Store the updated body. 147 | # 148 | $data->{ 'body' } = $result; 149 | } 150 | 151 | # 152 | # Return the updated post. 153 | # 154 | return ($data); 155 | } 156 | 157 | 158 | =head2 _order 159 | 160 | We want this plugin to be called I the other plugins which 161 | filter new entries. 162 | 163 | This method is present such that L can 164 | order our plugins. 165 | 166 | =cut 167 | 168 | sub _order 169 | { 170 | return 200; 171 | } 172 | 173 | 174 | 1; 175 | 176 | 177 | =head1 LICENSE 178 | 179 | This module is free software; you can redistribute it and/or modify it 180 | under the terms of either: 181 | 182 | a) the GNU General Public License as published by the Free Software 183 | Foundation; either version 2, or (at your option) any later version, 184 | or 185 | 186 | b) the Perl "Artistic License". 187 | 188 | =cut 189 | 190 | =head1 AUTHOR 191 | 192 | Steve Kemp 193 | 194 | =cut 195 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Generate/Index.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Generate::Index - Generate the blog-index. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module will be invoked automatically when your site is built 9 | via the C hook which Chronicle provides. 10 | 11 | It is responsible for creating the top-level /index.html file 12 | which is your blogs front-page. 13 | 14 | =cut 15 | 16 | =head1 METHODS 17 | 18 | Now follows documentation on the available methods. 19 | 20 | =cut 21 | 22 | 23 | package Chronicle::Plugin::Generate::Index; 24 | 25 | 26 | use strict; 27 | use warnings; 28 | 29 | 30 | our $VERSION = "5.1.7"; 31 | 32 | 33 | =head2 on_generate 34 | 35 | The C method is automatically invoked to generate output 36 | pages. This particular plugin method is invoked I any 37 | C methods which might be present. 38 | 39 | This method is responsible for generating the front-page of your 40 | blog site, via the theme-template C. 41 | 42 | =cut 43 | 44 | sub on_generate 45 | { 46 | my ( $self, %args ) = (@_); 47 | 48 | my $dbh = $args{ 'dbh' }; 49 | my $config = $args{ 'config' }; 50 | 51 | 52 | # 53 | # The number of posts to show on the front-page 54 | # 55 | my $count = $config->{ 'entry-count' } || 10; 56 | 57 | my $recent = 58 | $dbh->prepare("SELECT id FROM blog ORDER BY date DESC LIMIT 0,$count") or 59 | die "Failed to find recent posts"; 60 | 61 | $recent->execute() or die "Failed to execute:" . $dbh->errstr(); 62 | my $id; 63 | $recent->bind_columns( undef, \$id ); 64 | 65 | 66 | my $entries; 67 | 68 | while ( $recent->fetch() ) 69 | { 70 | my $post = 71 | Chronicle::getBlog( dbh => $dbh, 72 | id => $id, 73 | config => $config 74 | ); 75 | if ( $config->{ 'lower-case' } ) 76 | { 77 | $post->{ 'link' } = lc( $post->{ 'link' } ); 78 | } 79 | 80 | push( @$entries, $post ); 81 | } 82 | $recent->finish(); 83 | 84 | 85 | # 86 | # The index-file to generate 87 | # 88 | my $index = $config->{ 'index_filename' } || "index.html"; 89 | 90 | $config->{ 'verbose' } && 91 | print "Creating : $config->{'output'}/$index\n"; 92 | 93 | my $c = Chronicle::load_template("index.tmpl"); 94 | return unless ($c); 95 | 96 | $c->param( top => $config->{ 'top' } ); 97 | $c->param( entries => $entries ) if ($entries); 98 | open( my $handle, ">:encoding(UTF-8)", "$config->{'output'}/$index" ) or 99 | die "Failed to open"; 100 | print $handle $c->output(); 101 | close($handle); 102 | } 103 | 104 | 105 | 1; 106 | 107 | 108 | =head1 LICENSE 109 | 110 | This module is free software; you can redistribute it and/or modify it 111 | under the terms of either: 112 | 113 | a) the GNU General Public License as published by the Free Software 114 | Foundation; either version 2, or (at your option) any later version, 115 | or 116 | 117 | b) the Perl "Artistic License". 118 | 119 | =cut 120 | 121 | =head1 AUTHOR 122 | 123 | Steve Kemp 124 | 125 | =cut 126 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Generate/Sitemap.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Generate::Sitemap - Generate a sitemap automatically 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module will be invoked automatically when your site is built 9 | via the C hook that Chronicle provides. 10 | 11 | It is responsible for creating the top-level C file 12 | which you use for search engine submission, etc. 13 | 14 | =cut 15 | 16 | =head1 METHODS 17 | 18 | Now follows documentation on the available methods. 19 | 20 | =cut 21 | 22 | 23 | package Chronicle::Plugin::Generate::Sitemap; 24 | 25 | 26 | use strict; 27 | use warnings; 28 | 29 | 30 | our $VERSION = "5.1.7"; 31 | 32 | 33 | =head2 on_generate 34 | 35 | The C method is automatically invoked to generate output 36 | pages. This particular plugin method is invoked I any 37 | C methods which might be present. 38 | 39 | This method is responsible for generating a sitemap for your site. 40 | 41 | Unlike all the other plugins it doesn't need to use a template because 42 | it can keep track of each distinct page which has been generated. 43 | 44 | The generated sitemap file includes: 45 | 46 | =over 8 47 | 48 | =item All the distinct posts ever made. 49 | 50 | =item A link to the tag-index. 51 | 52 | =item A link to the archive-index. 53 | 54 | =back 55 | 56 | If you merge any static pages, such as C then these will not be 57 | included in the map. 58 | 59 | =cut 60 | 61 | sub on_generate 62 | { 63 | my ( $self, %args ) = (@_); 64 | 65 | my $dbh = $args{ 'dbh' }; 66 | my $config = $args{ 'config' }; 67 | 68 | 69 | # 70 | # Load our HTML::Template file 71 | # 72 | my $tmpl = do {local $/; }; 73 | return unless ( length($tmpl) ); 74 | 75 | 76 | # 77 | # This is the file we're going to write. 78 | # 79 | my $output = $config->{ 'output' } . "/sitemap.xml"; 80 | 81 | my $sql = $dbh->prepare("SELECT link FROM blog") or 82 | die "Failed to prepare: " . $dbh->errstr(); 83 | 84 | my $link; 85 | $sql->execute(); 86 | 87 | $sql->bind_columns( undef, \$link ); 88 | 89 | my $urls; 90 | 91 | while ( $sql->fetch() ) 92 | { 93 | # Handle down-cased sites. 94 | $link = lc($link) if ( $config->{ 'lower-case' } ); 95 | 96 | push( @$urls, { url => $config->{ 'top' } . $link } ); 97 | } 98 | $sql->finish(); 99 | 100 | 101 | # 102 | # Load the template 103 | # 104 | my $template = Chronicle::load_template( undef, $tmpl ); 105 | 106 | $template->param( urls => $urls ) if ($urls); 107 | $template->param( top => $config->{ 'top' } ) if ( $config->{ 'top' } ); 108 | 109 | open( my $handle, ">:encoding(UTF-8)", $output ) or 110 | die "Failed to open output file $output - $!"; 111 | print $handle $template->output(); 112 | close($handle); 113 | 114 | if ( $config->{ 'verbose' } ) 115 | { 116 | print "Wrote " . ( $urls ? scalar(@$urls) : 0 ) . " URLS to $output\n"; 117 | } 118 | 119 | } 120 | 121 | 122 | 1; 123 | 124 | 125 | =head1 LICENSE 126 | 127 | This module is free software; you can redistribute it and/or modify it 128 | under the terms of either: 129 | 130 | a) the GNU General Public License as published by the Free Software 131 | Foundation; either version 2, or (at your option) any later version, 132 | or 133 | 134 | b) the Perl "Artistic License". 135 | 136 | =cut 137 | 138 | =head1 AUTHOR 139 | 140 | Steve Kemp 141 | 142 | =cut 143 | 144 | 145 | __DATA__ 146 | 147 | 152 | 153 | 154 | 0.50 155 | weekly 156 | 157 | 158 | 159 | 0.75 160 | weekly 161 | 162 | 163 | /archive/ 164 | 0.50 165 | weekly 166 | 167 | 168 | /tags/ 169 | 0.50 170 | weekly 171 | 172 | 173 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/InPlacePosts.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::InPlacePosts - maintains the input directory structure. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin is designed to allow blog entries remain in the same 9 | directory structure as the input folder by adding the config 10 | C. 11 | 12 | The default behaviour of chronicle is to flatten any posts 13 | present in the input folder to the http doc root, however this 14 | plugin sets to replicate the input folder stucture. 15 | 16 | =for example begin 17 | 18 | input/ 19 | 2015/ 20 | June/ 21 | 15/ 22 | A_post.post 23 | 24 | 25 | output/ 26 | 2015/ 27 | June/ 28 | 15/ 29 | A_post.html 30 | 31 | =for example end 32 | 33 | See also C. 34 | 35 | =cut 36 | 37 | =head1 METHODS 38 | 39 | Now follows documentation on the available methods. 40 | 41 | =cut 42 | 43 | package Chronicle::Plugin::InPlacePosts; 44 | 45 | use strict; 46 | use warnings; 47 | 48 | our $VERSION = "5.1.7"; 49 | 50 | 51 | =head2 on_insert 52 | 53 | The C method is automatically invoked when a new blog post 54 | must be inserted into the SQLite database, that might be because a post 55 | is new, or because it has been updated. 56 | 57 | The method is designed to return an updated blog meta-data, 58 | after performing any massaging required. 59 | 60 | If within the config has C the posts link meta-data 61 | is changed to reflect the users intent to retain the posts input location. 62 | 63 | =cut 64 | 65 | sub on_insert 66 | { 67 | my ( $self, %args ) = (@_); 68 | 69 | my $config = $args{ 'config' }; 70 | my $data = $args{ 'data' }; 71 | 72 | if ( $config->{ 'entry_inplace' } ) 73 | { 74 | $config->{ 'verbose' } && 75 | print "Changing Link to stay in place: $data->{'file'}\n"; 76 | 77 | my $inplacelink = $data->{ 'file' }; 78 | 79 | # strip off the source dir with the first '/' 80 | # this will be added back later 81 | my $input = $config->{ 'input' }; 82 | $inplacelink =~ s#$input/?##; 83 | 84 | # strip off the filename and add the title in its place 85 | $inplacelink =~ s#/?[^/]+$##; 86 | 87 | # if the $inplacelink variable is empty it means the file lives 88 | # in the root of the input directory, and does not require a '/' 89 | # to be added between the inplace link and the file name. 90 | # Conversely if $inplacelink is not empty the file lives within 91 | # we need to add a '/' between it ans the file name 92 | $inplacelink .= '/' if ( $inplacelink !~ /^\s*$/ ); 93 | 94 | $data->{ 'link' }->path_prepend($inplacelink); 95 | } 96 | 97 | return ($data); 98 | } 99 | 100 | 101 | 1; 102 | 103 | 104 | =head1 LICENSE 105 | 106 | This module is free software; you can redistribute it and/or modify it 107 | under the terms of either: 108 | 109 | a) the GNU General Public License as published by the Free Software 110 | Foundation; either version 2, or (at your option) any later version, 111 | or 112 | 113 | b) the Perl "Artistic License". 114 | 115 | =cut 116 | 117 | =head1 AUTHOR 118 | 119 | Stuart Skelton 120 | 121 | =cut 122 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Markdown.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Markdown - Support markdown-formatted input. 5 | 6 | =head1 DESCRIPTION 7 | 8 | The module allows you to write your input blog-entries in the 9 | Markdown format. 10 | 11 | Add the C header to your entries and they 12 | will be automatically converted as part of the import process. 13 | 14 | B If you enable/disable this plugin you will need to regenerate 15 | your SQLite database, because the conversion happens at import-time. 16 | 17 | =cut 18 | 19 | =head1 METHODS 20 | 21 | Now follows documentation on the available methods. 22 | 23 | =cut 24 | 25 | package Chronicle::Plugin::Markdown; 26 | 27 | 28 | use strict; 29 | use warnings; 30 | 31 | 32 | our $VERSION = "5.1.7"; 33 | 34 | 35 | =head2 on_insert 36 | 37 | The C method is automatically invoked when a new blog post 38 | must be inserted into the SQLite database, that might be because a post 39 | is new, or because it has been updated. 40 | 41 | The method is designed to return an updated blog-post structure, 42 | after performing any massaging required. If the method returns undef 43 | then the post is not inserted. 44 | 45 | If the new entry has a C header which contains the value C 46 | we invoke the L module to perform the HTML conversion. 47 | 48 | =cut 49 | 50 | sub on_insert 51 | { 52 | my ( $self, %args ) = (@_); 53 | 54 | # 55 | # The post data and input format 56 | # 57 | my $data = $args{ 'data' }; 58 | my $format = $data->{ 'format' }; 59 | 60 | if ( $format && ( $format =~ /^markdown$/i ) ) 61 | { 62 | my $test = "use Text::Markdown;"; 63 | ## no critic (Eval) 64 | eval($test); 65 | ## use critic 66 | 67 | if ($@) 68 | { 69 | print <{ $key } = Text::Markdown::markdown( $data->{ $key } ) 82 | if ( $data->{ $key } ); 83 | } 84 | 85 | 86 | 87 | } 88 | return ($data); 89 | } 90 | 91 | 92 | 1; 93 | 94 | 95 | =head1 LICENSE 96 | 97 | This module is free software; you can redistribute it and/or modify it 98 | under the terms of either: 99 | 100 | a) the GNU General Public License as published by the Free Software 101 | Foundation; either version 2, or (at your option) any later version, 102 | or 103 | 104 | b) the Perl "Artistic License". 105 | 106 | =cut 107 | 108 | =head1 AUTHOR 109 | 110 | Steve Kemp 111 | 112 | =cut 113 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/MultiMarkdown.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::MultiMarkdown - Support Multimarkdown-formatted input. 5 | 6 | =head1 DESCRIPTION 7 | 8 | The module allows you to write your input blog-entries in the 9 | Multimarkdown format. 10 | 11 | Add the C header to your entries and they 12 | will be automatically converted as part of the import process. 13 | 14 | B If you enable/disable this plugin you will need to regenerate 15 | your SQLite database, because the conversion happens at import-time. 16 | 17 | =cut 18 | 19 | =head1 METHODS 20 | 21 | Now follows documentation on the available methods. 22 | 23 | =cut 24 | 25 | package Chronicle::Plugin::MultiMarkdown; 26 | 27 | 28 | use strict; 29 | use warnings; 30 | 31 | 32 | our $VERSION = "5.1.7"; 33 | 34 | 35 | =head2 on_insert 36 | 37 | The C method is automatically invoked when a new blog post 38 | must be inserted into the SQLite database, that might be because a post 39 | is new, or because it has been updated. 40 | 41 | The method is designed to return an updated blog-post structure, 42 | after performing any massaging required. If the method returns undef 43 | then the post is not inserted. 44 | 45 | If the new entry has a C header which contains the value C 46 | we invoke the L module to perform the HTML conversion. 47 | 48 | =cut 49 | 50 | sub on_insert 51 | { 52 | my ( $self, %args ) = (@_); 53 | 54 | # 55 | # The post data and input format 56 | # 57 | my $data = $args{ 'data' }; 58 | my $format = $data->{ 'format' }; 59 | 60 | if ( $format && ( $format =~ /^multimarkdown$/i ) ) 61 | { 62 | my $test = "use Text::MultiMarkdown;"; 63 | ## no critic (Eval) 64 | eval($test); 65 | ## use critic 66 | 67 | if ($@) 68 | { 69 | print <{ $key } = Text::MultiMarkdown::markdown( $data->{ $key } ) 81 | if ( $data->{ $key } ); 82 | } 83 | 84 | } 85 | return ($data); 86 | } 87 | 88 | 89 | 1; 90 | 91 | 92 | =head1 LICENSE 93 | 94 | This module is free software; you can redistribute it and/or modify it 95 | under the terms of either: 96 | 97 | a) the GNU General Public License as published by the Free Software 98 | Foundation; either version 2, or (at your option) any later version, 99 | or 100 | 101 | b) the Perl "Artistic License". 102 | 103 | =cut 104 | 105 | =head1 AUTHOR 106 | 107 | Stuart Skelton 108 | 109 | =cut 110 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/PostBuild.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::PostBuild - Execute commands after building the blog 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module exists to provide compatibility with previous 9 | releases of chronicle, which allowed the user to specify 10 | commands to be executed after the blog had been generated. 11 | 12 | If your configuration file defines a command to execute after 13 | building your blog this module will ensure it is executed. 14 | 15 | For example you might write this in your configuration file: 16 | 17 | =for example begin 18 | 19 | post-build = rsync -vazr output/ user@remote:htdocs/ 20 | 21 | =for example end 22 | 23 | Multiple commands may be defined, and they will be executed 24 | in the order listed. 25 | 26 | =cut 27 | 28 | =head1 METHODS 29 | 30 | Now follows documentation on the available methods. 31 | 32 | =cut 33 | 34 | package Chronicle::Plugin::PostBuild; 35 | 36 | 37 | use strict; 38 | use warnings; 39 | 40 | 41 | our $VERSION = "5.1.7"; 42 | 43 | 44 | =head2 on_generate 45 | 46 | The C method is automatically invoked to generate output 47 | pages. This particular plugin method is invoked I any 48 | C methods which might be present. 49 | 50 | This method merely looks for defined post-build commands, and if any 51 | are encountered they are executed via C. 52 | 53 | =cut 54 | 55 | sub on_generate 56 | { 57 | my ( $self, %args ) = (@_); 58 | 59 | my $dbh = $args{ 'dbh' }; 60 | my $config = $args{ 'config' }; 61 | 62 | return unless ( $config->{ 'post-build' } ); 63 | 64 | foreach my $cmd ( @{ $config->{ 'post-build' } } ) 65 | { 66 | $config->{ 'verbose' } && print "PostBuild($cmd)\n"; 67 | system($cmd ); 68 | } 69 | } 70 | 71 | 72 | =head2 _order 73 | 74 | We ensure that this plugin is invoked last by setting a priority of 999, 75 | which is greater than the default supported by L. 76 | 77 | This method is present such that L can 78 | order our plugins. 79 | 80 | =cut 81 | 82 | sub _order 83 | { 84 | return 999; 85 | } 86 | 87 | 88 | 1; 89 | 90 | 91 | =head1 LICENSE 92 | 93 | This module is free software; you can redistribute it and/or modify it 94 | under the terms of either: 95 | 96 | a) the GNU General Public License as published by the Free Software 97 | Foundation; either version 2, or (at your option) any later version, 98 | or 99 | 100 | b) the Perl "Artistic License". 101 | 102 | =cut 103 | 104 | =head1 AUTHOR 105 | 106 | Steve Kemp 107 | 108 | =cut 109 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/PostSpooler.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::PostSpooler - Autopost entries in the future. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin is designed to allow new posts to be scheduled automatically. 9 | 10 | Rather than writing a post with a C header you should instead 11 | write a post with a C header. When such a post is found it 12 | will be added to the blog only if the publish-date is in the past. 13 | 14 | This allows you to write a post such as the following, confident it 15 | will not be included until the target date is reached: 16 | 17 | =for example begin 18 | 19 | Publish: 10th March 2076 20 | Subject: I'm a 100 years old 21 | Tags: life, birthday, fiction 22 | 23 |

Hello, I am old.

24 | 25 | =for example end 26 | 27 | =cut 28 | 29 | =head1 METHODS 30 | 31 | Now follows documentation on the available methods. 32 | 33 | =cut 34 | 35 | package Chronicle::Plugin::PostSpooler; 36 | 37 | 38 | use strict; 39 | use warnings; 40 | 41 | 42 | our $VERSION = "5.1.7"; 43 | 44 | 45 | use Date::Format; 46 | use Date::Parse; 47 | 48 | 49 | =head2 on_insert 50 | 51 | The C method is automatically invoked when a new blog post 52 | must be inserted into the SQLite database, that might be because a post 53 | is new, or because it has been updated. 54 | 55 | The method is designed to return an updated blog-post structure, 56 | after performing any massaging required. If the method returns undef 57 | then the post is not inserted. 58 | 59 | If the post we're being invoked upon does not contain a C 60 | header then this plugin will do nothing. 61 | 62 | If there is such a header the post will be ignored unless that header 63 | is in the past - if the post refers to a future time it will be skipped. 64 | 65 | =cut 66 | 67 | sub on_insert 68 | { 69 | my ( $self, %args ) = (@_); 70 | 71 | my $config = $args{ 'config' }; 72 | my $data = $args{ 'data' }; 73 | 74 | # 75 | # If there is no Publish header then return immediately. 76 | # 77 | return $data unless ( $data->{ 'publish' } ); 78 | 79 | # 80 | # Now we need to see if the post is in the future or not. 81 | # 82 | # Parse the publish-date into seconds and get the current time. 83 | # 84 | my $seconds = str2time( $data->{ 'publish' } ); 85 | my $current = time(); 86 | 87 | # 88 | # Has this date occurred? If so publish. 89 | # 90 | if ( $seconds <= $current ) 91 | { 92 | $data->{ 'date' } = $data->{ 'publish' }; 93 | delete( $data->{ 'publish' } ); 94 | return ($data); 95 | } 96 | else 97 | { 98 | 99 | # 100 | # The post will be published in the future, 101 | # skip it for now. 102 | # 103 | return; 104 | } 105 | } 106 | 107 | 108 | 1; 109 | 110 | 111 | =head1 LICENSE 112 | 113 | This module is free software; you can redistribute it and/or modify it 114 | under the terms of either: 115 | 116 | a) the GNU General Public License as published by the Free Software 117 | Foundation; either version 2, or (at your option) any later version, 118 | or 119 | 120 | b) the Perl "Artistic License". 121 | 122 | =cut 123 | 124 | =head1 AUTHOR 125 | 126 | Steve Kemp 127 | 128 | =cut 129 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/PreBuild.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::PreBuild - Execute commands before building the blog 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module exists to provide compatibility with previous 9 | releases of chronicle, which allowed the user to specify 10 | commands to be executed prior to the blog generation. 11 | 12 | If your configuration file defines a command to before 13 | building your blog this module will ensure it is executed. 14 | 15 | For example you might write this in your configuration file: 16 | 17 | =for example begin 18 | 19 | pre-build = rsync -vazr user@remote::comments/ comments/ 20 | 21 | =for example end 22 | 23 | Multiple commands may be defined, and they will be executed 24 | in the order listed. 25 | 26 | =cut 27 | 28 | =head1 METHODS 29 | 30 | Now follows documentation on the available methods. 31 | 32 | =cut 33 | 34 | package Chronicle::Plugin::PreBuild; 35 | 36 | 37 | use strict; 38 | use warnings; 39 | 40 | 41 | our $VERSION = "5.1.7"; 42 | 43 | 44 | =head2 on_initiate 45 | 46 | The C method is automatically invoked just before any 47 | C methods which might be present. 48 | 49 | This method merely looks for defined pre-build commands, and if any 50 | are encountered they are executed via C. 51 | 52 | =cut 53 | 54 | sub on_initiate 55 | { 56 | my ( $self, %args ) = (@_); 57 | 58 | my $dbh = $args{ 'dbh' }; 59 | my $config = $args{ 'config' }; 60 | 61 | return unless ( $config->{ 'pre-build' } ); 62 | 63 | foreach my $cmd ( @{ $config->{ 'pre-build' } } ) 64 | { 65 | $config->{ 'verbose' } && print "PreBuild($cmd)\n"; 66 | 67 | system($cmd ); 68 | } 69 | } 70 | 71 | 72 | =head2 _order 73 | 74 | We ensure that this plugin is invoked last by setting a priority of 0, 75 | which is lower than the default supported by L. 76 | 77 | This method is present such that L can 78 | order our plugins. 79 | 80 | =cut 81 | 82 | sub _order 83 | { 84 | return 0; 85 | } 86 | 87 | 88 | 1; 89 | 90 | 91 | =head1 LICENSE 92 | 93 | This module is free software; you can redistribute it and/or modify it 94 | under the terms of either: 95 | 96 | a) the GNU General Public License as published by the Free Software 97 | Foundation; either version 2, or (at your option) any later version, 98 | or 99 | 100 | b) the Perl "Artistic License". 101 | 102 | =cut 103 | 104 | =head1 AUTHOR 105 | 106 | Steve Kemp 107 | 108 | =cut 109 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/SkipDrafts.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::SkipDrafts - Ignore draft posts. 5 | 6 | =head1 DESCRIPTION 7 | 8 | If your blog-post contains a "C" header then it will 9 | not be inserted into the blog. 10 | 11 | =cut 12 | 13 | =head1 METHODS 14 | 15 | Now follows documentation on the available methods. 16 | 17 | =cut 18 | 19 | package Chronicle::Plugin::SkipDrafts; 20 | 21 | 22 | use strict; 23 | use warnings; 24 | 25 | 26 | our $VERSION = "5.1.7"; 27 | 28 | 29 | =head2 on_insert 30 | 31 | The C method is automatically invoked when a new blog post 32 | must be inserted into the SQLite database, that might be because a post 33 | is new, or because it has been updated. 34 | 35 | The method is designed to return an updated blog-post structure, 36 | after performing any massaging required. If the method returns undef 37 | then the post is not inserted. 38 | 39 | Here we look for a C header in the post, if one is found then 40 | the method returns undef which causes it to be excluded from the blog 41 | generation process. 42 | 43 | =cut 44 | 45 | sub on_insert 46 | { 47 | my ( $self, %args ) = (@_); 48 | 49 | my $config = $args{ 'config' }; 50 | my $data = $args{ 'data' }; 51 | 52 | # 53 | # We'll return undef here, which will stop the insertion process 54 | # 55 | if ( $data->{ 'draft' } ) 56 | { 57 | $config->{ 'verbose' } && 58 | $data->{ 'filename' } && 59 | print "Skipping draft: $data->{'filename'} \n"; 60 | 61 | ## no critic (ReturnUndef) 62 | return undef; 63 | ## use critic 64 | } 65 | 66 | # 67 | # Otherwise return the unmodified data. 68 | # 69 | return ($data); 70 | } 71 | 72 | 73 | 1; 74 | 75 | 76 | =head1 LICENSE 77 | 78 | This module is free software; you can redistribute it and/or modify it 79 | under the terms of either: 80 | 81 | a) the GNU General Public License as published by the Free Software 82 | Foundation; either version 2, or (at your option) any later version, 83 | or 84 | 85 | b) the Perl "Artistic License". 86 | 87 | =cut 88 | 89 | =head1 AUTHOR 90 | 91 | Steve Kemp 92 | 93 | =cut 94 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Snippets/AllTags.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Snippets::AllTags - Generate a list of tags. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module will be invoked automatically when your site is built 9 | via the C hook which Chronicle provides. 10 | 11 | It is responsible for creating the a data-structure to show all 12 | previously-used tags. 13 | 14 | To use this in your templates add the following: 15 | 16 | =for example begin 17 | 18 | 19 |
  • 20 | 21 |
  • 22 | 23 | 24 | 25 | 26 | =for example end 27 | 28 | =cut 29 | 30 | 31 | =head1 METHODS 32 | 33 | Now follows documentation on the available methods. 34 | 35 | =cut 36 | 37 | package Chronicle::Plugin::Snippets::AllTags; 38 | 39 | 40 | use strict; 41 | use warnings; 42 | 43 | 44 | our $VERSION = "5.1.7"; 45 | 46 | 47 | =head2 on_initiate 48 | 49 | The C method is automatically invoked just before any 50 | C methods which might be present. 51 | 52 | This method updates the global variables, which are made available to 53 | all loaded templates, to define a C variable containing the 54 | all the tags which have ever been used within a blog, along with their 55 | use-counts. 56 | 57 | =cut 58 | 59 | sub on_initiate 60 | { 61 | my ( $self, %args ) = (@_); 62 | 63 | my $dbh = $args{ 'dbh' }; 64 | my $config = $args{ 'config' }; 65 | 66 | # 67 | # Get the tags, and their count. 68 | # 69 | my $sql = $dbh->prepare( 70 | 'SELECT DISTINCT(name),COUNT(name) AS runningtotal FROM tags GROUP BY name ORDER BY name' 71 | ) or 72 | die "Failed to prepare tag cloud"; 73 | $sql->execute() or die "Failed to execute: " . $dbh->errstr(); 74 | 75 | my ( $tag, $count ); 76 | $sql->bind_columns( undef, \$tag, \$count ); 77 | 78 | my $tags; 79 | 80 | # Default sizing options 81 | my $min = $config->{ 'tag_cloud_size_min' } || 5; 82 | my $max = $config->{ 'tag_cloud_size_max' } || 60; 83 | my $step = $config->{ 'tag_cloud_size_step' } || 5; 84 | 85 | # 86 | # Process the results. 87 | # 88 | while ( $sql->fetch() ) 89 | { 90 | my $size = $count * $step + $min; 91 | $size = $max if ( $size > $max ); 92 | 93 | push( @$tags, 94 | { tag => $tag, 95 | count => $count, 96 | tsize => $size 97 | } ); 98 | 99 | } 100 | $sql->finish(); 101 | 102 | 103 | # 104 | # Now we have the structure. 105 | # 106 | $Chronicle::GLOBAL_TEMPLATE_VARS{ "all_tags" } = $tags if ($tags); 107 | } 108 | 109 | 110 | =head2 _order 111 | 112 | This plugin must be called "early". 113 | 114 | This means we're called prior to any of the page-generation plugins, such 115 | that any page-templates which make use of the data-structure we've created 116 | are called after that structure is setup. 117 | 118 | This method is present such that L can 119 | order our plugins. 120 | 121 | =cut 122 | 123 | sub _order 124 | { 125 | return 10; 126 | } 127 | 128 | 129 | 1; 130 | 131 | 132 | =head1 LICENSE 133 | 134 | This module is free software; you can redistribute it and/or modify it 135 | under the terms of either: 136 | 137 | a) the GNU General Public License as published by the Free Software 138 | Foundation; either version 2, or (at your option) any later version, 139 | or 140 | 141 | b) the Perl "Artistic License". 142 | 143 | =cut 144 | 145 | =head1 AUTHOR 146 | 147 | Steve Kemp 148 | 149 | =cut 150 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Snippets/RecentPosts.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Snippets::RecentPosts - Generate recent posts. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module will be invoked automatically when your site is built 9 | via the C hook which Chronicle provides. 10 | 11 | It is responsible for creating the a data-structure to show recent 12 | posts. The number of posts defaults to ten, but can be changed if 13 | you're using a configuration file, via: 14 | 15 | =for example begin 16 | 17 | recent-post-count = 20 18 | 19 | =for example end 20 | 21 | To use this in your theme add the following: 22 | 23 | =for example begin 24 | 25 | 26 |

    Recent Posts

    27 |
      28 | 29 |
    • 30 |
        31 |
      • 32 |
    • 33 | 34 |
    35 | 36 | 37 | =for example end 38 | 39 | =cut 40 | 41 | =head1 METHODS 42 | 43 | Now follows documentation on the available methods. 44 | 45 | =cut 46 | 47 | package Chronicle::Plugin::Snippets::RecentPosts; 48 | 49 | 50 | use strict; 51 | use warnings; 52 | 53 | 54 | use Date::Format; 55 | use Date::Parse; 56 | 57 | 58 | 59 | our $VERSION = "5.1.7"; 60 | 61 | 62 | =head2 on_initiate 63 | 64 | The C method is automatically invoked just before any 65 | C methods which might be present. 66 | 67 | This method updates the global variables, which are made available to 68 | all loaded templates, to define a C variable containing 69 | references to the most recent posts. 70 | 71 | The number of tags included in that list will default to 10, but can 72 | be changed via the C setting in the configuration file. 73 | 74 | =cut 75 | 76 | sub on_initiate 77 | { 78 | my ( $self, %args ) = (@_); 79 | 80 | my $dbh = $args{ 'dbh' }; 81 | my $config = $args{ 'config' }; 82 | 83 | # 84 | # The number of posts to include. 85 | # 86 | my $count = $config->{ 'recent-post-count' } || 10; 87 | 88 | my $recent = 89 | $dbh->prepare("SELECT id FROM blog ORDER BY date DESC LIMIT 0,$count") or 90 | die "Failed to find recent posts"; 91 | 92 | $recent->execute() or die "Failed to execute:" . $dbh->errstr(); 93 | my $id; 94 | $recent->bind_columns( undef, \$id ); 95 | 96 | 97 | my $entries = undef; 98 | 99 | while ( $recent->fetch() ) 100 | { 101 | my $data = Chronicle::getBlog( dbh => $dbh, 102 | id => $id, 103 | config => $config 104 | ); 105 | 106 | $data->{ 'link' } = lc( $data->{ 'link' } ) 107 | if ( $config->{ 'lower-case' } ); 108 | 109 | delete @$data{ qw/ comments body tags truncatedbody / } 110 | ; # get rid of heavy fields 111 | push @$entries, $data; 112 | } 113 | $recent->finish(); 114 | 115 | 116 | # 117 | # Now we have the structure. 118 | # 119 | $Chronicle::GLOBAL_TEMPLATE_VARS{ "recent_posts" } = $entries if $entries; 120 | } 121 | 122 | 123 | =head2 _order 124 | 125 | This plugin must be called "early". 126 | 127 | This means we're called prior to any of the page-generation plugins, such 128 | that any page-templates which make use of the data-structure we've created 129 | are called after that structure is setup. 130 | 131 | This method is present such that L can 132 | order our plugins. 133 | 134 | =cut 135 | 136 | sub _order 137 | { 138 | return 10; 139 | } 140 | 141 | 142 | 1; 143 | 144 | 145 | =head1 LICENSE 146 | 147 | This module is free software; you can redistribute it and/or modify it 148 | under the terms of either: 149 | 150 | a) the GNU General Public License as published by the Free Software 151 | Foundation; either version 2, or (at your option) any later version, 152 | or 153 | 154 | b) the Perl "Artistic License". 155 | 156 | =cut 157 | 158 | =head1 AUTHOR 159 | 160 | Steve Kemp 161 | 162 | =cut 163 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Snippets/RecentTags.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Snippets::RecentTags - Generate a list of recent tags. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This module will be invoked automatically when your site is built 9 | via the C hook which Chronicle provides. 10 | 11 | It is responsible for creating the a data-structure containing 12 | recently used tags. 13 | 14 | To use this plugin add the following to your theme: 15 | 16 | =for example begin 17 | 18 | 19 |

    Recent Tags

    20 |
      21 | 22 |
    • 23 | 24 |
    25 | 26 | 27 | =for example end 28 | 29 | =cut 30 | 31 | =head1 METHODS 32 | 33 | Now follows documentation on the available methods. 34 | 35 | =cut 36 | 37 | 38 | package Chronicle::Plugin::Snippets::RecentTags; 39 | 40 | 41 | use strict; 42 | use warnings; 43 | 44 | 45 | our $VERSION = "5.1.7"; 46 | 47 | 48 | =head2 on_initiate 49 | 50 | The C method is automatically invoked just before any 51 | C methods which might be present. 52 | 53 | This method updates the global variables, which are made available to 54 | all loaded templates, to define a C variable containing the 55 | recently used tag-names. 56 | 57 | The number of tags included in that list will default to 10, but can 58 | be changed via the C setting in the configuration file. 59 | 60 | =cut 61 | 62 | sub on_initiate 63 | { 64 | my ( $self, %args ) = (@_); 65 | 66 | my $dbh = $args{ 'dbh' }; 67 | my $config = $args{ 'config' }; 68 | 69 | # 70 | # The number of tags to include. 71 | # 72 | my $count = $config->{ 'recent-tag-count' } || 10; 73 | 74 | # 75 | # Fetch the most recent tags, obeying the limit. 76 | # 77 | my $recent = $dbh->prepare( 78 | "SELECT a.name FROM tags AS a JOIN blog AS b WHERE ( b.id = a.blog_id ) ORDER BY b.date DESC LIMIT $count" 79 | ) or 80 | die "Failed to find recent tags: " . $dbh->errstr(); 81 | 82 | # 83 | # Count how many times the given tag has been used. 84 | # 85 | $count = $dbh->prepare("SELECT COUNT(name) FROM tags WHERE name=?") or 86 | die "Failed to count tag-usage: " . $dbh->errstr(); 87 | 88 | 89 | # 90 | # Look for the recent tags 91 | # 92 | $recent->execute() or die "Failed to execute:" . $dbh->errstr(); 93 | my $tag; 94 | $recent->bind_columns( undef, \$tag ); 95 | 96 | 97 | my $entries = undef; 98 | my %SEEN; 99 | 100 | while ( $recent->fetch() ) 101 | { 102 | # Count prior-uses 103 | $count->execute($tag); 104 | my $c = $count->fetchrow_array(); 105 | 106 | # store the results, unless this tag is a duplicate 107 | push( @$entries, 108 | { tag => $tag, 109 | count => $c 110 | } ) 111 | unless ( $SEEN{ $tag } ); 112 | 113 | # We avoid using this tag again in the future. 114 | $SEEN{ $tag } += 1; 115 | } 116 | 117 | # Close our statements 118 | $recent->finish(); 119 | $count->finish(); 120 | 121 | # 122 | # Now we have the structure. 123 | # 124 | $Chronicle::GLOBAL_TEMPLATE_VARS{ "recent_tags" } = $entries if ($entries); 125 | } 126 | 127 | 128 | =head2 _order 129 | 130 | This plugin must be called "early". 131 | 132 | This means we're called prior to any of the page-generation plugins, such 133 | that any page-templates which make use of the data-structure we've created 134 | are called after that structure is setup. 135 | 136 | This method is present such that L can 137 | order our plugins. 138 | 139 | =cut 140 | 141 | sub _order 142 | { 143 | return 10; 144 | } 145 | 146 | 147 | 1; 148 | 149 | 150 | =head1 LICENSE 151 | 152 | This module is free software; you can redistribute it and/or modify it 153 | under the terms of either: 154 | 155 | a) the GNU General Public License as published by the Free Software 156 | Foundation; either version 2, or (at your option) any later version, 157 | or 158 | 159 | b) the Perl "Artistic License". 160 | 161 | =cut 162 | 163 | =head1 AUTHOR 164 | 165 | Steve Kemp 166 | 167 | =cut 168 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Textile.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Textile - Support textile-formatted input. 5 | 6 | =head1 DESCRIPTION 7 | 8 | The module allows you to write your input blog-entries in the 9 | Textile format. 10 | 11 | Add the C header to your entries and they 12 | will be automatically converted as part of the import process. 13 | 14 | B If you enable/disable this plugin you will need to regenerate 15 | your SQLite database, because the conversion happens at import-time. 16 | 17 | =cut 18 | 19 | =head1 METHODS 20 | 21 | Now follows documentation on the available methods. 22 | 23 | =cut 24 | 25 | 26 | package Chronicle::Plugin::Textile; 27 | 28 | 29 | use strict; 30 | use warnings; 31 | 32 | 33 | our $VERSION = "5.1.7"; 34 | 35 | 36 | =head2 on_insert 37 | 38 | The C method is automatically invoked when a new blog post 39 | must be inserted into the SQLite database, that might be because a post 40 | is new, or because it has been updated. 41 | 42 | The method is designed to return an updated blog-post structure, 43 | after performing any massaging required. If the method returns undef 44 | then the post is not inserted. 45 | 46 | If the new entry has a C header which contains the value C 47 | we invoke the L module to perform the HTML conversion. 48 | 49 | =cut 50 | 51 | sub on_insert 52 | { 53 | my ( $self, %args ) = (@_); 54 | 55 | # 56 | # The post data and input-format 57 | # 58 | my $data = $args{ 'data' }; 59 | my $format = $data->{ 'format' }; 60 | 61 | if ( $format && ( $format =~ /^textile$/i ) ) 62 | { 63 | my $test = "use Text::Textile;"; 64 | 65 | ## no critic (Eval) 66 | eval($test); 67 | ## use critic 68 | 69 | if ($@) 70 | { 71 | print <{ $key } = $textile->process( $data->{ $key } ) 87 | if ( $data->{ $key } ); 88 | } 89 | 90 | 91 | 92 | } 93 | 94 | return ($data); 95 | } 96 | 97 | 98 | 1; 99 | 100 | 101 | =head1 LICENSE 102 | 103 | This module is free software; you can redistribute it and/or modify it 104 | under the terms of either: 105 | 106 | a) the GNU General Public License as published by the Free Software 107 | Foundation; either version 2, or (at your option) any later version, 108 | or 109 | 110 | b) the Perl "Artistic License". 111 | 112 | =cut 113 | 114 | =head1 AUTHOR 115 | 116 | Steve Kemp 117 | 118 | =cut 119 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/Tidy.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::Tidy - Attempt to fix malformed HTML. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin is designed to prevent malformed HTML from being generated. 9 | 10 | It does that by using the L module to parse the 11 | HTML that has been inserted into the SQLite database and then rewalks 12 | the tree to try to fix broken entries. 13 | 14 | As an example the following bogus HTML will be fixed: 15 | 16 | =for example begin 17 | 18 |

    This is a line of text.

    19 | 20 | =for example end 21 | 22 | Similarly tags that are not closed will be fixed up. 23 | 24 | =cut 25 | 26 | =head1 METHODS 27 | 28 | Now follows documentation on the available methods. 29 | 30 | =cut 31 | 32 | package Chronicle::Plugin::Tidy; 33 | 34 | 35 | use strict; 36 | use warnings; 37 | 38 | 39 | our $VERSION = "5.1.7"; 40 | 41 | 42 | =head2 on_insert 43 | 44 | The C method is automatically invoked when a new blog post 45 | must be inserted into the SQLite database, that might be because a post 46 | is new, or because it has been updated. 47 | 48 | The method is designed to return an updated blog-post structure, 49 | after performing any massaging required. If the method returns undef 50 | then the post is not inserted. 51 | 52 | Here we walk the HTML entry, which might have been written by hand 53 | or which might have been created via L, 54 | or some other plugin, and try to ensure it is well-formed. 55 | 56 | =cut 57 | 58 | sub on_insert 59 | { 60 | my ( $self, %args ) = (@_); 61 | 62 | my $conf = $args{ 'config' }; 63 | my $data = $args{ 'data' }; 64 | my $html = $data->{ 'body' }; 65 | 66 | # 67 | # Load the HTML::TreeBuilder module, if present. 68 | # 69 | foreach my $mod (qw! HTML::TreeBuilder !) 70 | { 71 | my $test = "use $mod;"; 72 | ## no critic (Eval) 73 | eval($test); 74 | ## use critic 75 | 76 | if ($@) 77 | { 78 | return ($data); 79 | } 80 | } 81 | 82 | my $tree = HTML::TreeBuilder->new(); 83 | $tree->ignore_unknown(0); 84 | $tree->ignore_ignorable_whitespace(0); 85 | $tree->no_space_compacting(1); 86 | $tree->p_strict(1); 87 | $tree->store_comments(0); 88 | $tree->store_declarations(0); 89 | $tree->store_pis(0); 90 | $tree->parse_content($html); 91 | 92 | my $txt; 93 | 94 | my @nodes = $tree->disembowel(); 95 | foreach my $node (@nodes) 96 | { 97 | if ( ref $node ) 98 | { 99 | $txt .= $node->as_HTML( undef, '', {} ); 100 | chomp $txt; 101 | $node->delete(); 102 | } 103 | else 104 | { 105 | $txt .= $node; 106 | } 107 | } 108 | $tree->delete(); 109 | 110 | # 111 | # Update the body and return the updated post. 112 | # 113 | $data->{ 'body' } = $txt; 114 | return ($data); 115 | } 116 | 117 | 118 | =head2 _order 119 | 120 | We want this plugin to be called I the other plugins which 121 | filter new entries - so that we can fix their broken HTML. 122 | 123 | This method is present such that L can 124 | order our plugins. 125 | 126 | =cut 127 | 128 | sub _order 129 | { 130 | return 999; 131 | } 132 | 133 | 134 | 1; 135 | 136 | 137 | =head1 LICENSE 138 | 139 | This module is free software; you can redistribute it and/or modify it 140 | under the terms of either: 141 | 142 | a) the GNU General Public License as published by the Free Software 143 | Foundation; either version 2, or (at your option) any later version, 144 | or 145 | 146 | b) the Perl "Artistic License". 147 | 148 | =cut 149 | 150 | =head1 AUTHOR 151 | 152 | Steve Kemp 153 | 154 | =cut 155 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/TruncatedBody.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::TruncatedBody - Support for Truncating longer blog posts. 5 | 6 | =head1 DESCRIPTION 7 | 8 | The module allows you to truncate longer blog posts. 9 | 10 | To use this you need to add C<__CUT__> within the post body to 11 | the start of its own separate line. 12 | 13 | =cut 14 | 15 | =head1 METHODS 16 | 17 | Now follows documentation on the available methods. 18 | 19 | =cut 20 | 21 | package Chronicle::Plugin::TruncatedBody; 22 | 23 | 24 | use strict; 25 | use warnings; 26 | 27 | our $VERSION = "5.1.7"; 28 | 29 | 30 | =head2 on_insert 31 | 32 | The C method is automatically invoked when a new blog post 33 | must be inserted into the SQLite database, that might be because a post 34 | is new, or because it has been updated. 35 | 36 | The method is designed to return an updated blog-post structure, 37 | after performing any massaging required. If the method returns undef 38 | then the post is not inserted. 39 | 40 | If the new entry has a C<__CUT__> on its own line, the text before the 41 | cut is marked as part of the truncated body and a link pointing the 42 | reader to the rest of the article. 43 | 44 | The body text is then cleaned by removing C<__CUT__>. 45 | 46 | :B If there are multiple __CUT__'s within a file, only the first 47 | correctly placed __CUT__ will be used. Other __CUTS__ will be ignored 48 | and will remain within the body and or in the truncated body in the case of a 49 | incorrectly placed __CUT__ prior to a correctly placed __CUT__. 50 | 51 | 52 | 53 | =cut 54 | 55 | sub on_insert 56 | { 57 | my ( $self, %args ) = (@_); 58 | 59 | # 60 | # Get access to the post-data, and configuration-object. 61 | # 62 | my $config = $args{ 'config' }; 63 | my $data = $args{ 'data' }; 64 | 65 | # 66 | # Get the body of the post, and the link 67 | # 68 | my $body = $data->{ 'body' }; 69 | my $link = defined $data->{ 'link' } ? $data->{ 'link' }->as_string : ''; 70 | 71 | # 72 | # The link needs to be qualified. 73 | # 74 | my $top = $config->{ 'top' } || ""; 75 | $link = $top . $link; 76 | 77 | 78 | # we are only concerned with first correct cut 79 | if ( $body =~ /^(.+?)\n^__CUT__/ms ) 80 | { 81 | 82 | # assign the text before the cut to cut and add a link to read more 83 | $data->{ 'truncatedbody' } = $1 . "\n\nRead More"; 84 | 85 | # remove the cut from the main body 86 | $data->{ 'body' } =~ s/^(.+?)\n^__CUT__/$1/ms; 87 | } 88 | return ($data); 89 | } 90 | 91 | 92 | # The order is important as we need to run this before any formatting is 93 | # applied to the post 94 | sub _order 95 | { 96 | 1; 97 | } 98 | 1; 99 | 100 | 101 | =head1 LICENSE 102 | 103 | This module is free software; you can redistribute it and/or modify it 104 | under the terms of either: 105 | 106 | a) the GNU General Public License as published by the Free Software 107 | Foundation; either version 2, or (at your option) any later version, 108 | or 109 | 110 | b) the Perl "Artistic License". 111 | 112 | =cut 113 | 114 | =head1 AUTHOR 115 | 116 | Stuart Skelton 117 | 118 | =cut 119 | -------------------------------------------------------------------------------- /lib/Chronicle/Plugin/YouTube.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Plugin::YouTube - Allow Youtube videos to be embedded. 5 | 6 | =head1 DESCRIPTION 7 | 8 | This plugin allows simple markup to be expanded into inline YouTube 9 | videos. The tag "youtube" will also be automatically applied to 10 | the appropriate entries. 11 | 12 | For example the following blog-post will contain an inline video: 13 | 14 | =for example begin 15 | 16 | Title: My Title 17 | Date: 10th March 2015 18 | 19 | XXXXX 20 | 21 | =for example end 22 | 23 | =cut 24 | 25 | =head1 METHODS 26 | 27 | Now follows documentation on the available methods. 28 | 29 | =cut 30 | 31 | package Chronicle::Plugin::YouTube; 32 | 33 | 34 | use strict; 35 | use warnings; 36 | 37 | 38 | our $VERSION = "5.1.7"; 39 | 40 | 41 | =head2 on_insert 42 | 43 | The C method is automatically invoked when a new blog post 44 | must be inserted into the SQLite database, that might be because a post 45 | is new, or because it has been updated. 46 | 47 | The method is designed to return an updated blog-post structure, 48 | after performing any massaging required. If the method returns undef 49 | then the post is not inserted. 50 | 51 | This plugin will look for lines of the form: 52 | 53 | =for example begin 54 | 55 | $ID 56 | 57 | =for example end 58 | 59 | Any such link will be replaced by an inline version of the video, 60 | and the blog-post will have the string C added to its 61 | tag-list. 62 | 63 | =cut 64 | 65 | sub on_insert 66 | { 67 | my ( $self, %args ) = (@_); 68 | 69 | my $conf = $args{ 'config' }; 70 | my $data = $args{ 'data' }; 71 | 72 | # get the body 73 | my $old_body = $data->{ 'body' }; 74 | my $new_body = ""; 75 | 76 | my $updated = 0; 77 | 78 | # tokenize by line 79 | foreach my $line ( split( /[\r\n]/, $old_body ) ) 80 | { 81 | while ( $line =~ /^(.*)([^<]+)<\/youtube>(.*)$/i ) 82 | { 83 | my $pre = $1; 84 | my $vid = $2; 85 | my $post = $3; 86 | 87 | $line = $1; 88 | $line .= < 90 | EOF 91 | $line .= $3; 92 | 93 | $updated += 1; 94 | } 95 | 96 | $new_body .= $line . "\n"; 97 | } 98 | 99 | if ($updated) 100 | { 101 | $data->{ 'body' } = $new_body; 102 | 103 | if ( $data->{ 'tags' } ) 104 | { 105 | $data->{ 'tags' } .= ",youtube"; 106 | } 107 | else 108 | { 109 | $data->{ 'tags' } .= "youtube"; 110 | } 111 | } 112 | 113 | # 114 | # Return the updated post. 115 | # 116 | return ($data); 117 | } 118 | 119 | 120 | 1; 121 | 122 | 123 | =head1 LICENSE 124 | 125 | This module is free software; you can redistribute it and/or modify it 126 | under the terms of either: 127 | 128 | a) the GNU General Public License as published by the Free Software 129 | Foundation; either version 2, or (at your option) any later version, 130 | or 131 | 132 | b) the Perl "Artistic License". 133 | 134 | =cut 135 | 136 | =head1 AUTHOR 137 | 138 | Steve Kemp 139 | 140 | =cut 141 | -------------------------------------------------------------------------------- /lib/Chronicle/Template/GenericXslate.pm: -------------------------------------------------------------------------------- 1 | package Chronicle::Template::GenericXslate; 2 | 3 | use strict; 4 | use warnings; 5 | use Chronicle::Template; 6 | use parent 'Chronicle::Template'; 7 | use Path::Class; 8 | 9 | =head1 NAME 10 | 11 | Chronicle::Template::GenericXslate - Base class for Xslate based templates 12 | 13 | =head1 DESCRIPTION 14 | 15 | This class contains all the functionality required for templates based on 16 | L but is not intended to be used directly. Its subclasses must 17 | define the methods C<_extension> and C<_syntax>; see 18 | C for trivial examples. 19 | 20 | =head1 METHODS 21 | 22 | =head2 new 23 | 24 | See L 25 | 26 | =cut 27 | 28 | sub new 29 | { 30 | my $class = shift; 31 | 32 | my $test = "use Text::Xslate;"; 33 | 34 | ## no critic (Eval) 35 | eval($test); 36 | ## use critic 37 | 38 | if ($@) 39 | { 40 | die "Failed to load Text::Xslate module - $!"; 41 | } 42 | 43 | my %options = @_; 44 | my $self = $class->SUPER::new(@_); 45 | bless $self, $class; 46 | 47 | if ( $options{ tmpl_string } ) 48 | { 49 | $self->{ render } = sub { 50 | return $self->{ xslate } 51 | ->render_string( $options{ tmpl_string }, $self->{ params } ); 52 | }; 53 | } 54 | else 55 | { 56 | my $filename = sprintf "%s.%s", $options{ tmpl_file }, 57 | $self->_extension; 58 | $self->_theme_file_path($filename) or return; 59 | $self->{ render } = sub { 60 | return $self->{ xslate }->render( $filename, $self->{ params } ); 61 | }; 62 | } 63 | 64 | $self->{ xslate } = 65 | Text::Xslate->new( 66 | path => [$self->_theme_dir, dir( $self->_theme_dir, 'inc' )->stringify], 67 | syntax => $self->_syntax, ); 68 | return $self; 69 | } 70 | 71 | =head2 output 72 | 73 | See L 74 | 75 | =cut 76 | 77 | sub output 78 | { 79 | my $self = shift; 80 | return $self->{ render }->( $self->{ params } ); 81 | } 82 | 83 | 1; 84 | -------------------------------------------------------------------------------- /lib/Chronicle/Template/HTMLTemplate.pm: -------------------------------------------------------------------------------- 1 | package Chronicle::Template::HTMLTemplate; 2 | 3 | use strict; 4 | use warnings; 5 | use Chronicle::Template; 6 | use parent 'Chronicle::Template'; 7 | use HTML::Template; 8 | use Path::Class; 9 | 10 | my %DEFAULT_OPTIONS = ( open_mode => '<:encoding(UTF-8)', 11 | die_on_bad_params => 0, 12 | loop_context_vars => 1, 13 | global_vars => 1, 14 | ); 15 | 16 | =head1 NAME 17 | 18 | Chronicle::Template::HTMLTemplate - L based templates 19 | 20 | =head1 DESCRIPTION 21 | 22 | This class contains all the functionality required for templates based on 23 | L. 24 | 25 | =head1 METHODS 26 | 27 | =head2 new 28 | 29 | See L and C<%DEFAULT_OPTIONS> defined above. 30 | 31 | =cut 32 | 33 | sub new 34 | { 35 | my $class = shift; 36 | my %options = @_; 37 | my $self = $class->SUPER::new(@_); 38 | bless $self, $class; 39 | 40 | if ( exists $options{ tmpl_string } ) 41 | { 42 | $options{ scalarref } = \do {delete $options{ tmpl_string }}; 43 | } 44 | else 45 | { 46 | my $filename = ( delete $options{ tmpl_file } ) . ".tmpl"; 47 | $self->_theme_file_path($filename) or return; 48 | $options{ filename } = $filename; 49 | } 50 | 51 | $self->{ htmpl } = 52 | HTML::Template->new( %DEFAULT_OPTIONS, %options, 53 | path => [$self->_theme_dir], ); 54 | 55 | 56 | 57 | return $self; 58 | } 59 | 60 | =head2 output 61 | 62 | See L 63 | 64 | =cut 65 | 66 | sub output 67 | { 68 | my $self = shift; 69 | my $htmpl = $self->{ htmpl }; 70 | $htmpl->param( $_ => $self->{ params }{ $_ } ) 71 | for keys %{ $self->{ params } }; 72 | return $htmpl->output; 73 | } 74 | 1; 75 | -------------------------------------------------------------------------------- /lib/Chronicle/Template/Xslate.pm: -------------------------------------------------------------------------------- 1 | package Chronicle::Template::Xslate; 2 | use strict; 3 | use warnings; 4 | use parent 'Chronicle::Template::GenericXslate'; 5 | 6 | =head1 NAME 7 | 8 | Chronicle::Template::Xslate - L templates with Kolon syntax. 9 | 10 | =head1 DESCRIPTION 11 | 12 | This trivial class defines the two private methods C<_syntax> and C<_extension> 13 | to specify processing details for L 14 | 15 | =head1 METHODS 16 | 17 | =head2 _syntax 18 | 19 | Return the syntax to use 20 | 21 | =cut 22 | 23 | sub _syntax {'Kolon'} 24 | 25 | =head2 _extension 26 | 27 | Return the template file extension 28 | 29 | =cut 30 | 31 | sub _extension {'tx'} 32 | 33 | 1; 34 | 35 | -------------------------------------------------------------------------------- /lib/Chronicle/Template/XslateTT.pm: -------------------------------------------------------------------------------- 1 | package Chronicle::Template::XslateTT; 2 | use strict; 3 | use warnings; 4 | use parent 'Chronicle::Template::GenericXslate'; 5 | 6 | =head1 NAME 7 | 8 | Chronicle::Template::XslateTT - L templates with TTerse syntax. 9 | 10 | =head1 DESCRIPTION 11 | 12 | This trivial class defines the two private methods C<_syntax> and C<_extension> 13 | to specify processing details for L 14 | 15 | =head1 METHODS 16 | 17 | =head2 _syntax 18 | 19 | Return the syntax to use 20 | 21 | =cut 22 | 23 | sub _syntax {'TTerse'} 24 | 25 | =head2 _extension 26 | 27 | Return the template file extension 28 | 29 | =cut 30 | 31 | sub _extension {'ttx'} 32 | 33 | 1; 34 | -------------------------------------------------------------------------------- /lib/Chronicle/URI.pm: -------------------------------------------------------------------------------- 1 | package Chronicle::URI; 2 | use strict; 3 | use warnings; 4 | use URI; 5 | use Unicode::Normalize 'normalize'; 6 | 7 | use parent 'URI'; 8 | 9 | our $NORMALFORM = 'C'; 10 | 11 | =head1 NAME 12 | 13 | Chronicle::URI - A URI subclass that simplifies handling of HTTP URIs 14 | 15 | =head1 DESCRIPTION 16 | 17 | It is advantageous to handle URIs as objects for easier Unicode handling, 18 | canonicalization etc. 19 | 20 | We ony need HTTP URIs here so we can save some boilerplate by subclassing URI, 21 | but some extra methods for unescaping and prepending/appending also come in 22 | handy. 23 | 24 | This class will convert any string arguments passed to it to Unicode NFC form 25 | 26 | =head1 METHODS 27 | 28 | =head2 new 29 | 30 | The constructor takes only a single argument that will be assumed to be an http 31 | URI, or in our case usually a path fragment thereof. 32 | 33 | =cut 34 | 35 | sub new 36 | { 37 | my ( $class, $path ) = @_; 38 | my $self = $class->SUPER::new( normalize( $NORMALFORM, $path ), 'http' ); 39 | return bless $self, $class; 40 | } 41 | 42 | =head2 unescaped 43 | 44 | Return the URI or framgemt completely unescaped. That is, the result of 45 | URI::as_iri with additionally all the ASCII characters unescaped. This method 46 | is supposed to generate a filename from an URI. 47 | 48 | =cut 49 | 50 | sub unescaped 51 | { 52 | my ($self) = @_; 53 | my $iri = $self->as_iri; 54 | 55 | # Unescape all the ASCII left escaped by as_iri(); 56 | $iri =~ s/%([[:xdigit:]]{2})/chr(hex $1)/eg; 57 | return $iri; 58 | } 59 | 60 | 61 | =head2 path_append 62 | 63 | Append its string argument to the path part of the URI. 64 | 65 | =cut 66 | 67 | sub path_append 68 | { 69 | my ( $self, $s ) = @_; 70 | return $self->path( $self->path . normalize( $NORMALFORM, $s ) ); 71 | } 72 | 73 | =head2 path_prepend 74 | 75 | Prepend its string argument to the path part of the URI. 76 | 77 | =cut 78 | 79 | sub path_prepend 80 | { 81 | my ( $self, $s ) = @_; 82 | return $self->path( normalize( $NORMALFORM, $s ) . $self->path ); 83 | } 84 | 85 | =head2 i_use_hfs 86 | 87 | This is a class method that must be called prior to creating any objects that 88 | you want to render in a way that will be compatible with serving them from an 89 | HFS+ file system. HFS+ converts non-ASCII characters in file names to Unicode 90 | NFD form fo URIs have to be composed differently or the corresponding files 91 | won't be found later. 92 | 93 | =cut 94 | 95 | sub i_use_hfs 96 | { 97 | our $NORMALFORM = 'D'; 98 | } 99 | 100 | =head1 LICENSE 101 | 102 | This module is free software; you can redistribute it and/or modify it 103 | under the terms of either: 104 | 105 | a) the GNU General Public License as published by the Free Software 106 | Foundation; either version 2, or (at your option) any later version, 107 | or 108 | 109 | b) the Perl "Artistic License". 110 | 111 | =head1 AUTHOR 112 | 113 | Matthias Bethke 114 | 115 | =cut 116 | 117 | 1; 118 | 119 | -------------------------------------------------------------------------------- /lib/Chronicle/Utils.pm: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Chronicle::Utils - some utility functions needed here and there 5 | 6 | =head1 DESCRIPTION 7 | 8 | So far, this only contains the date/time localization function to avoid too 9 | much ugly cross-namespace calling. 10 | 11 | =head1 FUNCTIONS 12 | 13 | =head2 format_datetime 14 | 15 | Format a date or time value to a string. Uses a config setting passed in $config_var 16 | or a default format in $date_fmt to generate both an English and a localized version. 17 | An empty list is returned if the format string doesn't contain a % character. 18 | 19 | =cut 20 | 21 | =head1 LICENSE 22 | 23 | This module is free software; you can redistribute it and/or modify it 24 | under the terms of either: 25 | 26 | a) the GNU General Public License as published by the Free Software 27 | Foundation; either version 2, or (at your option) any later version, 28 | or 29 | 30 | b) the Perl "Artistic License". 31 | 32 | =cut 33 | 34 | =head1 AUTHOR 35 | 36 | Matthias Bethke 37 | 38 | =cut 39 | 40 | 41 | package Chronicle::Utils; 42 | 43 | use strict; 44 | use warnings; 45 | 46 | use Encode qw/ decode /; 47 | use Date::Format; 48 | use Date::Language; 49 | use parent 'Exporter'; 50 | 51 | our @EXPORT_OK = qw/ format_datetime /; 52 | 53 | 54 | my $date_loc = Date::Language->new( $ENV{ 'MONTHS' } // "English" ); 55 | 56 | sub format_datetime 57 | { 58 | my ( $config, $config_var, $format, $time ) = @_; 59 | 60 | # If the config overrides the default $date_fmt, use it 61 | $format = $config->{ $config_var } if $config and $config->{ $config_var }; 62 | 63 | return unless $format =~ /%/; 64 | 65 | return 66 | time2str( $format, $time ), 67 | decode( 'ISO-8859-1', $date_loc->time2str( $format, $time ) ); 68 | } 69 | 70 | 1; 71 | -------------------------------------------------------------------------------- /t/load-modules.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 24; 7 | 8 | BEGIN 9 | { 10 | 11 | # 12 | # Helpers 13 | # 14 | use_ok( "Chronicle::Config::Reader", "Loaded module" ); 15 | 16 | # 17 | # General plugins 18 | # 19 | use_ok( "Chronicle::Plugin::Archived", "Loaded module" ); 20 | use_ok( "Chronicle::Plugin::DBTweak", "Loaded module" ); 21 | use_ok( "Chronicle::Plugin::Filter", "Loaded module" ); 22 | use_ok( "Chronicle::Plugin::Markdown", "Loaded module" ); 23 | use_ok( "Chronicle::Plugin::MultiMarkdown", "Loaded module" ); 24 | use_ok( "Chronicle::Plugin::PostBuild", "Loaded module" ); 25 | use_ok( "Chronicle::Plugin::PostSpooler", "Loaded module" ); 26 | use_ok( "Chronicle::Plugin::PreBuild", "Loaded module" ); 27 | use_ok( "Chronicle::Plugin::SkipDrafts", "Loaded module" ); 28 | use_ok( "Chronicle::Plugin::Textile", "Loaded module" ); 29 | use_ok( "Chronicle::Plugin::Tidy", "Loaded module" ); 30 | use_ok( "Chronicle::Plugin::YouTube", "Loaded module" ); 31 | 32 | # 33 | # Snippets 34 | # 35 | use_ok( "Chronicle::Plugin::Snippets::AllTags", "Loaded module" ); 36 | use_ok( "Chronicle::Plugin::Snippets::Archives", "Loaded module" ); 37 | use_ok( "Chronicle::Plugin::Snippets::Meta", "Loaded module" ); 38 | use_ok( "Chronicle::Plugin::Snippets::RecentPosts", "Loaded module" ); 39 | use_ok( "Chronicle::Plugin::Snippets::RecentTags", "Loaded module" ); 40 | 41 | # 42 | # Generators 43 | # 44 | use_ok( "Chronicle::Plugin::Generate::Archive", "Loaded module" ); 45 | use_ok( "Chronicle::Plugin::Generate::Index", "Loaded module" ); 46 | use_ok( "Chronicle::Plugin::Generate::Pages", "Loaded module" ); 47 | use_ok( "Chronicle::Plugin::Generate::RSS", "Loaded module" ); 48 | use_ok( "Chronicle::Plugin::Generate::Sitemap", "Loaded module" ); 49 | use_ok( "Chronicle::Plugin::Generate::Tags", "Loaded module" ); 50 | } 51 | -------------------------------------------------------------------------------- /t/manifest.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | unless ( $ENV{ RELEASE_TESTING } ) 9 | { 10 | plan( skip_all => "Author tests not required for installation" ); 11 | } 12 | 13 | eval "use Test::CheckManifest 0.9"; 14 | plan skip_all => "Test::CheckManifest 0.9 required" if $@; 15 | ok_manifest(); 16 | -------------------------------------------------------------------------------- /t/pod-coverage.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -Ilib/ -I../lib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | eval "use Test::Pod::Coverage 1.00"; 8 | 9 | plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" 10 | if $@; 11 | 12 | all_pod_coverage_ok(); 13 | -------------------------------------------------------------------------------- /t/pod.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | # Ensure a recent version of Test::Pod 9 | my $min_tp = 1.22; 10 | eval "use Test::Pod $min_tp"; 11 | 12 | plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; 13 | 14 | all_pod_files_ok(); 15 | -------------------------------------------------------------------------------- /t/style-no-tabs.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Test that none of our scripts contain any literal TAB characters. 4 | # 5 | # Steve 6 | # -- 7 | 8 | 9 | use strict; 10 | use warnings; 11 | 12 | use Test::More; 13 | 14 | ## no critic (Eval) 15 | eval "use Test::NoTabs;"; 16 | ## use critic 17 | 18 | plan skip_all => "Test::NoTabs required for testing." if $@; 19 | 20 | all_perl_files_ok(); 21 | -------------------------------------------------------------------------------- /t/test-archive-links.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 9; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN { 12 | use_ok('Chronicle::Plugin::Archived'); 13 | use_ok('Chronicle::URI'); 14 | } 15 | 16 | 17 | # 18 | # Create some data 19 | # 20 | my %data; 21 | my $link = "/some_blog_post.html"; 22 | 23 | $data{ 'body' } = "This is **bold**"; 24 | $data{ 'link' } = Chronicle::URI->new($link); 25 | $data{ 'date' } = scalar( localtime() ); 26 | 27 | 28 | 29 | # 30 | # Run through the plugin and verify the link has a date-prefix now 31 | # 32 | my $out = Chronicle::Plugin::Archived::on_insert( undef, data => \%data ); 33 | 34 | 35 | # 36 | # The body, date, and title won't have changed. 37 | # 38 | foreach my $key (qw! body date title !) 39 | { 40 | is( $out->{ $key }, $data{ $key }, "The blog field is unchanged $key" ); 41 | } 42 | 43 | 44 | # 45 | # But the link should have done 46 | # 47 | isnt( $out->{ 'link' }->as_string, $link, "The link is updated" ); 48 | 49 | 50 | # 51 | # We should expect NNNN/MM/$title 52 | # 53 | my $YEAR = substr( $data{ 'link' }->as_string, 0, 4 ); 54 | my $MON = substr( $data{ 'link' }->as_string, 5, 2 ); 55 | 56 | # 57 | # Test they're numeric. 58 | # 59 | ok( $YEAR =~ /^([0-9]+)$/, "Year is numeric" ); 60 | ok( $MON =~ /^([0-9]+)$/, "Month is numeric" ); 61 | 62 | # 63 | # And the link should make sense - so it should have todays date in it. 64 | # 65 | # FInd the current date. 66 | # 67 | my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = 68 | localtime(time); 69 | $year += 1900; 70 | $mon += 1; 71 | 72 | # 73 | # Ensure the month is zero-padded if appropriate. 74 | # 75 | $mon = sprintf( "%02d", $mon ); 76 | 77 | is( $out->{ 'link' }->as_string, 78 | "$year/$mon/$link", "We got the link we expected: " . $out->{'link'}->as_string ); 79 | -------------------------------------------------------------------------------- /t/test-config-reader.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 10; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Config::Reader');} 12 | require_ok('Chronicle::Config::Reader'); 13 | 14 | 15 | 16 | my $c = Chronicle::Config::Reader->new(); 17 | isa_ok( $c, "Chronicle::Config::Reader", "Construction succeeds" ); 18 | 19 | 20 | # 21 | # The variables we'll update via the config 22 | # 23 | my %VARS; 24 | $VARS{ 'foo' } = 'bar'; 25 | 26 | 27 | # 28 | # Setting a new key. 29 | # 30 | $c->parseLine( \%VARS, "steve = kemp " ); 31 | is( $VARS{ 'steve' }, "kemp", "Setting a single key works" ); 32 | 33 | 34 | # 35 | # Unsetting a previously set value. 36 | # 37 | is( $VARS{ 'foo' }, "bar", "Initial value is OK" ); 38 | $c->parseLine( \%VARS, "foo =" ); 39 | is( $VARS{ 'foo' }, "", "The value has been removed" ); 40 | $c->parseLine( \%VARS, "foo = meow" ); 41 | is( $VARS{ 'foo' }, "meow", "The value has been updated" ); 42 | 43 | # 44 | # Expand ENV 45 | # 46 | SKIP: 47 | { 48 | skip "No USER environment setup", 1 unless ( $ENV{ 'USER' } ); 49 | 50 | $c->parseLine( \%VARS, 'user = $USER' ); 51 | is( $VARS{ 'user' }, $ENV{ 'USER' }, "Environmental variable updated" ); 52 | } 53 | 54 | # 55 | # Test for command. 56 | # 57 | SKIP: 58 | { 59 | skip "No /bin/ls", 2 unless ( -x "/bin/ls" ); 60 | 61 | $c->parseLine( \%VARS, 'ls = `/bin/ls /bin/ls`' ); 62 | ok( $VARS{ 'ls' }, "Command expansion resulted in something" ); 63 | ok( $VARS{ 'ls' } =~ /ls/i, "Command contains something" ); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /t/test-html-tidy.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 3; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::Tidy');} 12 | require_ok('Chronicle::Plugin::Tidy'); 13 | 14 | 15 | # 16 | # Create a fake blog-post 17 | # 18 | my %data; 19 | $data{ 'body' } = "

    This paragraph is unclosed"; 20 | $data{ 'title' } = "Irrelevent"; 21 | 22 | 23 | # 24 | # Run through the plugin and verify that the input hasn't changed. 25 | # 26 | # (Because no "format" key exists in the hash.) 27 | # 28 | my $out = Chronicle::Plugin::Tidy::on_insert( undef, data => \%data ); 29 | 30 | is( $out->{ 'body' }, 31 | "

    This paragraph is unclosed

    ", 32 | "The trailing P was fixed" ); 33 | -------------------------------------------------------------------------------- /t/test-in-place-post.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use Test::More tests => 25; 7 | # 8 | # Load the module. 9 | # 10 | BEGIN { 11 | use_ok('Chronicle::Plugin::InPlacePosts'); 12 | use_ok('Chronicle::URI'); 13 | } 14 | 15 | 16 | # 17 | # Create a fake post 18 | # 19 | our %config; 20 | our %data; 21 | $config{ 'input' } = "./blog"; 22 | 23 | $data{ 'body' } = "

    It is true

    "; 24 | $data{ 'date' } = "10th March 1976"; 25 | $data{ 'file' } = "./blog/2015/June/13/cake.post"; 26 | $data{ 'link' } = Chronicle::URI->new("I_lïke_caກ.html"); 27 | $data{ 'title' } = "I like cake"; 28 | 29 | 30 | # 31 | # Call the plugin 32 | # 33 | my $out = 34 | Chronicle::Plugin::InPlacePosts::on_insert( undef, 35 | config => \%config, 36 | data => \%data 37 | ); 38 | 39 | # 40 | # There should be no change to our data as there is no entry_inplace. 41 | # 42 | 43 | ok( $out, "Returned something." ); 44 | is( ref($out), "HASH", "Which was a hash" ); 45 | foreach my $key ( keys %data ) 46 | { 47 | is( $data{ $key }, $out->{ $key }, "The field is unchanged: $key" ); 48 | } 49 | 50 | 51 | # 52 | # There should be no change to our data as entry_inplace is false 53 | # 54 | $config{ 'entry_inplace' } = 0; 55 | 56 | # 57 | # Call the plugin 58 | # 59 | $out = 60 | Chronicle::Plugin::InPlacePosts::on_insert( undef, 61 | config => \%config, 62 | data => \%data 63 | ); 64 | 65 | ok( $out, "Returned something." ); 66 | is( ref($out), "HASH", "Which was a hash" ); 67 | foreach my $key ( keys %data ) 68 | { 69 | is( $data{ $key }, $out->{ $key }, "The field is unchanged: $key" ); 70 | } 71 | 72 | # 73 | # OK now make the chronicle generate the page in place. 74 | # 75 | $config{ 'entry_inplace' } = 1; 76 | 77 | # 78 | # Call the plugin 79 | # 80 | $out = 81 | Chronicle::Plugin::InPlacePosts::on_insert( undef, 82 | config => \%config, 83 | data => \%data 84 | ); 85 | 86 | ok( $out, "Returned something." ); 87 | is( ref($out), "HASH", "Which was a hash" ); 88 | foreach my $key (qw(body date file title)) 89 | { 90 | is( $data{ $key }, $out->{ $key }, "The field is unchanged: $key" ); 91 | } 92 | 93 | # 94 | # The only data that should be changed is the link. 95 | # and should be changed 96 | # from: 'I_like_cake.html' 97 | # to: '2015/June/13/I_like_cake.html' 98 | is( $out->{ link }->as_string, 99 | '2015/June/13/I_l%C3%AFke_ca%E0%BA%81.html', 100 | "The link field should be changed" ); 101 | 102 | # Restore the link and do the same thing again, this time for HFS 103 | Chronicle::URI::i_use_hfs; 104 | $data{ 'link' } = Chronicle::URI->new("I_lïke_caກ.html"); 105 | $out = 106 | Chronicle::Plugin::InPlacePosts::on_insert( undef, 107 | config => \%config, 108 | data => \%data 109 | ); 110 | is( ref($out), "HASH", "Got a hash" ); 111 | is( $out->{ link }->as_string, 112 | '2015/June/13/I_li%CC%88ke_ca%E0%BA%81.html', 113 | "Link changed correctly to NFD form" ); 114 | -------------------------------------------------------------------------------- /t/test-markdown-formatter.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 6; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::Markdown');} 12 | require_ok('Chronicle::Plugin::Markdown'); 13 | 14 | 15 | # 16 | # Create some data 17 | # 18 | my %data; 19 | $data{ 'body' } = "This is **bold**"; 20 | 21 | 22 | # 23 | # Run through the plugin and verify that the input hasn't changed. 24 | # 25 | # (Because no "format" key exists in the hash.) 26 | # 27 | my $f = Chronicle::Plugin::Markdown::on_insert( undef, data => \%data ); 28 | is( $f->{ 'body' }, $data{ 'body' }, 29 | "Body is unchanged with no formatter set" ); 30 | 31 | # 32 | # Now we'll set a format type, and ensure that this has caused 33 | # the expected expansion to happen. 34 | # 35 | foreach my $type (qw! markdown MARKDOWN MarkDoWN !) 36 | { 37 | $data{ 'format' } = $type; 38 | 39 | my $out = Chronicle::Plugin::Markdown::on_insert( undef, data => \%data ); 40 | 41 | is( $out->{ 'body' }, 42 | "

    This is bold

    \n", 43 | "Body has been processed with formatter:" . $type ); 44 | } 45 | -------------------------------------------------------------------------------- /t/test-multimarkdown-formatter.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | # 9 | # SHould we skip the tests? 10 | # 11 | my $skip = 0; 12 | 13 | 14 | # 15 | # Load the module. 16 | ## no critic (Eval) 17 | eval "use Text::MultiMarkdown;"; 18 | ## use critic 19 | 20 | $skip = 1 if ($@); 21 | 22 | 23 | 24 | SKIP: 25 | { 26 | 27 | if ($skip) 28 | { 29 | done_testing; 30 | exit(0); 31 | } 32 | 33 | BEGIN {use_ok('Chronicle::Plugin::MultiMarkdown');} 34 | require_ok('Chronicle::Plugin::MultiMarkdown'); 35 | 36 | # 37 | # Create some data 38 | # 39 | my %data; 40 | $data{ 'body' } = 41 | "Here is some text containing a footnote[^somesamplefootnote]. You can then continue your thought... 42 | 43 | [^somesamplefootnote]: Here is the text of the footnote itself."; 44 | 45 | 46 | # 47 | # Run through the plugin and verify that the input hasn't changed. 48 | # 49 | # (Because no "format" key exists in the hash.) 50 | # 51 | my $f = 52 | Chronicle::Plugin::MultiMarkdown::on_insert( undef, data => \%data ); 53 | is( $f->{ 'body' }, 54 | $data{ 'body' }, 55 | "Body is unchanged with no formatter set" ); 56 | 57 | # 58 | # Now we'll set a format type, and ensure that this has caused 59 | # the expected expansion to happen. 60 | # 61 | foreach my $type (qw! multimarkdown MULTIMARKDOWN MuLtIMarkDoWN !) 62 | { 63 | $data{ 'format' } = $type; 64 | 65 | my $out = 66 | Chronicle::Plugin::MultiMarkdown::on_insert( undef, data => \%data ); 67 | 68 | is( $out->{ 'body' }, 69 | '

    Here is some text containing a footnote1. You can then continue your thought...

    70 | 71 |
    72 |
    73 |
      74 | 75 |
    1. Here is the text of the footnote itself. ↩

    2. 76 | 77 |
    78 |
    79 | ', 80 | "Body has been processed with formatter:" . $type 81 | ); 82 | } 83 | 84 | # 85 | # All done 86 | # 87 | done_testing; 88 | } 89 | -------------------------------------------------------------------------------- /t/test-publish-future.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 5; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::PostSpooler');} 12 | require_ok('Chronicle::Plugin::PostSpooler'); 13 | 14 | 15 | 16 | # 17 | # Create a fake blog-post 18 | # 19 | my %data; 20 | $data{ 'body' } = "This is **bold**"; 21 | $data{ 'publish' } = scalar( localtime() ); 22 | $data{ 'publish' } =~ s/(20[0-9]+)/2099/g; 23 | 24 | 25 | # 26 | # Run through the plugin and verify the the post won't be added. 27 | # 28 | my $out = Chronicle::Plugin::PostSpooler::on_insert( undef, data => \%data ); 29 | is( $out, undef, "The future post won't be made live" ); 30 | 31 | # 32 | # OK now try a post that is in the past. 33 | # 34 | $data{ 'publish' } =~ s/2099/1999/g; 35 | 36 | # 37 | # This should be present. 38 | # 39 | $out = Chronicle::Plugin::PostSpooler::on_insert( undef, data => \%data ); 40 | is( $out->{ 'publish' }, 41 | undef, "Thie publish field was removed in a post to be published." ); 42 | ok( $out->{ 'date' } =~ /1999/, "The post is dated in the past, as expected." ); 43 | -------------------------------------------------------------------------------- /t/test-skip-drafts.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 9; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::SkipDrafts');} 12 | require_ok('Chronicle::Plugin::SkipDrafts'); 13 | 14 | 15 | # 16 | # Create a fake post 17 | # 18 | our %config; 19 | our %data; 20 | 21 | $data{ 'title' } = "I like cake"; 22 | $data{ 'body' } = "

    It is true

    "; 23 | $data{ 'date' } = "10th March 1976"; 24 | 25 | 26 | # 27 | # Call the plugin 28 | # 29 | my $out = 30 | Chronicle::Plugin::SkipDrafts::on_insert( undef, 31 | config => \%config, 32 | data => \%data 33 | ); 34 | 35 | # 36 | # There should be no change to our data. 37 | # 38 | ok( $out, "Returned something." ); 39 | is( ref($out), "HASH", "Which was a hash" ); 40 | foreach my $key ( keys %data ) 41 | { 42 | is( $data{ $key }, $out->{ $key }, "The field is unchanged: $key" ); 43 | } 44 | 45 | 46 | # 47 | # OK now make the post a draft, and repeat. 48 | # 49 | # This time we should get null replies. 50 | # 51 | $data{ 'draft' } = 1; 52 | 53 | $out = 54 | Chronicle::Plugin::SkipDrafts::on_insert( undef, 55 | config => \%config, 56 | data => \%data 57 | ); 58 | 59 | # 60 | # There should be no change to our data. 61 | # 62 | ok( !$out, "Returned empty result for draft-post." ); 63 | is( ref($out), '', "This is not a hash" ); 64 | -------------------------------------------------------------------------------- /t/test-strict.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | BEGIN 9 | { 10 | my $str = "use Test::Strict;"; 11 | 12 | ## no critic (Eval) 13 | eval($str); 14 | ## use critic 15 | 16 | plan skip_all => "Skipping as Test::Strict isn't installed" 17 | if ($@); 18 | } 19 | 20 | all_perl_files_ok(); 21 | -------------------------------------------------------------------------------- /t/test-textile-formatter.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 6; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::Textile');} 12 | require_ok('Chronicle::Plugin::Textile'); 13 | 14 | 15 | # 16 | # Create some data 17 | # 18 | my %data; 19 | $data{ 'body' } = "This is *bold*"; 20 | 21 | 22 | # 23 | # Run through the plugin and verify that the input hasn't changed. 24 | # 25 | # (Because no "format" key exists in the hash.) 26 | # 27 | my $f = Chronicle::Plugin::Textile::on_insert( undef, data => \%data ); 28 | is( $f->{ 'body' }, $data{ 'body' }, 29 | "Body is unchanged with no formatter set" ); 30 | 31 | # 32 | # Now we'll set a format type, and ensure that this has caused 33 | # the expected expansion to happen. 34 | # 35 | foreach my $type (qw! Textile TEXTILE textile !) 36 | { 37 | $data{ 'format' } = $type; 38 | 39 | my $out = Chronicle::Plugin::Textile::on_insert( undef, data => \%data ); 40 | 41 | is( $out->{ 'body' }, 42 | "

    This is bold

    ", 43 | "Body has been processed with formatter:" . $type ); 44 | } 45 | -------------------------------------------------------------------------------- /t/test-truncatedbody.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 12; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::TruncatedBody');} 12 | require_ok('Chronicle::Plugin::TruncatedBody'); 13 | 14 | 15 | # 16 | # Create some data 17 | # 18 | 19 | 20 | # 21 | # This tests the correct form of one __CUT__ 22 | # 23 | 24 | my %correct_cut_data; 25 | $correct_cut_data{ 'body' } = "Text before the cut 26 | __CUT__ 27 | 28 | text after the cut"; 29 | 30 | 31 | my $out = Chronicle::Plugin::TruncatedBody::on_insert( undef, 32 | data => \%correct_cut_data ); 33 | 34 | is( $out->{ 'body' }, 'Text before the cut 35 | 36 | text after the cut', 'one cut body' 37 | ); 38 | 39 | is( $out->{ 'truncatedbody' }, "Text before the cut 40 | 41 | Read More", 'one cut truncatedbody' 42 | ); 43 | 44 | 45 | # 46 | # This tests the incorrect form of one __CUT__ 47 | # 48 | 49 | my %incorrect_cut_data; 50 | $incorrect_cut_data{ 'body' } = "Text before the cut 51 | __CUT__ 52 | 53 | text after the cut"; 54 | 55 | 56 | $out = Chronicle::Plugin::TruncatedBody::on_insert( undef, 57 | data => \%incorrect_cut_data ); 58 | 59 | is( $out->{ 'body' }, 'Text before the cut 60 | __CUT__ 61 | 62 | text after the cut', 'one incorrect cut body' 63 | ); 64 | 65 | is( $out->{ 'truncatedbody' }, undef, 'one incorrect cut truncatedbody' ); 66 | 67 | 68 | # 69 | # This tests with two __CUT__'s with the both cut being correct 70 | # 71 | 72 | my %two_cut_data; 73 | $two_cut_data{ 'body' } = "Text before the cut 74 | __CUT__ 75 | 76 | text inbetween the cuts 77 | 78 | __CUT__ 79 | text after the cut"; 80 | 81 | 82 | $out = 83 | Chronicle::Plugin::TruncatedBody::on_insert( undef, data => \%two_cut_data ); 84 | 85 | is( $out->{ 'body' }, 'Text before the cut 86 | 87 | text inbetween the cuts 88 | 89 | __CUT__ 90 | text after the cut', 'two cut body' 91 | ); 92 | 93 | is( $out->{ 'truncatedbody' }, "Text before the cut 94 | 95 | Read More", 'two cut truncatedbody' 96 | ); 97 | 98 | 99 | # 100 | # This tests with two __CUT__'s with the first cut being correct 101 | # 102 | 103 | my %two_cut_data_1; 104 | $two_cut_data_1{ 'body' } = "Text before the cut 105 | __CUT__ 106 | 107 | text inbetween the cuts 108 | 109 | __CUT__ 110 | text after the cut"; 111 | 112 | 113 | $out = Chronicle::Plugin::TruncatedBody::on_insert( undef, 114 | data => \%two_cut_data_1 ); 115 | 116 | is( $out->{ 'body' }, 'Text before the cut 117 | 118 | text inbetween the cuts 119 | 120 | __CUT__ 121 | text after the cut', 'two cut body 1' 122 | ); 123 | 124 | is( $out->{ 'truncatedbody' }, "Text before the cut 125 | 126 | Read More", 'two cut truncatedbody 1' 127 | ); 128 | 129 | # 130 | # This tests with two __CUT__'s with the second cut being correct 131 | # 132 | 133 | my %two_cut_data_2; 134 | $two_cut_data_2{ 'body' } = "Text before the cut 135 | __CUT__ 136 | 137 | text inbetween the cuts 138 | 139 | __CUT__ 140 | text after the cut"; 141 | 142 | 143 | $out = Chronicle::Plugin::TruncatedBody::on_insert( undef, 144 | data => \%two_cut_data_2 ); 145 | 146 | is( $out->{ 'body' }, 'Text before the cut 147 | __CUT__ 148 | 149 | text inbetween the cuts 150 | 151 | text after the cut', 'two cut body 2' 152 | ); 153 | 154 | is( $out->{ 'truncatedbody' }, "Text before the cut 155 | __CUT__ 156 | 157 | text inbetween the cuts 158 | 159 | 160 | Read More", 'two cut truncatedbody 2' 161 | ); 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /t/test-version-insertion.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 9; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::Snippets::Meta');} 12 | 13 | package Chronicle; 14 | 15 | our $VERSION = "cake.free"; 16 | our %GLOBAL_TEMPLATE_VARS = (); 17 | # This is a hack that works because the function isn't being tested anyway 18 | sub format_datetime {} 19 | 20 | package main; 21 | 22 | 23 | # 24 | # Ensure things start properly. 25 | # 26 | is( $VERSION, "cake.free", "Our version is sane" ); 27 | is( $Chronicle::VERSION, "cake.free", "The global version is unchanged" ); 28 | 29 | # 30 | # We have no global variables 31 | # 32 | is( scalar keys %GLOBAL_TEMPLATE_VARS, 0, "We have no global variables" ); 33 | is( $GLOBAL_TEMPLATE_VARS{ 'chronicle_release' }, 34 | undef, "Which means we have no chronicle version definition" ); 35 | 36 | 37 | # 38 | # Invoke the plugin 39 | # 40 | Chronicle::Plugin::Snippets::Meta::on_initiate(); 41 | 42 | # 43 | # Now we should have some defined variables. 44 | # 45 | ok( scalar keys %GLOBAL_TEMPLATE_VARS >= 2, 46 | "We have some global variables defined" ); 47 | is( $GLOBAL_TEMPLATE_VARS{ 'chronicle_version' }, 48 | "cake.free", "Including the chronicle release" ); 49 | 50 | # 51 | # And the version is unchanged. 52 | # 53 | is( $VERSION, "cake.free", "The global version is unchanged" ); 54 | is( $Chronicle::VERSION, "cake.free", "The global version is unchanged" ); 55 | -------------------------------------------------------------------------------- /t/test-youtube.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -I../lib/ -Ilib/ 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 6; 7 | 8 | # 9 | # Load the module. 10 | # 11 | BEGIN {use_ok('Chronicle::Plugin::YouTube');} 12 | require_ok('Chronicle::Plugin::YouTube'); 13 | 14 | 15 | # 16 | # Create some data 17 | # 18 | my %data; 19 | $data{ 'date' } = scalar( localtime() ); 20 | $data{ 'body' } = <testme
    24 | 25 | 1234 XXXX 26 | EOF 27 | 28 | 29 | # 30 | # Load the plugin. 31 | # 32 | my $out = Chronicle::Plugin::YouTube::on_insert( undef, data => \%data ); 33 | $out = $out->{ 'body' }; 34 | 35 | # 36 | # We shouldn't have the "
    " tag present any more 37 | # 38 | ok( $out !~ /<\/youtube>/, "The links are removed" ); 39 | 40 | # 41 | # Bud we should have three links 42 | # 43 | foreach my $link (qw! testme 1234 XXXX !) 44 | { 45 | ok( $out =~ /embed\/$link/, "We found a sane embedded link" ); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/archive.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Entries posted in <!-- tmpl_var name='month_name' --> <!-- tmpl_var name='year' --> 5 | 6 | 7 | 8 | 9 | 10 |

    11 | 12 | 13 | 23 | 24 |
    14 |

    Entries posted in

    15 |
    16 | 17 | 18 | 19 | 20 | 21 |
    22 |
    25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/archive_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <!-- tmpl_var name='blog_title' -->: Archive 6 | 7 | 8 | 9 | 10 |

    11 | 12 | 23 | 24 |
    13 |

    Blog Archive

    14 |
    15 |
    16 | 17 |
    18 |

    posts.

    19 | 20 |
    21 |
    22 |
    25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/entry.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 |

    11 | 19 |
    12 | 13 | 14 | 15 | 16 |

     

    17 | 18 |
    20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/add-comment.inc: -------------------------------------------------------------------------------- 1 | 2 | 4 |
    5 |
    6 |

    Add A Comment

    7 |
    8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 |
    Name:
    Email:
    Website:
    Your Comment
    20 |
    23 |
    24 | 27 |

    Your submission will be ignored if the name, email, or comment field is left blank.

    28 |

    Your email address will never be displayed, but your homepage will be.

    29 |
    30 |
    31 |
    32 | 33 | 71 | 72 | 73 | 74 |
    75 |

    Comments are closed on posts which are more than ten days old.

    76 |
    77 | 78 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/blog-post-truncated.inc: -------------------------------------------------------------------------------- 1 |
    2 |

    3 |

    at

    4 |
    5 | 6 | 7 | 8 | 9 | 10 |
    11 |

    12 | 19 | | 20 | 21 | 22 | comments. 23 | 24 | No comments 25 | 26 | 27 |

    28 |
    29 |

     

    30 | 31 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/blog-post.inc: -------------------------------------------------------------------------------- 1 |
    2 |

    3 |

    at

    4 |
    5 | 6 |
    7 |

    8 | 15 | | 16 | 17 | 18 | comments. 19 | 20 | No comments 21 | 22 | 23 |

    24 |
    25 |

     

    26 | 27 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/comment-loop.inc: -------------------------------------------------------------------------------- 1 | 2 |
    3 |

    Comments On This Entry

    4 |
    5 | 6 | 7 |
    8 |
    9 | 10 | 11 | 18 | 25 | 26 | 27 | 30 |
    12 | 13 | [author] 14 | 15 | [gravitar] 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 |
    28 |

    29 |
    31 | 32 |
    33 |
    34 | 35 |
    36 |
    37 | 38 | 39 |
    40 |
    41 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/footer.inc: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/sidebar.inc: -------------------------------------------------------------------------------- 1 |

    Spiral Logo

    2 | 3 | 4 |

    Recent Posts

    5 |
      6 | 7 |
    • 8 |
        9 |
      • 10 |
    • 11 | 12 |
    13 | 14 | 15 | 16 |

    Recent Tags

    17 |
      18 | 19 |
    • 20 | 21 |
    22 | 23 | 24 |

    Links

    25 | 29 | 30 |

    Search

    31 |

    Search prior entries:

    32 |
    33 | 34 | 35 | 36 | 37 |
    38 | 39 |

    RSS Feed

    40 |
      41 |
    • Subscribe to feed
    • 42 |
    43 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/inc/style.inc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' --> 7 | 8 | 9 | 10 |

    11 | 12 | 13 | 20 |
    14 | 15 | 16 | 17 | 18 | 19 |
    21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/page.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 |

    11 | 15 |
    12 |

    13 | 14 |
    16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/static/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/blog.steve.org.uk/static/progress.gif -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/static/spiral.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/blog.steve.org.uk/static/spiral.gif -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/static/xml.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/blog.steve.org.uk/static/xml.gif -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/tag.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Entries tagged <!-- tmpl_var name='tag' escape='html' --> 7 | 8 | 9 | 10 |

    11 | 12 | 13 | 34 | 35 |
    14 |

    Entries tagged

    15 | 16 | 17 |
    18 |

    Related tags: 19 | 20 | ., 21 | 22 |

    23 |
    24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

    No entries were found with the given tag.

    32 | 33 |
    36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /themes/blog.steve.org.uk/tag_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: Tag Cloud 7 | 8 | 9 | 10 |

    11 | 12 | 13 | 23 |
    14 |

    Tag Cloud

    15 | 16 | 17 | 18 | 19 | 20 |

    No tagged posts were found.

    21 | 22 |
    24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /themes/bs2/archive.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> - Archive <!-- tmpl_var name='month_name' --> <!-- tmpl_var name='year' --> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | js/html5shiv.js"> 19 | 20 | 21 | 22 | 42 |
    43 | 48 |
    49 |
    50 |
    51 |
    52 | 53 | 54 | 55 | 56 | 57 |
    58 |
    59 | 60 |
    61 | 62 |
    63 |
    64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /themes/bs2/archive_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> - Archive 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | js/html5shiv.js"> 19 | 20 | 21 | 22 | 42 |
    43 | 47 |
    48 |
    49 |
    50 |
    51 | 52 | 53 |
      54 | 55 |
    • 56 |
        57 | 58 |
      • ()
      • 59 | 60 |
    • 61 | 62 |
    63 | 64 | 65 |
    66 |
    67 | 68 |
    69 | 70 |
    71 |
    72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /themes/bs2/entry.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | js/html5shiv.js"> 19 | 20 | 21 | 22 | 42 |
    43 | 47 |
    48 |
    49 |
    50 |
    51 | 52 | 53 | 54 | 55 | 56 | 57 |
    58 |
    59 | 60 |
    61 | 62 |
    63 |
    64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /themes/bs2/inc/add-comment.inc: -------------------------------------------------------------------------------- 1 | 2 | 4 |
    5 |

    Add A Comment

    6 |
    7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 |
    Name:
    Email:
    Website:
    Your Comment
    18 |
    21 |
    22 | 25 |

    Your submission will be ignored if the name, email, or comment field is left blank.

    26 |

    Your email address will never be displayed, but your homepage will be.

    27 |
    28 | 29 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /themes/bs2/inc/blog-post-truncated.inc: -------------------------------------------------------------------------------- 1 |

    2 | 3 |
    Tags: .,
    4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

    13 | Posted on at - 14 | comments. No comments 15 |

    16 | 17 | -------------------------------------------------------------------------------- /themes/bs2/inc/blog-post.inc: -------------------------------------------------------------------------------- 1 |

    2 | 3 |
    Tags: .,
    4 | 5 | 6 |

    7 | Posted on at - 8 | comments. No comments 9 |

    10 | 11 | -------------------------------------------------------------------------------- /themes/bs2/inc/comment-loop.inc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
    5 |

    Comments

    6 | 7 | 8 |
    9 |
    10 | 11 | 12 | 19 | 26 | 27 | 28 | 31 |
    13 | 14 | [author] 15 | 16 | [gravitar] 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 |
    29 |

    30 |
    32 | 33 |
    34 |
    35 | 36 |
    37 |
    38 | 39 | 40 |
    -------------------------------------------------------------------------------- /themes/bs2/inc/footer.inc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | Powered by 5 | chronicle 6 |

    7 |
    8 |
    9 | -------------------------------------------------------------------------------- /themes/bs2/inc/sidebar.inc: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    Recent Posts

    4 |
      5 | 6 |
    • 7 |
        8 |
      • 9 |
    • 10 | 11 |
    12 | 13 | 14 |

    RSS Feed

    15 | RSS 16 |
    17 | -------------------------------------------------------------------------------- /themes/bs2/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 42 |
    43 | 46 |
    47 |
    48 |
    49 |
    50 | 51 | 52 | 53 | 54 | 55 |
    56 |
    57 | 58 |
    59 | 60 |
    61 |
    62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /themes/bs2/page.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 42 |
    43 |
    44 |
    45 |

    46 | 47 |
    48 | 49 | 50 |
    51 |
    52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /themes/bs2/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/bs2/static/favicon.ico -------------------------------------------------------------------------------- /themes/bs2/static/js/bootstrap-extras.js: -------------------------------------------------------------------------------- 1 | $(function() 2 | { 3 | 4 | // Form css tweaks. 5 | $('.middle input:text, .middle input:password, textarea').not('[class]').addClass('input-xlarge'); 6 | $('.control-group label').addClass('control-label'); 7 | 8 | 9 | var $dropdowns = $('li.dropdown'); // Specifying the element is faster for older browsers 10 | /** 11 | * Mouse events 12 | * 13 | * @description Mimic hoverIntent plugin by waiting for the mouse to 'settle' within the target before triggering 14 | */ 15 | $dropdowns 16 | .on('mouseover', function() // Mouseenter (used with .hover()) does not trigger when user enters from outside document window 17 | { 18 | var $this = $(this); 19 | if ($this.prop('hoverTimeout')) 20 | { 21 | $this.prop('hoverTimeout', clearTimeout($this.prop('hoverTimeout'))); 22 | } 23 | $this.prop('hoverIntent', setTimeout(function() 24 | { 25 | $this.addClass('open'); 26 | }, 250)); 27 | }) 28 | .on('mouseleave', function() 29 | { 30 | var $this = $(this); 31 | if ($this.prop('hoverIntent')) 32 | { 33 | $this.prop('hoverIntent', clearTimeout($this.prop('hoverIntent'))); 34 | } 35 | $this.prop('hoverTimeout', setTimeout(function() 36 | { 37 | $this.removeClass('open'); 38 | }, 250)); 39 | }); 40 | /** 41 | * Touch events 42 | * 43 | * @description Support click to open if we're dealing with a touchscreen 44 | */ 45 | if ('ontouchstart' in document.documentElement) 46 | { 47 | $dropdowns.each(function() 48 | { 49 | var $this = $(this); 50 | this.addEventListener('touchstart', function(e) 51 | { 52 | if (e.touches.length === 1) 53 | { 54 | // Prevent touch events within dropdown bubbling down to document 55 | e.stopPropagation(); 56 | // Toggle hover 57 | if (!$this.hasClass('open')) 58 | { 59 | // Prevent link on first touch 60 | if (e.target === this || e.target.parentNode === this) 61 | { 62 | e.preventDefault(); 63 | } 64 | // Hide other open dropdowns 65 | $dropdowns.removeClass('open'); 66 | $this.addClass('open'); 67 | 68 | // Hide dropdown on touch outside 69 | document.addEventListener('touchstart', closeDropdown = function(e) 70 | { 71 | e.stopPropagation(); 72 | 73 | $this.removeClass('open'); 74 | document.removeEventListener('touchstart', closeDropdown); 75 | }); 76 | } 77 | } 78 | }, false); 79 | }); 80 | } 81 | }); 82 | -------------------------------------------------------------------------------- /themes/bs2/static/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/bs2/static/progress.gif -------------------------------------------------------------------------------- /themes/bs2/tag.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> - Tagged <!-- tmpl_var name='tag' --> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | js/html5shiv.js"> 19 | 20 | 21 | 22 | 42 |
    43 | 48 | 49 | 50 | 56 | 57 | 58 |
    59 |
    60 |
    61 |
    62 | 63 | 64 | 65 | 66 | 67 |
    68 |
    69 | 70 |
    71 | 72 |
    73 |
    74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /themes/bs2/tag_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <!-- tmpl_var name='blog_title' --> - Tags 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | 51 |
    52 | 56 |
    57 |
    58 |
    59 |
    60 | 61 |
      62 | 63 |
    • 64 | 65 |
    66 | 67 |

    No tagged posts were found.

    68 | 69 | 70 |
    71 |
    72 | 73 |
    74 | 75 |
    76 |
    77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /themes/default/archive.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: entries from <!-- tmpl_var name='month_name' --> <!-- tmpl_var name='year' --> 7 | 8 | 9 | 10 | 11 | 12 |

    Entries from .

    13 | 14 |
    15 |
    16 | 17 |
    18 |
    19 | at 20 |
    21 |
    22 | 23 | 24 | 25 | 26 | 27 |
    28 |
    29 | 30 | 31 | comments. 32 | 33 | 34 | 35 | Tags: ., 36 | 37 | No tags 38 | 39 | 40 |
    41 | Referenced by: , 42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |

    RSS Feed

    49 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /themes/default/archive_index.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: Archive 7 | 8 | 9 | 10 | 11 |

    Archive

    12 |
    13 | 14 |
    15 |

    posts.

    16 | 17 |
    18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /themes/default/comment-form.inc: -------------------------------------------------------------------------------- 1 | 2 |

    Add A Comment

    3 |
    4 |
    5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 |
    Your Name
    Your EMail
    Your Comment
    13 |
    17 |
    18 |

    Your submission will be ignored if any field is left blank. But your email address will not be displayed.

    19 |
    20 | 21 | -------------------------------------------------------------------------------- /themes/default/comment-loop.inc: -------------------------------------------------------------------------------- 1 | 2 |

    Comments On This Entry

    3 |
    4 | 5 |
    6 |
    7 |
    8 |
    9 |
    10 | 11 |
    12 | 13 | -------------------------------------------------------------------------------- /themes/default/entry.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | , " /> 17 | 18 | 19 | 20 | 21 | 22 |
    23 |
    24 |
    at
    25 |
    26 | 27 |
    Tags: .,
    28 | 29 | 30 |
    31 | Referenced by: , 32 |
    33 | 34 |
    35 | 36 | 37 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /themes/default/footer.inc: -------------------------------------------------------------------------------- 1 |

    2 | Created by Chronicle v 3 |

    4 | -------------------------------------------------------------------------------- /themes/default/header.inc: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | 4 | 5 | Untitled Blog 6 | 7 |

    8 | 9 |

    10 | 11 |
    12 | 13 | -------------------------------------------------------------------------------- /themes/default/index.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' --> 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 |
    15 |
    at
    16 |
    17 | 18 | 19 | 20 | 21 | 22 |
    23 |
    24 | 25 | 26 | comments. 27 | 28 | 29 | 30 | Tags: ., 31 | 32 | No tags 33 | 34 |
    35 |
    36 |
    37 | 38 |

    RSS feed

    39 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /themes/default/page.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | , " /> 17 | 18 | 19 | 20 | 21 | 22 |

    23 |
    24 | 25 |
    26 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /themes/default/sidebar.inc: -------------------------------------------------------------------------------- 1 | 2 |

    Archived Posts

    3 |
      4 | 5 |
    • 6 |
        7 | 8 |
      • ()
      • 9 | 10 |
    • 11 | 12 |
    13 | 14 | 15 | 16 | 17 |

    All Tags

    18 |
      19 | 20 |
    • 21 | 22 |
    23 | -------------------------------------------------------------------------------- /themes/default/static/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | padding: 0 10px; 4 | padding-top: 1px; 5 | padding-bottom: 1em; 6 | background-color: white; 7 | color: black; 8 | margin: 0; 9 | margin-right: 185px; 10 | border-right: 1px solid rgb(128, 128, 128); 11 | 12 | } 13 | 14 | div.title { 15 | } 16 | 17 | div.title h1 { 18 | margin: 0px -10px; 19 | padding: 5px 10px 0px 10px; 20 | text-align: center; 21 | } 22 | 23 | div.title a { 24 | color: black !important; 25 | text-decoration: none !important; 26 | } 27 | div.title a:hover { 28 | color: black !important; 29 | text-decoration: none !important; 30 | } 31 | 32 | div.title h2 { 33 | margin: 0 0 1ex 0; 34 | padding: 0; 35 | text-align: right; 36 | } 37 | 38 | /* 39 | * Special markup for weblog entries. 40 | */ 41 | div.entry { 42 | border-left: 1px solid rgb(128, 128, 128); 43 | border-right: 1px solid rgb(128, 128, 128); 44 | border-top: 1px solid rgb(128, 128, 128); 45 | border-bottom: 1px solid rgb(128, 128, 128); 46 | margin: 10px 0px; 47 | } 48 | 49 | div.padding { 50 | padding-top: 15px; 51 | padding-bottom: 15px; 52 | } 53 | div.entry div.body { 54 | padding: 10px 10px; 55 | } 56 | 57 | div.entry .title { 58 | background-color: #eee; 59 | border-bottom: 1px solid rgb(128, 128, 128); 60 | font-weight: bold; 61 | font-size: 120%; 62 | padding: 0.26ex 10px; 63 | 64 | } 65 | div.entry div.date { 66 | text-align: right; 67 | } 68 | div.entry div.title a { 69 | color: black !important; 70 | text-decoration: none !important; 71 | } 72 | div.entry div.title a:hover { 73 | color: black !important; 74 | text-decoration: none !important; 75 | } 76 | 77 | div.entry div.tags { 78 | border-top: 1px solid rgb(128, 128, 128); 79 | font-style: italic; 80 | font-family: Verdana, Georgia, Arial, sans-serif; 81 | font-size: 90%; 82 | text-align: right; 83 | } 84 | div.entry div.tags span.comments { 85 | padding-left: 5px; 86 | float: left; 87 | } 88 | 89 | div.entry div.xrefs { 90 | font-style: italic; 91 | font-family: Verdana, Georgia, Arial, sans-serif; 92 | font-size: 90%; 93 | text-align: right; 94 | } 95 | 96 | 97 | div#sidebar { 98 | position: absolute; 99 | top: 0px; 100 | right: 0px; 101 | width: 165px; 102 | 103 | font-family: sans-serif; 104 | font-size: 80%; 105 | 106 | text-align: justify; 107 | 108 | padding: 0 10px; 109 | 110 | background-color: white; 111 | border-left: 1px solid rgb(128, 128, 128); 112 | margin: 0; 113 | } 114 | 115 | pre { 116 | overflow: auto; 117 | background: rgb(230,230,230); 118 | border: solid; 119 | border-width: thin; 120 | padding: 5px 10px; 121 | } 122 | -------------------------------------------------------------------------------- /themes/default/static/xml.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/default/static/xml.gif -------------------------------------------------------------------------------- /themes/default/tag.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: Entries Tagged <!-- tmpl_var name='tag' --> 7 | 8 | 9 | 10 | 11 |

    Entries tagged "".

    12 | 13 |
    14 |
    15 | 16 |
    17 |
    18 | at 19 |
    20 |
    21 | 22 | 23 | 24 | 25 | 26 |
    27 |
    28 | 29 | 30 | comments. 31 | 32 | 33 | 34 | Tags: ., 35 | 36 | 37 |
    38 | Referenced by: , 39 |
    40 | 41 |
    42 |
    43 |
    44 | 45 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /themes/default/tag_index.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: Tag List 7 | 8 | 9 | 10 | 11 |

    Tag Cloud

    12 | 13 |
      14 | 15 |
    • 16 | 17 |
    18 | 19 |

    No tagged posts were found.

    20 | 21 | 22 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /themes/leftbar/archive.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Entries posted in <!-- tmpl_var name='month_name' --> <!-- tmpl_var name='year' --> 5 | 6 | 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 24 |
    15 |

    Entries posted in

    16 |
    17 | 18 | 19 | 20 | 21 | 22 |
    23 |
    25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /themes/leftbar/archive_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <!-- tmpl_var name='blog_title' -->: Archive 6 | 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 25 |
    15 |

    Blog Archive

    16 |
    17 |
    18 | 19 |
    20 |

    posts.

    21 | 22 |
    23 |
    24 |
    26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /themes/leftbar/entry.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 22 |
    15 | 16 | 17 | 18 | 19 |

     

    20 | 21 |
    23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /themes/leftbar/inc/add-comment.inc: -------------------------------------------------------------------------------- 1 | 2 | 4 |
    5 |
    6 |

    Add A Comment

    7 |
    8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 |
    Name:
    Email:
    Website:
    Your Comment
    20 |
    23 |
    24 | 27 |

    Your submission will be ignored if the name, email, or comment field is left blank.

    28 |

    Your email address will never be displayed, but your homepage will be.

    29 |
    30 |
    31 |
    32 | 33 | 71 | 72 | 73 | 74 |
    75 |

    Comments are closed on posts which are more than ten days old.

    76 |
    77 | 78 | -------------------------------------------------------------------------------- /themes/leftbar/inc/analytics.inc: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /themes/leftbar/inc/blog-post-truncated.inc: -------------------------------------------------------------------------------- 1 |
    2 |

    3 |

    at

    4 |
    5 | 6 | 7 | 8 | 9 | 10 |
    11 |

    12 | 19 | | 20 | 21 | 22 | comments. 23 | 24 | No comments 25 | 26 | 27 |

    28 |
    29 |

     

    30 | -------------------------------------------------------------------------------- /themes/leftbar/inc/blog-post.inc: -------------------------------------------------------------------------------- 1 |
    2 |

    3 |

    at

    4 |
    5 | 6 |
    7 |

    8 | 15 | | 16 | 17 | 18 | comments. 19 | 20 | No comments 21 | 22 | 23 |

    24 |
    25 |

     

    26 | 27 | -------------------------------------------------------------------------------- /themes/leftbar/inc/comment-loop.inc: -------------------------------------------------------------------------------- 1 | 2 |
    3 |

    Comments On This Entry

    4 |
    5 | 6 | 7 |
    8 |
    9 | 10 | 11 | 18 | 25 | 26 | 27 | 30 |
    12 | 13 | [author] 14 | 15 | [gravitar] 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 |
    28 |

    29 |
    31 | 32 |
    33 |
    34 | 35 |
    36 |
    37 | 38 | 39 |
    40 |
    41 | -------------------------------------------------------------------------------- /themes/leftbar/inc/sidebar.inc: -------------------------------------------------------------------------------- 1 |

    Spiral Logo

    2 | 3 | 4 |

    Recent Posts

    5 |
      6 | 7 |
    • 8 |
        9 |
      • 10 |
    • 11 | 12 |
    13 | 14 | 15 | 16 |

    Recent Tags

    17 |
      18 | 19 |
    • 20 | 21 |
    22 | 23 | 24 |

    Links

    25 | 29 | 30 |

    Search

    31 |

    Search prior entries:

    32 |
    33 | 34 | 35 | 36 | 37 |
    38 | 39 |

    RSS Feed

    40 |
      41 |
    • Subscribe to feed
    • 42 |
    43 | -------------------------------------------------------------------------------- /themes/leftbar/inc/style.inc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/leftbar/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' --> 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 15 |
    16 | 17 | 18 | 19 | 20 | 21 |
    22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /themes/leftbar/page.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='title' --> 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 20 |
    15 |

    16 |
    17 | 18 |
    19 |
    21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /themes/leftbar/static/spiral.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/leftbar/static/spiral.gif -------------------------------------------------------------------------------- /themes/leftbar/static/xml.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skx/chronicle2/58a75a3b0349ae4c3f8300d34829d77d24ce0e31/themes/leftbar/static/xml.gif -------------------------------------------------------------------------------- /themes/leftbar/tag.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Entries tagged <!-- tmpl_var name='tag' escape='html' --> 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 15 | 25 |
    16 |

    Entries tagged

    17 | 18 | 19 | 20 | 21 | 22 |

    No entries were found with the given tag.

    23 | 24 |
    26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /themes/leftbar/tag_index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <!-- tmpl_var name='blog_title' -->: Tag Cloud 7 | 8 | 9 | 10 | 11 |

    12 | 13 | 14 | 15 |
    16 |

    Tag Cloud

    17 | 18 | 19 | 20 | 21 | 22 |

    No tagged posts were found.

    23 | 24 |
    25 | 28 | 29 | 30 | --------------------------------------------------------------------------------