App-WRT

 view release on metacpan or  search on metacpan

lib/App/WRT.pm  view on Meta::CPAN

    $ 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):

     <perl>my $dog = "Ralph."; return $dog;</perl>

This code is evaluated before any other processing is done, so you can return
any other markup understood by the script and have it handled appropriately.

B<Interpolated variables> - actually keys to the hash underlying the App::WRT
object, for the moment:

     <perl>$self->{title} = "About Ralph, My Dog"; return '';</perl>

     <p>The title is <em>${title}</em>.</p>

This is likely to change at some point, so don't build anything too elaborate
on it.

Embedded code and variables are intended only for use in the F<template> file,
where it's handy to drop in titles or conditionalize aspects of a layout. You
want to be careful with this sort of thing - it's useful in small doses, but
it's also a maintainability nightmare waiting to happen.

B<Includes> - replaced by the contents of the enclosed file path, from the
root of the current wrt project:

    <include>path/to/file</include>

This is a bit constraining, since it doesn't currently allow for files outside
of the current project, but is useful for including HTML generated by some
external script in a page.

B<Several forms of lightweight markup>:

     <markdown>John Gruber's Markdown, by way of
     Text::Markdown::Discount</markdown>

     <textile>Dean Allen's Textile, via Brad Choate's
     Text::Textile.</textile>

     <freeverse>An easy way to
     get properly broken lines
     plus -- em dashes --
     for poetry and such.</freeverse>

B<And a couple of shortcuts>:

lib/App/WRT.pm  view on Meta::CPAN

    );
    if (scalar @filter_list) {
      return $self->{filters}->dispatch($entry, $html, @filter_list);
    }
    return $html;
  }

  return '';
}

=item list_contents($entry, @entries)

Returns links (maybe with icons) for a set of sub-entries within an entry.

=cut

sub list_contents {
  my $self = shift;
  my ($entry) = shift;
  my (@entries) = @_;

  my $contents;
  foreach my $se (@entries) {
    my $linktext = $self->icon_markup("$entry/$se", $se);
    $linktext ||= $se;

    $contents .= q{ }
              . a({ href  => $self->{url_root} . "$entry/$se",
                    title => $se },
                  $linktext);
  }

  return p( em('more:') . " $contents" ) . "\n";
}

=item get_title($entry)

Returns a title for the entry - potentially a cached one extracted earlier from
the entry's HTML; otherwise just reuse the entry path itself.

=cut

sub get_title {
  my ($self, $entry) = @_;

  # Base title - just the entry path:
  my $title = $entry;

  # Do we have anything in the cache?
  if (defined $self->{title_cache}{$entry}) {
    $title = $self->{title_cache}{$entry};
  }
  return $title;
}

=item icon_markup($entry, $alt)

Check if an icon exists for a given entry if so, return markup to include it.
Icons are PNG or JPEG image files following a specific naming convention:

  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.



( run in 0.471 second using v1.01-cache-2.11-cpan-744e820c463 )