CustomizingWithOverlays

From Request Tracker Wiki
Jump to navigation Jump to search

Introduction

The RT library was designed to allow users and third party vendors to reimplement or modify specific functionality of the RT system easily. This is achieved by using overlays.

Basics

The overlay hierarchy goes as such:

XXX.pm
 |- XXX_Overlay.pm
 |- XXX_Vendor.pm
 |- XXX_Local.pm

The real RT code is in XXX.pm and XXX_Overlay.pm. You can redefine any method or variable in a XXX_Local.pm file and your code will be overlaid on top of the original RT code. See the bottom of lib/RT/Ticket.pm for more information.

But note that you are redefining a method and you have to maintain it across releases.

Wrapping functions

with Hook::LexWrap

Hook::LexWrap is perl module available from the CPAN that allows you to add a hook on any function in a perl module. This is very powerful tool in cooperation with overlays, as you can extend things like in sub-classing without rewriting whole method. You can change arguments of the method, run additional code before or after the method, you can replace the method by condition and many other things you can't do with overlays or the local dir. You can find examples in the RT book, RTIR and several Extensions.

It appears that Hook::LexWrap is no longer the recommended approach, RTIR has stopped doing this, as has several extensions (like MergeUsers), see this thread for a discussion about this: https://forum.bestpractical.com/t/plugins-and--vendor-files/4717/4

with symbol table hackery

With a bit of Perl magic you /can/ wrap subroutines manually:

*_Foo = &Foo;
 *Foo = sub { $RT::Logger->debug("Reversing output of foo"); reverse &_Foo };
 
 

This first expression makes an alias to the original sub. Then second redefines the sub Foo to be our new anonymous subroutine, which does any pre-/post- work we like, and invokes the original to do most of the work. I realize this is a lot of smoke and mirrors but its not all that hard to put into use.

Note: calling &_Load calls _Load with all the same parameters Load was called with, you may override that list the normal way i.e. _Load(param1,param2,etc)

Alternatives to overlays

CustomizingWithLocalDir is not really an alternative, but additional opportunity to make customization clearer and easier for upgrade or move to another server. For example, you can put XXX_Local.pm and XXX_Vendor.pm overlay files in the local tree to keep them segregated from the distribution files.

CustomizingWithPatches may be preferred in some cases.

Example

Imagine a simple example, you want to redefine the order of appearance of Queues everywhere, let's say by name in reverse alphabetical order. This is equivalent to redefine the Queues' _Init method. We could do something like the symbol example above, but it'd be more efficient if the database simply sorted things correctly in the first place.

This can be done by creating a new file named lib/RT/Queues_Local.pm, with:

<pre>
 use strict;
 no warnings qw(redefine);
 package RT::Queues;
 
 sub _Init {
  my $self = shift;
  $self->{'table'} = "Queues";
  $self->{'primary_key'} = "id";
 
  # By default, order by name
  $self->OrderBy( ALIAS => 'main',
                  FIELD => 'Name',
                  ORDER => 'DESC');
 
  return ($self->SUPER::_Init(@_));
 }
 
 1;
</pre>

Another example with funcion of different parameters:

<pre>
#
# Redefinition of certain procedures in lib/RT/Interface/Email.pm.
# Filename is local/lib/RT/Interface/Email_Local.pm.
#

package RT::Interface::Email;
use strict;
use warnings;
no warnings qw(redefine);
use 5.010;

=head3 GetForwardFrom Ticket => undef, Transaction => undef
Resolve the From field to use in forward mail
=cut

sub GetForwardFrom {
    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    my $txn    = $args{Transaction};
    my $ticket = $args{Ticket} || $txn->Object;

    if ( RT->Config->Get('ForwardFromUser') ) {
        return ( $txn || $ticket )->CurrentUser->EmailAddress;
    }
    else {
        ### HACK: use Comment address instead of Correspond address
        return $ticket->QueueObj->CommentAddress || RT->Config->Get('CommentAddress');
        #return $ticket->QueueObj->CorrespondAddress || RT->Config->Get('CorrespondAddress');
    }
   return (SUPER::GetForwardFrom(@_));
}

1;
</pre>

Caveats

When overlaying methods in _Local.pm files it is important to realize that you are not subclassing the base class - you are just replacing the base class's method with your own. For this reason do not do this:

Date_Local.pm:

 sub Set {
  $self->SUPER::Set;
 }

Or you will find yourself in an infinite loop. And yes, I found out this the hard way. -- Stephen Turner

When putting overlay files in the "local" tree, you may find that simply copying the original .pm from the RT tree breaks due to namespace problems, i.e. "function not found" errors for calls in the RT::* namespace. This should be resolvable by adding explicit 'use' statements for the module(s) in question to your local overlay. -- Bill Cole

Other ways to customize RT

Customizing