blosxom :: the zen of blogging | ||||||
|
Plugins Blosxom sports a plugin architecture, allowing the core of Blosxom to remain small, sleek, and simpler-than-pie while providing room for extension and integration into different environments and uses.The Blosxom Plugin Registry... Browse the Blosxom Plugin Registry for all your plugin needs. There is an ever-growing list of plugins developed by Blosxom users just like you. There are display plugins, fiddling with blog posts on the fly and providing additional template elements. Image plugins provide everything from convenient image references to resizing and creating an image gallery. Input plugins provide writebacks (comments and trackbacks), karma tracking, and email-to-blog gateways. Have a Blosxom Plug-in to share with the Blosxom community? Plug it in here!What is a Plugin...? If you know your way around Perl with some measure of comfort, you can write a Blosxom plugin. Plugins are simply Perl scripts that sit in a Blosxom-accessibleplugins directory and define specific subroutines or functions that are called at particular points in Blosxom's execution.
Using Plugins... Take a gander at the users' plugin documentation for information on activating Blosxom plugin support, installing plugins, ordering plugins, removing plugins, and so forth.Plugin Zen... Of course you're free to do as you choose, but I do hold fond hopes that Blosxom plugins will attempt to keep to the zen of Blosxom everyone seems to like so much, that combination of power and utter ease-of-use. To this end, please do try to make your plugins as simple to install, configure, and run as you possibly can. Ideally, a plugin is a single file to be dropped in to the plugins directory and, optionally, configured. Should you need to add other bits and require other modules, attempt to bundle all this up as a plugin file and associated subdirectory to be dropped as a group into the plugins folder. Don't require a plethora of prerequisites forcing your users to become experts and compiling and installing Perl modules. Also, bear in mind cross-platform issues whenever possible. Blosxom runs about anywhere. As do all of the modules I've built. It'd be a shame to build something sexy that only runs under Linux when something ever-so-slightly-less-sexy would run just fine everywhere. Writing a Plugin... The best way to understand Blosxom's plugin architecture is to step through each bit in turn. ...Blosxom variables Available to each plugin are key Blosxom variables for their reference (read: customarily read-only). The vast majority appear in Blosxom's
All of these variables live in the main Also passed to each subroutine as appropriate are context-specific variables for reference and alteration (e.g. a reference to a story's body is passed to the ...getting off to a good start Every plugin should start off by identifying itself; a few friendly lines of commentary will do nicely... # Blosxom Plugin: sample # Author(s): Rael Dornfest Your plugin should live in its own namespace so that all its variables and subroutines won't conflict with those of other plugins. In Perl, this is accomplished using a package sample; For this example, I've named the package Each plugin lives not only in its own package, but in its own file. Files are kept in the plugins directory and MUST be named precisely the same name as their package name. So, for this example, the plugin would live in plugins/sample. If you'd like to control the order in which plugins load, prepend the filenames with numbers like: 00loadfirst, 50loadsometime, 99loadlast, and so forth. Plugins are loaded in alphanumeric order, so this will force the ordering nicely for you. Blosxom will strip the numeric bits from the plugin name for you, so refer to the plugin by its package name (e.g. ...configuration, variables, and modules While from here-on out, your Perl-style may run free, I suggest the following conventions so that others might more easily understand, use, and adapt your plugins. As in Blosxom itself, user-configurable variables should be kept together. I suggest that everything from the beginning of the file down through the configurable variables section be considered for the user's information and alteration. Something like: # --- Configurable variables ----- $email_address = 'me@example'; # -------------------------------- Configuration variables should be anything you need the user to define in order to use the plugin properly. Now's a good time to define variables useful to the plugin. I'll define a couple here to be used in just a moment: $title_and_path = ''; $story_number = 1; All of these variables you define (unless my'd) are available to your flavour templates, prefixed with the package name; so, Indeed, one possible use of the plugin architecture is simply to define a set of custom variables you'll be using on a regular basis but which do not come in the Blosxom box. This is also a good place to require any Perl modules you might need for your plugin. Perhaps you'd like access to the wonderful set of functions in the CGI module: use CGI qw/:standard/; ...the subroutines The Blosxom plugin architecture defines a set of hooks, points in Blosxom's execution that it'll give each plugin the chance to act. These hooks -- they're just subroutines -- are, on order of execution: ...the subroutines ...start The sub start { 1; } This lets Blosxom know that it should consider the plugin alive and well and should offer it the ability to act at each upcoming callback point. The start routine is a perfect point to restrict a plugin's operation to dynamic or static rendering, like so: sub start { return $blosxom::static_or_dynamic eq 'dynamic' ? 1 : 0; } The above example declares the plugin active only if Blosxom is currently running dynamically. Otherwise it effectively shuts the plugin down. ...the subroutines ...template Thetemplate subroutine offers the plugin the chance to swap in a replacement for the default template subroutine (the one that brings in your flavour templates).
The template hook should return a reference to an anonymous subroutine to be used in place of the default, as defined and assigned to $template in blosxom.cgi itself.
Given a path, template chunk (e.g. "head" or "foot") and a flavour, The subroutine should return the contents of the template component at hand.
The following example reads template from an alternate "flavour" directory rather than the main data directory, separating flavours from weblog postings:
sub template { return sub { my ($path, $chunk, $flavour) = @_; $path =~ s!^/*!!; $path = "/$path"; my $p = $path; do { return join '', <$fh> if $fh->open("< $flavour_dir/$p/$chunk.$flavour"); } while ($p =~ s/(\/*[^\/]*)$// and $1); return join '', ($blosxom::template{$flavour}{$chunk} || $blosxom::template{error}{$chunk} || ''); }; } If your plugin decides not to override the default after all, simply return an undefined value, like so: sub template { # for some reason determine that you don't want to override the default... return undef; } Subsequent plugins will then be given the chance to override the default template subroutine. ...the subroutines ...entries The The The subroutine should return references to a hash of files and another of indexes to be built (in the case of static rendering). The following simplistic (read: silly) example populates the %files hash with just one story, $datadir/just/one/story.txt: sub entries { # The entries() sub returns a reference an anonymous subroutine return sub { my(%files, %indexes); # Populate the files list with just one file and its modification time $files{"$blosxom::datadir/just/one/story.txt"} = stat("$blosxom::datadir/just/one/story.txt")->mtime; # When run, the subroutine returns references to the files it found and # indexes to be constructed when building statically return (\%files, \%indexes); }; } A more comprehensive and realistic example may be found in entries_index plugin. If your plugin decides not to override the default after all, simply return an undefined value, like so: sub entries { # for some reason determine that you don't want to override the default... return undef; } Subsequent plugins will then be given the chance to override the default entries subroutine. ...the subroutines ...filter The The subroutine is passed a reference ( sub filter { my($pkg, $files_ref) = @_; 1; } One example of filtering is re-building the list based on the results of a local search -- the Lucene plugin used on my site does this. Here's a silly example, removing all files that don't contain the word "spiffy": sub filter { my($pkg, $files_ref) = @_; foreach ( keys %$files_ref ) { $_ =~ /spiffy/ or delete $files_ref->{$_}; } 1; } Notice that I end my subroutine with a ...the subroutines ...skip Theskip subroutine is called just as Blosxom starts actually generating output. Any plugin can cut short story generation by returning a value of 1 . Of course, the first plugin to return a 1 is the last one called.
The skip routine is useful, for example, if your plugin is to return a redirect for some reason or send a binary stream (e.g. an image) to the browser, and hasn't any reason to bother generating any blog entries.
...the subroutines ...interpolate Theinterpolate subroutine offers the plugin the chance to swap in a replacement for the default interpolation subroutine (the one that replaces variables like $title and $someplugin::somevariable with their associated values).
The first plugin whose interpolate hook returns a reference to an anonymous subroutine to be used in place of the default has that code assigned to $interpolate in blosxom.cgi itself.
The subroutine assigned to $interpolate is called for template components (e.g. head, story, foot), being passed the contents thereof for appropriate interpolation.
The interpolate_conditional plugin, for example, provides all manner of conditional interpolation including: defined, not defined, not equal to, equal to, greater than, less than, and a recursive default (allowing you to use template variables in your stories themselves).
If your plugin decides not to override the default after all, simply return an undefined value, like so: sub interpolate { # for some reason determine that you don't want to override the default... return undef; } Subsequent plugins will then be given the chance to override the default interpolate subroutine. ...the subroutines ...head Bloxsom calls the The subroutine is passed the current working directory (as defined by the path) and a reference to the raw head.flavour source. sub head { my($pkg, $currentdir, $head_ref) = @_; 1; } The This is also a good point to alter those custom non-story-specific template variables. Here, for example, I append the path passed on the URL-line to the title, sandwiched with a stylish " :: " sub head { my($pkg, $currentdir, $head_ref) = @_; $title_and_path = $blosxom::blog_title; $blosxom::path_info and $title_and_path .= " :: /$blosxom::path_info"; 1; } I couldn't have accomplished this by simply having my head.flavour contain: A more non-trivial example is that used in creating the readme plugin used on my site. A readme or readme.flavour file encountered in the current working directory ( ...the subroutines ...sort The The entries hook should return a reference to an anonymous subroutine to be used in place of the default, as defined and assigned to $entries in blosxom.cgi itself. The subroutine should return a list of fully-qualified filenames in whatever order you prefer. The following example sorts entries in chronological (oldest first) order, the polar opposite of the default: sub sort { return sub { my($files_ref) = @_; return sort { $files_ref->{$a} <=> $files_ref->{$b} } keys %$files_ref; }; } If your plugin decides not to override the default after all, simply return an undefined value, like so: sub sort { # for some reason determine that you don't want to override the default... return undef; } Subsequent plugins will then be given the chance to override the default sort subroutine. ...the subroutines ...date Bloxsom calls The subroutine is passed the current directory, a reference to the raw date.flavour source, Unix-style modification time of the latest entry for the day, and some useful date bits derived from the modification time. sub date { my ($pkg, $currentdir, $date_ref, $mtime, @date_bits) = @_; my($dw,$mo,$mo_num,$da,$ti,$yr) = @date_bits; 1; }
sub date { my ($pkg, $date_ref, $mtime, $dw,$mo,$mo_num,$da,$ti,$yr) = @_; # %months defined in the header of the date_translate plugin $$date_ref =~ s/\$mo(?!_num)/$months[$mo_num]/mg; 1; } ...the subroutines ...story Bloxsom calls The subroutine is passed the story's path, filename, reference to the raw story.flavour source, reference to the story title, and reference to the body of the post. sub story { my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) 1; } You can alter the raw story.flavour source, alter the title or body of the post before its pasted into the template, and so forth. As a simplistic example, let's use the sub story { my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) $$title_ref = "#" . $story_number++ . ". " . $$title_ref; 1; } Or, as a better example, here's a simplified (but not by much) version of the TrackBack plugin used on my weblog. It retrieves the number of trackbacks from the standalone TrackBack implementation, storing the results in a $trackback variable. The value is swapped in for $trackbacks::trackback in my story.flavour. $trackback = ''; sub story { my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) # code to glean trackback filename based # upon story path and filename goes here $trackback = qq{<a href="$tb_url?__mode=list&tb_id=$tb_id">trackbacks ($tb_count)</a>}; 1; } ...the subroutines ...foot The flip-side of the The subroutine is passed the current working directory (as defined by the path) and a reference to the raw foot.flavour source. sub foot { my($pkg, $currentdir, $foot_ref) = @_; 1; } The ...the subroutines ...end The sub end { 1; } The subroutine is not passed anything by Blosxom. Here's where you can perform any cleanup or last-minute operations you might find useful. A couple of uses I've found thus far are: pinging weblogs.com if anything's changed since the modification time of a "touch file", and sending mail to a mailing list with all new weblog posts since so-many hours ago. ...the subroutines ...last Thelast subroutine hook is called just before the header is prepended to the output and the whole kit-and-kaboodle returned by the generate routine, either for display by the browser or saving to file (if statically rendering)
The subroutine is not passed anything by Blosxom.
|