[rt-users] RT Scrip

Craig Ringer craig at 2ndquadrant.com
Fri Jul 5 02:25:03 EDT 2013


On 07/02/2013 04:07 AM, Bryon Baker wrote:
> Is there a library of RT-scrips?  If so where can I find it

Personally I think any useful set of scrips should generally be turned
into an extension and published on CPAN.

That's what I've been doing with the ones I've written so far, like
RT::Extension::SMSNotify and RT::Extension::CustomerGroups .

I have another in the pipeline that's more of a collection of generally
useful small scrips, I just need to untangle it from some in-house-only
code.




I'll reproduce my notes on extension creation in case they're useful to
anyone. I really need to convert them to wikia syntax or integrate them
into the current guide. The following uses Redmine wiki markup. Details
of paths are specific to Debian installs of RT.




h1. RT development notes

To test an action without installing it to the DB and hooking it up to a
queue, you can use rt-crontool. Searches can be any TicketSQL, but
sometimes a simple ticket id is useful:

    rt-crontool --transaction last --search RT::Search::FromSQL
--search-arg "(id = 1033)" --action
RT::Action::ConvertRequestorCustomersToGroups

The error messages for compilation and module load errors are not very
informative. If you get errors about I18N or Locale, it' probably
actually your module failing to compile. Test with:


PERL5LIB=/usr/local/share/request-tracker4/lib:/usr/share/request-tracker4/lib
perl -mRT::Action::ConvertRequestorCustomersToGroups

h2. Test script

The following code starts up an RT script you can run arbitrary RT code in.
<pre>

#!/usr/bin/perl

use Modern::Perl;

BEGIN {
        push(@INC, '/usr/share/request-tracker4/lib');
        push(@INC, '/usr/local/share/request-tracker4/lib');
}

use strict;
use lib "/opt/rt3/lib";
use RT;
use RT::Interface::CLI qw( CleanEnv GetCurrentUser );
use RT::ScripCondition;
use RT::Utils2ndQ;

CleanEnv( );
RT::LoadConfig( );
RT::Init( );
my $user = GetCurrentUser( );
unless( $user->Id ) {
        print "No RT user found. Please consult your RT administrator.\n";
        exit 1;
}
say "Current RT::User is in \$user variable. Entering interactive shell.";
</pre>

Good for testing scripts and snippets.

You might find it useful to load this in `re.pl` from Perl's
"`Devel::REPL`
module":http://search.cpan.org/~ether/Devel-REPL-1.003015/lib/Devel/REPL.pm
(in CPAN) so you can interactively work with RT's API. Just point re.pl
to the file, eg:

<pre>
bash$ re.pl --rcfile /path/to/rtcmdline.pl
$_ $user->Id
149
$_ $user->Name
SomeUserName
$_
</pre>

You'll need the full explicit path; re.pl silently ignores files it
can't find and doesn't search the current directory for rcfiles.

I like to alias this to rtrepl in my bashrc:

<pre>
alias rtrepl='re.pl --rcfile ~/rtcmdline.pl'
</pre>

h2. Manually adding actions to the DB

You can use the following code in an RT script like the above test
script to create an action in the DB:

<pre>
$sa->Create(
        Name => 'ConvertRequestorCustomersToGroups',
        Description => 'Convert all customer accounts added as a
requestor into the associated customer group',
        ExecModule => 'ConvertRequestorCustomersToGroups',
        Argument => 'Requestor'
        );
</pre>

Argument is optional, though some actions require it. The meaning varies
from action to action.

h2. Packaging RT extensions

The packaging process for RT extensions is rather underdocumented. Most
of it is standard Perl module stuff, though:

h3. Packaging a Perl module

Create a Makefile.PL according to Perl's
"Module::Install":http://search.cpan.org/~adamk/Module-Install/lib/Module/Install.pod
 or use the example below, which adds use of

<pre>
use inc::Module::Install 0.77;
use Module::Install::AutoManifest;
use Module::Install::ReadmeFromPod;


name 'Example-Module';

all_from      'lib/Example/Module.pm';
requires      'Carp'            => 0;
# add additional dependencies here
test_requires 'Test::More'      => '0.47';

readme_from 'lib/Example/Module.pm';

