blosxom :: the zen of blogging

about

  • home/about
  • features
  • colophon
  • news
  • mailing list



  • documentation for users

  • overview
  • install
  • configure
  • blog
  • view
  • flavour
  • syndicate
  • plugins
  • plugin registry
  • static
  • faq
  • use cases*
  • documentation for developers

  • overview
  • plugins
  • downloads

  • mac
  • windows*
  • everyone

  • license
  • contributed*
  • powered by

    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-accessible plugins 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 # --- Configurable variables ----- section and documented in the configuration documentation. Here's the complete list:

    • $blog_title
    • $blog_description
    • $blog_language
    • $datadir
    • $url
    • $depth
    • $num_entries
    • $file_extension
    • $default_flavour
    • $plugin_dir
    • @plugins
    • $static_dir
    • $static_password
    • @static_flavours
    • $static_entries
    • $path_info
    • $flavour
    • $static_or_dynamic
    • $output
    • $version

    All of these variables live in the main blosxom namespace, so reference them as $blosxom::datadir, @blosxom::plugins, and the like.

    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 story() subroutine).

    ...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  
    # Version: 0+1i
    # Blosxom Home/Docs/Licensing: http://www.raelity.org/apps/blosxom/
    

    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, like so:

    package sample;
    

    For this example, I've named the package sample.

    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. loadfirst).

    ...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, $title_and_path would be referenced in your template in classic Perl style as $sample::title_and_path.

    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: start, entries, filter, head, sort, date, story, foot, end. The only required subroutine is start.

    ...the subroutines ...start

    The start subroutine is required. Its purpose is to Blosxom know that it has indeed loaded a plugin and should consider it active. Inform Blosxom so by returning a 1 (true), as shown in this simplest of possible examples:

    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

    The template 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 entries subroutine offers the plugin the chance to swap in a replacement for the default entry find subroutine (the one that finds all those weblog postings of yours).

    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 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 filter subroutine offers the plugin the chance to alter the full list of entries Blosxom has found in its data directory.

    The subroutine is passed a reference ($files_ref) to the hash of files. The hash consists of key/value pairs, the keys being the full-path of an entry and the value being its Unix-style modification time (mtime).

    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 1; . Returning a true (1) value when all goes as expected is good form; return a false (0) when problems occur. While Blosxom doesn't halt execution on a 0 or anything that severe, it does report on what happens with each plugin when run statically.

    ...the subroutines ...skip

    The skip 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

    The interpolate 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 head subroutine after reading in the appropriate head.flavour and before swapping in values for template components.

    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 head subroutine offers the plugin the opportunity to alter the raw header source and define or alter any variables before the header is added to the output stream.

    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: $blog_title :: $path_info since the " :: " bit would have shown up even if there was no path specified on the URL-line, making for a silly "raelity bytes ::".

    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 ($currentdir) is read in and appended to the raw header source ($head_ref).

    ...the subroutines ...sort

    The sort subroutine offers the plugin the chance to swap in a replacement for the default sorting subroutine that determines in what order all those weblog postings of yours are displayed, the default being reverse-chronological order (read: newest first).

    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 date for each new day, after reading in the appropriate date.flavour and before swapping in values for template components.

    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; }

    date has the opportunity to alter the raw date template or build an entirely new date. The following bit of code, for instance, translates the english month names into French or Japanese or ...

    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 story for each and every story, after reading in the appropriate story.flavour and before swapping in values for template components.

    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 $story_number variable defined above to number weblog entries...

    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 head subroutine, Bloxsom calls the foot subroutine after reading in the appropriate foot.flavour and before swapping in values for template components.

    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 foot subroutine offers the plugin the opportunity to alter the raw footer source and define or alter any variables before the footer is added to the output stream.

    ...the subroutines ...end

    The end subroutine is called at the last minute, after all output has been processed and sent to the browser and before Blosxom finishes executing.

    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

    The last 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.



    SourceForge.net Logo