|
|
- =pod
-
- =head1 NAME
-
- App::WRT - WRiting Tool, a static site/blog generator and related utilities
-
- =for HTML <a href="https://travis-ci.org/brennen/wrt"><img src="https://travis-ci.org/brennen/wrt.svg?branch=master"></a>
-
- =head1 SYNOPSIS
-
- Using the commandline tools:
-
- $ mkdir project
- $ cd project
- $ wrt init # set up some defaults
- $ wrt display new # print html for new posts 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 but not likely this would run on a Perl as old as 5.10.0. In
- practice, I know that it works under 5.20.2. It should be fine on any
- reasonably modern Linux distribution, and may also be fine on MacOS or a BSD of
- your choosing.
-
- To install the latest development version from the main repo:
-
- $ git clone https://github.com/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 as C<display.pl>, a simple 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.
-
- The C<App::WRT> module will work with FastCGI, if called from the appropriate
- wrapper script, such as C<wrt-fcgi>.
-
- 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.
-
- It's possible (although not as flexible as it ought to be) to redefine the
- directory layout. (See C<%default{entry_map}> below.)
-
- An entry may be either a plain text file, or a directory containing several
- 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
- interpretation), a supported form of lightweight markup, or some combination
- thereof. Actually, an entry may consist of any darn thing you please, as long
- as Perl will agree that it is text, but presumably you're going to be feeding
- this to a browser.
-
- Header tags (<h1>, <h2>, etc.) will be used to display titles in feeds 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. (WordPress, I am
- looking at you.)
-
- 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 allow for files outside of the
- current project, but is useful for including HTML generated by an external
- script in a page.
-
- B<Several forms of lightweight markup>:
-
- <markdown>John Gruber's Markdown, by way of
- Text::Markdown</markdown>
-
- <textile>Dean Allen's Textile, via Brad Choate's
- Text::Textile.</textile>
-
- <freeverse>An easy way to
- get properly broken lines
- plus -- en and em dashes ---
- for poetry and such.</freeverse>
-
- B<And a couple of shortcuts>:
-
- <image>filename.ext
- alt text, if any</image>
-
- <list>
- one list item
-
- another list item
- </list>
-
- As it stands, freeverse, image, and list are not particularly robust.
-
- =head2 TEMPLATES
-
- A single template, specified by the C<template_dir> and C<template> config
- values, is used to render all pages. See F<example/templates/basic> for an
- example, or run C<wrt init> in an empty directory and look at
- F<templates/default>.
-
- Here's a short example:
-
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>${title_prefix} - ${title}</title>
- </head>
-
- <body>
- ${content}
- </body>
-
- </html>
-
- Within templates, C<${foo}> will be replaced with the corresponding
- configuration value. C<${content}> will always be set to the content of the
- current entry.
-
- =head2 CONFIGURATION
-
- Configuration is read from a F<wrt.json> in the directory where the C<wrt>
- utility is invoked, or can (usually) be specified with the C<--config> option.
-
- See F<example/wrt.json> for a sample configuration.
-
- Under the hood, configuration is done by combining a hash called C<%default>
- with values pulled out of the JSON file. Most defaults can be overwritten
- from the config file, but changing some would require writing Perl, since
- they contain things like subroutine references.
-
- =over
-
- =item %default
-
- Here's a verbatim copy of C<%default>, with some commentary about values.
-
- my %default = (
- root_dir => '.', # dir for wrt repository
- entry_dir => 'archives', # dir for entry files
- publish_dir => 'public', # dir to publish site to
- url_root => "/", # root URL for building links
- image_url_root => '', # same for images
- template_dir => 'templates', # dir for template files
- template => 'default', # template to use
- title => '', # current title (used in template)
- title_prefix => '', # a string to slap in front of titles
- stylesheet_url => undef, # path to a CSS file (used in template)
- favicon_url => undef, # path to a favicon (used in template)
- feed_alias => 'feed', # what entry path should correspond to feed?
- feed_length => 30, # how many entries should there be in the feed?
- author => undef, # author name (used in template, feed)
- description => undef, # site description (used in template)
- content => undef, # place to stash content for templates
- embedded_perl => 1, # evaluate embedded <perl> tags?
- default_entry => 'new', # what to display if no entry specified
-
- # A license string for site content:
- license => 'public domain',
-
- # A string value to replace all pages with (useful for occasional
- # situations where every page of a site should serve some other
- # content in-place, like Net Neutrality protest blackouts):
- overlay => undef,
-
- # What gets considered an entry _path_:
- entrypath_expr => qr/^ ([a-z0-9_\/-]+) $/x,
-
- # What gets considered a subentry file (slightly misleading
- # terminology here):
- subentry_expr => qr/^[0-9a-z_-]+(\.(tgz|zip|tar[.]gz|gz|txt))?$/,
-
- # We'll show links for these, but not display them inline:
- binfile_expr => qr/[.](tgz|zip|tar[.]gz|gz|txt|pdf)$/,
- );
-
- =item $default{entry_map}
-
- A hash which will dispatch entries matching various regexen to the appropriate
- output methods. The default looks something like this:
-
- nnnn/[nn/nn/]doc_name - a document within a day.
- nnnn/nn/nn - a specific day.
- nnnn/nn - a month.
- nnnn - a year.
- doc_name - a document in the root directory.
-
- You can re-map things to an arbitrary archive layout.
-
- Since the entry map is a hash, and handle() simply loops over its keys, there
- is no guaranteed precedence of patterns. Be extremely careful that no entry
- will match more than one pattern, or you will wind up with unexpected behavior.
- A good way to ensure that this does not happen is to use patterns like:
-
- qr(
- ^ # start of string
- [0-9/]{4}/ # year
- [0-9]{1,2}/ # month
- [0-9]{1,2] # day
- $ # end of string
- )x
-
- ...always marking the start and end of the string explicitly.
-
- This may eventually be rewritten to use an array so that the order can be
- explicitly specified.
-
- =item $default{entry_descriptions}
-
- A hashref which contains a map of entry titles to entry descriptions.
-
- =back
-
- =head2 METHODS AND INTERNALS
-
- For no bigger than this thing is, the internals are convoluted. (This is
- because it's spaghetti code originally written in a now-archaic language by a
- teenager who didn't know how to program.)
-
- =over
-
- =item new_from_file($config_file)
-
- Takes a filename to pull JSON config data out of, and returns a new App::WRT
- instance with the parameters set in that file.
-
- =item new(%params)
-
- Get a new WRT object with the specified parameters set.
-
- =item display($entry1, $entry2, ...)
-
- Return a string containing the given entries, which are in the form of
- date/entry strings. If no parameters are given, default to default_entry().
-
- display() expands aliases ("new" and "all", for example) as necessary, collects
- output from handle($entry), and wraps the whole thing in a template file.
-
- =item handle($entry)
-
- Return the text of an individual entry.
-
- =begin digression
-
- =item A digression about each()
-
- I once spent a lot of time chasing down a bug caused by a while loop in this
- method. Specifically, I was using while to iterate over the entry_map hash.
- Since C<$self->entry_map> returns a reference to the same hash each time, every
- other request was finding C<each()> mid-way through iterating over this hash.
-
- I initially solved this by copying the hash into a local one called C<%map>
- every time C<handle()> was called. I could also have called C<keys> or
- C<values> on the anonymous hash, as these reset C<each()>.
-
- Presently I'm not using each() or an explicit loop, so this probably doesn't
- make a whole lot of sense in the context of the existing code.
-
- =end digression
-
- =item expand_option($option)
-
- Expands/converts 'all', 'new', and 'fulltext' to appropriate values.
-
- =item recent_month()
-
- Tries to find the most recent month in the archive.
-
- If a year file is text, returns that instead.
-
- =item fulltext()
-
- Returns the full text of all entries, in order.
-
- =item get_all_source_files()
-
- Returns a list of all source files for the current entry archive.
-
- This was originally in App::WRT::Renderer, so there may be some pitfalls here.
-
- =item get_all_day_entries()
-
- Returns a list of all entries which are a specific day.
-
- =item get_all_day_entries()
-
- Returns an easily sortable date of the form nnnn-nn-nn for a given entry.
-
- =item link_bar(@extra_links)
-
- Returns a little context-sensitive navigation bar.
-
- =item month_before($this_month)
-
- Return the month before the given month in the archive.
-
- Very naive; there has got to be a smarter way.
-
- =item year($year)
-
- List out the updates for a year.
-
- =item month($month)
-
- Prints the entries in a given month (nnnn/nn).
-
- =item entry_wrapped($entry, $level)
-
- Wraps entry() in entry_markup.
-
- =item entry_stamped($entry, $level)
-
- Wraps entry() + a datestamp in entry_markup()
-
- =item entry_topic_list($entry)
-
- Get a list of topics (by tag-* files) for the entry. This hardcodes a
- p1k3-specific thing, and is dumb.
-
- =item entry($entry)
-
- Returns the contents of a given entry. Calls dir_list
- and icon_markup. Recursively calls itself.
-
- =item get_sub_entries($entry_loc)
-
- Returns "sub entries" based on the C<subentry_expr> regexp.
-
- =item list_contents($entry, @entries)
-
- Returns links (potentially with icons) for a set of sub-entries within an
- entry.
-
- =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.
-
- =item datestamp($entry)
-
- Returns a nice html datestamp / breadcrumbs for a given entry.
-
- =item fragment_slurp($file)
-
- Read a text fragment, call line_parse() and eval_perl() to take care of funky
- markup and interpreting embedded code, and then return it as a string. Takes
- one parameter, the name of the file, and returns '' if it's not an extant text
- file.
-
- This might be the place to implement an in-memory cache for FastCGI or mod_perl
- environments. The trick is that the results for certain files shouldn't be
- cached because they contain embedded code.
-
- =item month_name($number)
-
- Turn numeric dates into English.
-
- =item root_locations($file)
-
- Given a file/entry, return the appropriate concatenations with
- entry_dir and url_root.
-
- =item local_path($file)
-
- Return an absolute path for a given file. Called by root_locations.
-
- Arguably this is stupid and inefficient.
-
- =item feed_print_latest()
-
- Return an Atom feed for the most recent entries.
-
- Called from display().
-
- =item feed_print(@entries)
-
- Return an Atom feed for the given list of entries.
-
- Requires XML::Atom::SimpleFeed.
-
- =back
-
- =head1 SEE ALSO
-
- walawiki.org, Blosxom, rassmalog, Text::Textile, XML::Atom::SimpleFeed,
- Image::Size, CGI::Fast, and about a gazillion static site generators.
-
- =head1 AUTHOR
-
- Copyright 2001-2017 Brennen Bearnes
-
- =head1 LICENSE
-
- wrt is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
|