auto_manifest;

homepage 'https://github.com/yours/module-example-perl';
bugtracker
'https://rt.cpan.org/Dist/Display.html?Status=Active&Queue=Module-Example';
repository 'git://github.com/yours/module-example-perl.git';

WriteAll;
</pre>

Create a MANIFEST.SKIP like:

<pre>
\.git/
\.gitignore
.*\.o
pm_to_blib
blib/
README.GIT
MANIFEST.SKIP
Module-Example-.*
\..*\.swp
</pre>

Make sure your main module's POD begins like this, replacing
Example::Module with your module package and name:

<pre>

package Example::Module;

=pod

=head1 NAME

Example::Module - Sample module with package Example::Module

=head1 SYNOPSIS

... code sample here ...

=head1 DESCRIPTION

... descriptive text ...

=head1 LICENSE

The same as for Perl itself

=cut

use 5.010;
use strict;

use vars qw{$VERSION @ISA};
BEGIN {
        $VERSION = '1.04';
        @ISA     = 'Example::Module';
}

</pre>

Add this to .gitignore:

<pre>
blib/
pm_to_blib
Makefile
Makefile.old
*.tar.gz
inc/
MANIFEST
META.yml
README
Module-Example-*
.*.swp
</pre>

h2. Additions for RT support

To turn the above into an RT extension module, add the following to
Makefile.PL after the 'use' statements and before the 'name' statement,
like:

<pre>
requires 'Module::Install::RTx' => '0.30';
use Module::Install::RTx;
RTx ('RT-Extension-SMSNotify');
requires_rt('4.0.7');
</pre>

This allows you to auto-detect the RT installation and install the
module to it. You can create a file etc/initialdata that sets up
database contents like scrips and scrip actions. This appears to be
undocumented, but examples can be found in RT::Extension::SLA among
others. The initialdata is applied with "make initdb".

h1. Extension mechanisms

See "Customizing":http://requesttracker.wikia.com/wiki/Customizing on
the RT wiki

h2. Scrip Actions and Scrip Conditions

RT's scrips are the most common way to extend RT. They can be run when
certain events occur on tickets or on a timed basis using rt-crontool
and can act on tickets. Actions are somewhat documented and there are
lots of sample modules you can use to learn how it works.

See "Scrips":http://requesttracker.wikia.com/wiki/Scrip and
"Extensions":http://requesttracker.wikia.com/wiki/Extensions .

scrip actions and conditions go in the lib/RT/Actions/ and
lib/RT/Conditions directories in extensions.

Scrip actions and conditions can be added directly as Perl modules in
the local lib directory, but are better added by packaged plugin extensions.

h2. Mail plugins

See the "Customization" section of "the rt-mailgate man
page":http://linux.die.net/man/1/rt-mailgate where it mentions the
@MailPlugins parameter.

MailPlugins appears to be for user authentication. The docs aren't very
clear, but the default Auth::MailFrom plugin is only added to the list
automatically if @MailPlugins isn't defined in the config.

There isn't a plug or hook mechanism for other parts of the mail import;
the <tt>rt-mailgate</tt> program invokes
<tt>RT::Interface::Email::Gateway</tt>, which is a rather monolithic
method. To hook it you have to replace it with an overlay, which means
the whole method is copied.

The lookup of sender address to user is handled by
GetAuthenticationLevel . The comment on this is considerably more useful
than rt-mailgate's docs. Looking at the code we can see:

<pre>
    # Authentication Level
    # -1 - Get out.  this user has been explicitly declined
    # 0 - User may not do anything (Not used at the moment)
    # 1 - Normal user
    # 2 - User is allowed to specify status updates etc. a la
enhanced-mailgate
</pre>

and in the loop over plugins, you can see that the auth level of the
result is the greatest auth level returned by any plugin a plugin
returns -1 to deny this user explicitly.

The user returned is whatever the last plugin executed returned. Each
plugin gets the previous plugin's CurrentUser as its input; it's free to
return this, modify it, or return a different CurrentUser.

The default plugin RT::Interface::Email::Auth::MailFrom does *not* use
the input CurrentUser argument, it presumes it is `undef` and ignores
it. This is raised in RT bug 23089. It can therefore not be used as the
last entry for user lookup since it'll clobber whatever came first. It
can be used BEFORE another plugin that overrides the input user, or you
can just rip the simple user lookup out of it and use your own module
instead of it.

h2. Callbacks

Callbacks allow you to customise the RT web UI's Mason pages. They're
fairly critically under-documented, but there's a "useful article
here":http://www.runpcrun.com/node/272. There's some information on the
"CustomizingWithCallbacks section of the RT
wiki":http://requesttracker.wikia.com/wiki/CustomizingWithCallbacks too.

Callbacks are placed in the html/ directory of extensions.

RT callbacks are written with Perl's Mason templating language. See "the
documentation for mason":http://www.masonbook.com/book/chapter-2.mhtml

You'll also need to read the docs on the <%init> and <%args> blocks.

Callbacks are "Components called with content" in Mason terms, as
covered in the "advanced features
chapter":http://www.masonbook.com/book/chapter-5.mhtml.


You can find available callbacks in the RT sources. Look in the
share/html/ directory; in say Ticket/Create.html you'll find ->callback
invocations like:

<pre>
$m->callback( CallbackName => 'BeforeCreate', ARGSRef => \%ARGS,
skip_create => \$skip_create,
              checks_failure => $checks_failure, results => \@results );
</pre>


Sometimes the logic is a bit hard to trace. For example, ticket creation
starts in the <%init> methods of SelfService/Create.html (unpriv) and
Ticket/Create.html (priv) where, in response to form data submission,
the BeforeCreate callback is invoked. These components then invoke the
corresponding Display.html code in the same directories to do the actual
ticket creation in their <%init> methods.

Both of these appear to go via lib/RT/Interface/Web.pm's CreateTicket
method, but email submission doesn't; it looks like
lib/RT/Interface/Email.pm calls RT::Ticket->new(...) directly from its
Gateway method.



h2. Overlays

Overlays may be used to fully override methods in RT's core modules or
to insert new methods. They're not usually recommended for use in
extensions because there's no good way to handle multiple extensions
overriding the same method.

See
"Overlays":http://requesttracker.wikia.com/wiki/CustomizingWithOverlays
in the RT wiki.

h1. The RT database

The RT DB is notably lacking in foreign keys and documentation. Here are
a few notes. It seems like a good idea to interact with it from Perl
most of the time.

h2. Finding the Privileged group

The system internal group Privileged can be found with:

<pre>
select id  from groups were where type = 'Privileged' and domain =
'SystemInternal';
</pre>

or from within RT to get the RT::Group ($priv) and then get the RT::User
members at all nested levels.

<pre>
my $priv = RT::Group->new( RT::SystemUser );
$priv->LoadSystemInternalGroup('Privileged');
@{$priv->UserMembersObj->ItemsArrayRef}
</pre>

h2. Finding privileged users

<pre>
select u.id, u.name from groups g INNER JOIN groupmembers gm ON (g.id =
gm.groupid) INNER JOIN users u ON (u.id = gm.memberid) where g.type =
'Privileged' and g.domain = 'SystemInternal';
</pre>

You can not use this approach to find group memberships to
non-SystemInternal groups because non-SystemInternal groups can have
other groups as members. You have to use a recursive query or query via
the cachedgroupmembers table. It's fine for the SystemInternal groups
'Everyone', 'Privileged' and 'Unprivileged'.

Don't query the DB directly from RT, use the RT::Group methods. Querying
the DB can mainly be useful for reporting.

>From within RT:

<pre>
my $u = RT::Users->new( RT::SystemUser );
$u->LimitToPrivileged();
@{$u->ItemsArrayRef}
</pre>

to list users you can do something like:

<pre>
foreach my $uid (@{$u->ItemsArrayRef}) {say $uid->Id, ',', $uid->Name,
',', $uid->EmailAddress; }
</pre>

or more usefully:

<pre>
use Text::CSV;
my $csv = Text::CSV->new();
foreach my $uid (@{$u->ItemsArrayRef}) {
    $csv->print(\*STDOUT, [$uid->Id, $uid->Name]);
    say '';
}
</pre>

-- 
 Craig Ringer                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services


More information about the rt-users mailing list