Tags:
create new tag
view all tags
TWiki plugins operate using a sort of crude C2Wiki:ObserverPattern, where unfortunately the coupling between the dispatcher (TWiki::Plugins) and the observer (TWiki::Plugin) is rather too tight for comfort. It's also specific to the concept of plugins being rendering agents, and many authors (including me) have gone way beyond that simplistic model. I believe it's time we fixed this, and moved to a more open architecture.

To understand what I'm driving at, consider the list of events that plugins can currently respond to:

Handler Context
afterAttachmentSaveHandler Save
afterCommonTagsHandler Render
afterEditHandler Edit
afterRenameHandler Rename
afterSaveHandler Save
beforeAttachmentSaveHandler Save
beforeCommonTagsHandler Render
beforeEditHandler Edit
beforeSaveHandler Save
commonTagsHandler Render
earlyInitPlugin god alone knows
initializeUserHandler Login
modifyHeaderHandler HTTP header construction (!)
mergeHandler Save
postRenderingHandler Render
preRenderingHandler Render
redirectCgiQueryHandler All
registrationHandler Register
renderFormFieldForEditHandler Render
renderWikiWordHandler Render

Really quite a limited set. There are a lot more "events" in TWiki that a plugin is interested in, but can't see because no handlers have been defined. Because of their origins as callbacks injected into the rendering process, plugin handlers have a number of unfortunate problems:

  1. They are injected into the rendering process! There is no way (except DIY) to call them asynchronously. This is a pain for, for example, mailers that want to send out a notification on topic change, but don't need to hold up the view process to do so.
  2. They have to be passed, and receive in return, masses of data (the topic and meta). This involves a lot of copying around of data which is often simply not required.
  3. There is an implicit (and quite unclear to most people) linear call order. This is somewhat controllable via INSTALLEDPLUGINS, but the constraints are clumsy. You can't for example, say in the plugin "I don't care when I am called, as long as I am called before SpreadSheetPlugin". this has to be manually defined by the admin.
  4. There is no way for a plugin to trigger an event in another plugin.
  5. Handlers defined in TWiki::Plugins are always called, even when no-one is listening out for them. This is inefficient.
It would be better to move to an cleaner event driven model, following the lead TWiki once had, but has now lost to Joomla, Confluence, and most other CMS and Wiki implementations. Most programmers are familiar with MVC these days, so event handling hold no terrors. Any programmer who has coded HTML or JavaScript is also familiar with the concept.

So how would this work? For example, the common tags handler from CommentPlugin currently looks like this:

sub commonTagsHandler {
    my ( $text, $topic, $web ) = @_;

    require TWiki::Plugins::CommentPlugin::Comment;
    if ($@) {
        TWiki::Func::writeWarning( $@ );
        return 0;
    }

    my $query = TWiki::Func::getCgiQuery();
    return unless( defined( $query ));

    return unless $_[0] =~ m/%COMMENT({.*?})?%/o;

    # SMELL: Nasty, tacky way to find out where we were invoked from
    my $scriptname = $ENV{'SCRIPT_NAME'} || '';
    # SMELL: unreliable
    my $previewing = ($scriptname =~ /\/(preview|gnusave|rdiff)/);
    TWiki::Plugins::CommentPlugin::Comment::prompt( $previewing,
                                                    $_[0], $web, $topic );
}
Under an event dispatcher it would look like this (commonTagsHandler was always a bad name):
sub onRenderTextFeatures {
    my $event = shift;

    require TWiki::Plugins::CommentPlugin::Comment;
    if ($@) {
        TWiki::Func::writeWarning( $@ );
        return 0;
    }

    my $query = $event->{query};
    return unless( defined( $query ));

    return unless $_[0] =~ m/%COMMENT({.*?})?%/o;

    # SMELL: Nasty, tacky way to find out where we were invoked from
    my $scriptname = $ENV{'SCRIPT_NAME'} || '';
    # SMELL: unreliable
    my $previewing = ($scriptname =~ /\/(preview|gnusave|rdiff)/);
    TWiki::Plugins::CommentPlugin::Comment::prompt(
        $previewing,
        $event->{text},
        $event->{web},
        $event->{topic} );
}
But why? I hear you cry. Well, a couple of things. First off, in the core code, the existing call:
    # Plugin Hook
    $this->{plugins}->commonTagsHandler( $text, $theTopic, $theWeb, 0 );
changes to this:
    $this->queueSynchronousEvent( 'renderTextFeatures' );
Secondly, an event model allows us to queue events for later execution as well as dispatch them directly
    $this->queueAsynchronousEvent( 'topicModified' );
The event dispatcher takes responsibility for prioritising and dispatching the events, so that it can handle more complex relationships between plugins (such as "call me before SpreadSheetPlugin"). It can even save events until after the page has already been rendered, or serialise then for later (e.g. a cron job might pick them up and execute them).

Another rationale is the potential for plugins to intercommunicate using events. For example, the ActionTrackerPlugin might want to use the MailerPlugin (aynchronously of course) to notify a user that a new action was just added for them.

TWiki::Func::queueEvent( 'sendMail', user => $user, text => "You have a new action...." );

Questions, comments, observations, better ideas?

-- Contributors: CrawfordCurrie - 05 Mar 2007

Discussion

Hi Crawford, I'm really surprised that this topic hasn't caught more traction since it was written. I've been watching it and waiting for the activity to pick up. Personally, I think you've got a fantastic idea. For me, the true power of TWiki is yet to be unlocked, and that will come when we start to capitalize on the opportunity of MashUps between plugins. And that can't happen until the plugins have a reliable way of passing events back & forth to each other...

-- KeithHelfrich - 08 Mar 2007

+1 to this

-- RafaelAlvarez - 08 Mar 2007

I think you are on the right track.

-- ArthurClemens - 08 Mar 2007

Edit | Attach | Watch | Print version | History: r4 < r3 < r2 < r1 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r4 - 2007-03-08 - ArthurClemens
 
  • Learn about TWiki  
  • Download TWiki
This site is powered by the TWiki collaboration platform Powered by Perl Hosted by OICcam.com Ideas, requests, problems regarding TWiki? Send feedback. Ask community in the support forum.
Copyright © 1999-2026 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.