My MovableType installation has close to ten patches applied to it to implement functionality I need or fix bugs that are present. Most of my patches can be found here. The patches work great—until the next version of MovableType is released, at which point you have to reapply them, which is tedious and error prone.

A better way of patching would be to pragmatically apply a patch file for a specific fix. The usual way of applying diff patches doesn’t really work, though, since this would require you to make a new patch file based on a new release—i.e. exactly the type of work that I want to avoid. Most MovableType patches consist of directions to go to a particular file, find a section and either replace it or add some code before or after it. This insulates a patch from other changes that may have been made to the file by MT’s authors.

My approach to patching must satisfy the following criteria:

  • A patch file should be human and machine readable and should specify all changes (in multiple files) necessary for a patch. It should contain meta information about the patch, such as patch name, a description, who wrote the patch and a URL for new versions of the patch.
  • The patch file should work for updated versions of MovableType (assuming that the sections being patched have not changed, of course).
  • When the patch is applied, the patch should be documented so make it clear which sections were added and which were removed.
  • The patched file should contain enough information to allow the patch to be unapplied.

Patch File Format

I have the initial format of the patching files specified and I think it’s fairly simple to write a patch file. The following is the patch file that implements the MTElse Fix For Plugins hack:

@@@ NAME: MTElse Fix For Plugins
@@@ VERSION: 1.0
@@@ AUTHOR: Stepan Riha
@@@ URL: http://www.nonplus.net/software/mt/MTElseFixForPlugins.htm
@@@ DESCRIPTION
Enables the MTElse tag to properly work for conditional plugins
@@@ SYNTAX: perl
@@@ FILE: lib/MT/Builder.pm
@@@ START
sub build {
@@@ FIND
            my($tokens, $uncompiled);
@@@ APPEND
            my $tokens_else;
@@@ FIND
                    $tokens = [ grep $_->[0] ne 'Else', @{ $t->[2] } ];
@@@ APPEND
                    $tokens_else = [ grep $_->[0] eq 'Else', @{ $t->[2] } ];
@@@ FIND
                $ctx->stash('tokens', $tokens);
@@@ APPEND
                $ctx->stash('tokens_else', $tokens_else);
@@@ FILE: lib/MT/Template/Context.pm
@@@ START
sub add_conditional_tag {
@@@ FIND
        if ($condition->(@_)) {
            return _hdlr_pass_tokens(@_);
        } else {
            return '';
        }
@@@ REPLACE
        if ($condition->(@_)) {
            return _hdlr_pass_tokens(@_);
        } else {
            my($ctx, $args, $cond) = @_;
            return $ctx->stash('builder')->build($ctx, $ctx->stash('tokens_else'), $cond);
        }
@@@ END

After the patch has been applied, the patched sections will look like this, in Builder.pm:

...
            my($tokens, $uncompiled);
#### PATCH MTElse Fix For Plugins BEGIN INSERT
            my $tokens_else;
#### PATCH MTElse Fix For Plugins END INSERT
...

And like this in Context.pm:

sub add_conditional_tag {
    my $class = shift;
    my($name, $condition) = @_;
    $Global_handlers{$name} = { code => sub {
#### PATCH MTElse Fix For Plugins BEGIN REMOVE
####         if ($condition->(@_)) {
####             return _hdlr_pass_tokens(@_);
####         } else {
####             return '';
####         }
#### PATCH MTElse Fix For Plugins END REMOVE
#### PATCH MTElse Fix For Plugins BEGIN INSERT
        if ($condition->(@_)) {
            return _hdlr_pass_tokens(@_);
        } else {
            my($ctx, $args, $cond) = @_;
            return $ctx->stash('builder')->build($ctx, $ctx->stash('tokens_else'), $cond);
        }
#### PATCH MTElse Fix For Plugins END INSERT
    }, is_container => 1 };
}

Patching Application

I’m envisioning a patching application that can work either as a CGI or off the command line.

The CGI allows for a nice UI, with previews of how a patch modifies the files—I may incorporate this functionality into MT-Medic. However, the CGI approach introduces the difficulties of having to deal with file permissions. It might deal with this by simply telling the user which files to chmod before applying (or unapplying) a patch, if it notices permission conflicts.

The command line tool shouldn’t have permission problems, but most MT users probably don’t have access to the command line—even if they do, most aren’t comfortable with it.

Update:

I got some good feedback (i.e. great idea but why reinvent the diff and patch wheel?) on the MT forum - special thanks to distler. I’ll take a look at what modules are available for implementing this using the diff format and then go that route.

After talking with David and taking a look at the progress of the Plugin Manager, I’ve decided to wait for PM to be released and then to try to incorporate patching functionality into it. It’s the more logical application to deal with that and mt-plugins.org would be the logical repository for contributed patches.