App-WRT
view release on metacpan or search on metacpan
lib/App/WRT.pm view on Meta::CPAN
=head1 SYNOPSIS
Using the commandline tools:
$ mkdir project
$ cd project
$ wrt init # set up some defaults
$ wrt config # dump configuration values
$ wrt ls # list entries
$ wrt display new # print HTML for new entries to stdout
$ wrt render-all # publish HTML to project/public/
Using App::WRT in library form:
#!/usr/bin/env perl
use App::WRT;
my $w = App::WRT->new(
entry_dir => 'archives',
url_root => '/',
# etc.
);
print $w->display(@ARGV);
=head1 INSTALLING
It's possible this would run on a Perl as old as 5.14.0. In practice, I know
that it works under 5.26.2. It should be fine on any reasonably modern Linux
distribution, and might work on BSD of your choosing. Maybe even MacOS. It's
possible that it would run under the Windows Subsystem for Linux, but it would
definitely fail under vanilla Windows; it currently makes too many assumptions
about things like directory path separators and filesystem semantics.
(Although I would like the code to be more robust across platforms, this is not
a problem I feel much urgency about solving at the moment, since I'm pretty
sure I am the only user of this software. Please let me know if I'm mistaken.)
To install the latest development version from the main repo:
$ git clone https://code.p1k3.com/gitea/brennen/wrt.git
$ cd wrt
$ perl Build.PL
$ ./Build installdeps
$ ./Build test
$ ./Build install
To install the latest version released on CPAN:
$ cpanm App::WRT
Or:
$ cpan -i App::WRT
You will likely need to use C<sudo> or C<su> to get a systemwide install.
=head1 DESCRIPTION
This started life somewhere around 2001 as C<display.pl>, a CGI script to
concatenate fragments of handwritten HTML by date. It has since accumulated
several of the usual weblog features (lightweight markup, feed generation,
embedded Perl, poetry tools, image galleries, and ill-advised dependencies),
but the basic idea hasn't changed that much.
The C<wrt> utility now generates static HTML files, instead of expecting to
run as a CGI script. This is a better idea, for the most part.
By default, entries are stored in a simple directory tree under C<entry_dir>.
Like:
archives/2001/1/1
archives/2001/1/2/index
archives/2001/1/2/sub_entry
Which will publish files like so:
public/index.html
public/all/index.html
public/2001/index.html
public/2001/1/index.html
public/2001/1/1/index.html
public/2001/1/2/index.html
public/2001/1/2/sub_entry/index.html
Contents will be generated for each year and for the entire collection of dated
entries. Month indices will consist of all entries for that month. A
top-level index file will consist of the most recent month's entries.
An entry may be either a plain UTF-8 text file, or a directory containing
several such files. If it's a directory, a file named "index" will be treated
as the text of the entry, and all other lower-case filenames without extensions
will be treated as sub-entries or documents within that entry, and displayed
accordingly. Links to certain other filetypes will be displayed as well.
Directories may be nested to an arbitrary depth, although it's probably not a
good idea to go very deep with the current display logic.
A PNG or JPEG file with a name like
2001/1/1.icon.png
2001/1/1/index.icon.png
2001/1/1/whatever.icon.png
2001/1/1/whatever/index.icon.png
will be treated as an icon for the corresponding entry file.
=head2 MARKUP
Entries may consist of hand-written HTML (to be passed along without further
mangling), a supported form of lightweight markup, or some combination thereof.
Header tags (<h1>, <h2>, etc.) will be used to display titles in feeds,
navigation, and other places.
Other special markup is indicated by a variety of HTML-like container tags.
B<Embedded Perl> - evaluated and replaced by whatever value you return
(evaluated in a scalar context):
lib/App/WRT.pm view on Meta::CPAN
index.icon.[png|jp(e)g] for directories
[filename].icon.[png|jp(e)g] for flat text files
Calls image_size, uses filename to determine type.
=cut
{ my %cache;
sub icon_markup {
my ($self, $entry, $alt) = @_;
return $cache{$entry . $alt}
if defined $cache{$entry . $alt};
my $icon_basepath;
if ($self->{entries}->is_file($entry)) {
$icon_basepath = "$entry.icon";
}
elsif ($self->{entries}->is_dir($entry)) {
$icon_basepath = "$entry/index.icon";
} else {
# XXX there are bugs lurking here for virtual entries probably
return;
}
# First suffix found will be used:
my $suffix;
for (qw(png jpg gif jpeg)) {
if ($self->{entries}->is_extant( "$icon_basepath.$_")) {
$suffix = $_;
last;
}
}
# Fail unless there's a file with one of the above suffixes:
return 0 unless $suffix;
my ($icon_loc, $icon_url) = $self->root_locations($icon_basepath);
# Slurp width & height from the image file:
my ($width, $height) = image_size(
$self->{root_dir_abs} . '/' . "$icon_loc.$suffix"
);
return $cache{$entry . $alt} =
qq{<img src="$icon_url.$suffix"\n width="$width" }
. qq{height="$height"\n alt="$alt" />};
}
}
=item datestamp($entry)
Returns a nice html datestamp / breadcrumbs for a given entry.
=cut
sub datestamp {
my $self = shift;
my ($entry) = @_;
my @fragment_stack;
my @fragment_stamps = (
a({ href => $self->{url_root} }, $self->{title_prefix}),
);
# Chop up by directory separator:
my @pieces = split '/', $entry;
foreach my $fragment (@pieces) {
push @fragment_stack, $fragment;
push @fragment_stamps,
a({ href => $self->{url_root} . (join '/', @fragment_stack) . '/',
title => $fragment }, $fragment);
}
my $stamp = p({class => 'datestamp'}, join(" /\n", @fragment_stamps));
my $tag_list = $self->entry_tag_list($entry);
if ($tag_list) {
$stamp = "\n" . p({class => 'tags'}, $tag_list) . $stamp;
}
return "\n$stamp\n";
}
=item root_locations($file)
Given an entry, return the appropriate concatenations with entry_dir and
url_root.
=cut
sub root_locations {
return (
$_[0]->{entry_dir} . '/' . $_[1], # location on filesystem
$_[0]->{url_root} . $_[1] # URL
);
}
=item feed_print_recent($count)
Print $count recent entries, falling back to the configured $feed_length.
=cut
sub feed_print_recent {
my ($self, $count) = @_;
$count //= $self->{feed_length};
return $self->feed_print(
$self->{entries}->recent_days($count)
);
}
=item feed_print_json_recent($count)
Print $count recent entries in JSON, falling back to the configured
$feed_length.
=cut
sub feed_print_json_recent {
my ($self, $count) = @_;
$count //= $self->{feed_length};
return $self->feed_print_json(
$self->{entries}->recent_days($count)
);
}
=item feed_print(@entries)
Return an Atom feed for the given list of entries.
Requires XML::Atom::SimpleFeed.
( run in 2.940 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )