The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

RT::Extension::TicketAging - allows tickets to be made inaccessable and finally completely deleted

DESCRIPTION

This extension allows closed tickets to be made inaccessable and finally completely deleted as they get older. It adds a new global ticket field, "Age", to track a ticket's movement through the life cycle.

The rt-aging program (see below) is used to move tickets through the life cycle and must be run separately.

When we speak of "age" we are referring to a ticket's "LastUpdated" property.

Default life cycle

The default life cycle is:

Active

Any unresolved ticket or a ticket with unresolved children is considered Active.

Finished

When a ticket and all its children have been resolved it becomes Finished. No further activity is expected on this ticket.

There is otherwise nothing special about a Finished ticket.

Dead

When a ticket and all its children are Finished and it has not been updated for 2 months it becomes Dead. Dead tickets are just like Finished tickets except they do not show up in RTIR's special lookup tools.

Extinct

When a ticket is Dead and has not been updated for 12 months it becomes Extinct. Extinct tickets have their status set to deleted. They won't show up in any searches unless explicitly asked for ('CF.{Age} = "Extinct"').

Destroyed

When a ticket is Extinct and has not been updated for 24 months and if all linked tickets are also Extinct a ticket is Destroyed. Destroyed tickets are no longer available in RT. They are wholely removed from RT using the Shredder. Destroyed tickets are saved in a SQL dump file from which they can be restored.

See RT::Shredder for more information.

Reactivation

When users reopen a ticket (change Status from inactive to active) it becomes Active again.

rt-aging

The real work is done by the rt-aging program installed to your RT local sbin. In general you simply run rt-aging from the command line or periodically from cron as an RT administrator. It will apply the aging rules and alter the tickets as necessary.

See rt-aging for more details.

Aging configuration

Library preloading

Add the line use RT::Extension::TicketAging;

to the bottom of the RT site config, etc/RT_SiteConfig.pm after adding all extension options described below.

This step is required to allow users to search by Extinct age.

ACLs

By default we grant SeeCustomField right to all privileged users.

$TicketAgingFilenameTemplate

This is the filename template used to create the Shredder dump file when tickets are Destroyed. Defaults to the RT::Shredder default. Good candidates:

    Set($TicketAgingFilenameTemplate, 'aging-%t.XXXX.sql');
    # or
    Set($TicketAgingFilenameTemplate, '/var/backups/aging/%t.XXXX.sql');

See the documentation for <RT::Shredder-GetFileName>> for more details.

$TicketAgingMap

THIS IS AN EXPERIMENTAL FEATURE

Administrators may define their own aging behaviors.

The ages available to RT are configured by changing the available values for the Age global custom field. An administrator may remove values to disable pre-configured options. Adding new values activates new age commands but if its not one of the above values you have to configure the conditions and actions for each age.

$TicketAgingMap can be set in your configuration to override or add to the default life cycle. Its easiest to illustrate this with code.

    Set( $TicketAgingMap, {
             AgeName => {
                 Condition => {
                     CallbackPre  => sub { ... },
                     SQL          => sub { ... },
                     CallbackPost => sub { ... },
                     Filter       => sub { ... },
                 },
                 Action => sub { ... }
             }
         });

Arguments

The aging conditions and actions generally have these arguments:

Age

This is the name of the age being processed. For example "Active".

Collection

This is an RT::Tickets object representing all the tickets visible to the $RT::SystemUser. It can be filtered by the Callbacks and the SQL.

Object

An individual RT::Ticket from the ticket Collection.

Fields

AgeName

The name of your Age field. For example, "Active".

Condition

Holds the rules for determining what tickets fall into this age. The rules are run in this order (excuse the pseudo-code):

    CallbackPre
    SQL
    CallbackPost
    foreach $Object ($Collection) {
        Filter
        Action
    }
SQL

This is the TicketSQL to be run to get the tickets of this Age. Generally its a check against LastUpdated and perhaps Status.

    SQL => sub { "LastUpdated < '-2 months'" }

The TicketSQL is run against the Collection.

Called with Age and Collection arguments.

Should return a valid TicketSQL string.

CallbackPre
CallbackPost

These callbacks can be used to alter the Collection before the SQL is run or before the Filter.

Called with Age and Collection arguments.

Must return true on success or a tuple of (false, $error_message) on failure.

For example:

    # Search for deleted tickets
    CallbackPre => sub {
        my %args = @_;
        return $args{Collection}{allow_deleted_search} = 1;
    };
Filter

Each ticket found by the SQL condition in the Collection is iterated over and the Filter is called on each one. This gives an opportunity to cull out individual tickets.

Called with Age, Collection and Object arguments. Object is an individual RT::Ticket from the ticket Collection.

Returns true if the ticket should be included in the age, false if it should be ignored.

    # Filter out tickets whose children are not of the same age.
    Filter => sub {
        my %args = @_;
        my $id = $args{Object}->id;

        my $find_different_children = qq{
              (CF.{Age} IS NULL OR CF.{Age} != $args{Age})
              AND Linked = $id
        };

        my $tickets = RT::Tickets->new( $RT::SystemUser );
        $tickets->{allow_deleted_search} = 1;
        $tickets->FromSQL( $find_different_children );
        return !$tickets->Count;
    }
Action

After filtering, each ticket found in the Collection is iterated over and the Action is called on each one. This is where whatever changes that need to be made to individual tickets when they change age should be done.

Called with Age, Collection and Object arguments.

Like the Callbacks, it returns true on success or a tuple of (false, $error_message) on failure.

    # Mark the ticket as deleted.
    Action => sub {
        my %args = @_;
        my $ticket = $args{Object};

        return $ticket->__Set( Field => "Status", Value => "deleted" );
    }

AUTHOR

Ruslan Zakirov <ruz@bestpractical.com>