Hi list,

This email doesn't ask a question. Instead, I'll describe the solution to a
problem, just in case anyone else finds it useful.

=Problem=

How do you use an event module to compute and store a value in a dynamic
field, with the purpose of showing it in customer UI, in the grid where the
list of tickets is shown?

A real world example would be: How do you show a new column holding the
moment when the latest article visible to the customer was added to the
ticket?

=Solution=

The aforementioned grid is shown in a location identified in OTRS lingo as
the CustomerTicketOverview view. This grid supports some customization via
SysConfig: you can show two additional columns, Owner and Queue, and you
can also show an arbitrary number of ticket dynamic fields. Thus we'll need
to make use of a ticket dynamic field for our new column, since we want to
avoid changing OTRS's original source files to keep future upgrades as
smooth as possible.

Now all we need is a way to compute the data for the new column, which I'll
start referring to as "Last Updated". The solution is to write an Event
Module, which is an extensibility mechanism provided by OTRS, so it's
likely that it will function even across future OTRS versions even though
it was tested on OTRS 3.2.3. The event module is a Perl source file, that
we'll need to write, containing a class that is executed by OTRS whenever
events that we'll specify are triggered. OTRS will provide our code with
information related to the event context and so we'll have everything
required to compute data for "Last Updated".

Our event module will look at each article as it's added and determine if
it'll be visible to the customer. If so, it will update the corresponding
ticket's dynamic field for "Last Updated" to match the creation date of
this article.

==Steps Towards Solution==

1. Create, via admin UI, a ticket dynamic field of type DateTime. I'll
assume its name is DtTmLastChangeForCustUI but you can use anything else.

2. Configure, via SysConfig, the CustomerTicketOverview view to show
DtTmLastChangeForCustUI, by adding its name to
Ticket::Frontend::CustomerTicketOverview###DynamicField.

3. Create an event module to compute the values for
DtTmLastChangeForCustUI. Because I have no idea how to add attachments that
will also be available in the mailing list archives, I'll provide the code
for this event module embedded in this email. You'll need to copy and paste
the code in a source file named
OtrsInstallDir\Custom\Kernel\System\Ticket\Event\LastChangeForCustomerUI.pm.
The path for this source file is important. The provided code contains at
least one small bug: it uses the date when the article was last changed
instead of the date when it was created. Due to the code being designed to
work only on ArticleCreate, there are no bad effects from this bug.

4. Hook up the event module to OTRS. You'll need to copy and paste the code
for LastChangeForCustomerUI.xml from this email to
OtrsInstallDir\Config\Files\LastChangeForCustomerUI.xml. Again, the path
for this file is important.

5. At this point you'll want to logoff from OTRS using an admin account and
logon again. Then navigate to SysConfig and there you should find a new
group in the dropdown on the left, named LastChangeForCustomerUI.

6. Set the content of the TargetedDynamicFieldName key to
DtTmLastChangeForCustUI for the new setting
Ticket::EventModulePost###LastChangeForCustomerUI.

7. At this point you should be all set for the functionality to work but
keep in mind that the field will be empty for already existent tickets so
it will start having meaningful values as tickets are created or articles
are added to older tickets.

My company (www.totalsoft.ro) was behind the development of this small
customization and they've decided it's OK to publish it to the public
domain. So there are no licensing issues tied to this code.

Enjoy,
Bogdan Iosif

Code for LastChangeForCustomerUI.xml:

<?xml version="1.0" encoding="utf-8" ?>
<otrs_config version="1.0" init="Changes">
    <ConfigItem Name="Ticket::EventModulePost###LastChangeForCustomerUI"
Required="0" Valid="1">
        <Description Lang="en">Stores in TargetedDynamicFieldName the
moment of the last ticket change, as it should be reported in the customer
interface. The moment is captured when an article visible in the customer
interface is created. This is why Event must be ArticleCreate.
TargetedDynamicFieldName must be the name of an existent ticket dynamic
field of "Date / Time" type.</Description>
        <Group>LastChangeForCustomerUI</Group>
        <SubGroup>LastChangeForCustomerUI</SubGroup>
        <Setting>
            <Hash>
                <Item
Key="Module">Kernel::System::Ticket::Event::LastChangeForCustomerUI</Item>
                <Item Key="Event">(ArticleCreate)</Item>
                <Item Key="TargetedDynamicFieldName"></Item>
            </Hash>
        </Setting>
    </ConfigItem>
</otrs_config>

Code for LastChangeForCustomerUI.pm:

# --
# Kernel/System/Ticket/Event/LastChangeForCustomerUI.pm
# - Sets a configurable dynamic field to the changed date of the latest
article visible to the customer
# --

package Kernel::System::Ticket::Event::LastChangeForCustomerUI;
use strict;
use warnings;

use Kernel::System::DynamicField;
use Kernel::System::DynamicField::Backend;
use Kernel::System::VariableCheck qw(:all);

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {};
    bless( $Self, $Type );

    # get needed objects
    for (qw(ConfigObject TicketObject LogObject TimeObject UserObject
CustomerUserObject SendmailObject)) {
        $Self->{$_} = $Param{$_} || die "Got no $_!";
    }

    # create additional objects
    $Self->{DynamicFieldObject} = Kernel::System::DynamicField->new(
        EncodeObject => $Param{TicketObject}->{EncodeObject},
        MainObject   => $Param{TicketObject}->{MainObject},
        DBObject     => $Param{TicketObject}->{DBObject},
        %Param,
    );
    $Self->{BackendObject} = Kernel::System::DynamicField::Backend->new(
        EncodeObject => $Param{TicketObject}->{EncodeObject},
        MainObject   => $Param{TicketObject}->{MainObject},
        DBObject     => $Param{TicketObject}->{DBObject},
        TimeObject   => $Param{TicketObject}->{TimeObject},
        %Param,
    );

    return $Self;
}

sub Run {
    my ( $Self, %Param ) = @_;

=Enable only during debug
    $Self->{LogObject}->Log(
        Priority => 'debug',
        Message => 'Run() Dumping %Param:'
    );
    $Self->{LogObject}->Dumper(
        Data => %Param
    );
=cut

    # check needed stuff
    for (qw(Data Event Config UserID)) {
        if ( !$Param{$_} ) {
            $Self->{LogObject}->Log( Priority => 'error', Message => "Need
$_!" );
            return;
        }
    }
    if ( $Param{Event} ne 'ArticleCreate' ) {
        $Self->{LogObject}->Log( Priority => 'error', Message => "Invalid
event module configuration. Invoked on '$Param{Event}' while designed only
for 'ArticleCreate'." );
        return;
    }
    if ( !$Param{Config}->{TargetedDynamicFieldName} ) {
        $Self->{LogObject}->Log( Priority => 'error', Message => "Invalid
event module configuration. Unspecified 'TargetedDynamicFieldName'." );
        return;
    }
    for (qw(TicketID ArticleID)) {
        if ( !$Param{Data}->{$_} ) {
            $Self->{LogObject}->Log( Priority => 'error', Message => "Need
$_ in Data!" );
            return;
        }
    }

    # get dynamic field config
    my $DynamicFieldConfig
        = $Self->{DynamicFieldObject}->DynamicFieldGet(
            Name => $Param{Config}->{TargetedDynamicFieldName}
        );
    if ( $DynamicFieldConfig->{FieldType} ne 'DateTime' ) {
        $Self->{LogObject}->Log( Priority => 'error', Message => "Invalid
event module configuration. '$Param{Config}->{TargetedDynamicFieldName}'
dynamic field type is not 'DateTime'." );
        return;
    }

    my @CustomerArticleTypes = $Self->{TicketObject}->ArticleTypeList( Type
=> 'Customer' );
    my %Article = $Self->{TicketObject}->ArticleGet(
        ArticleID       => $Param{Data}->{ArticleID},
        ArticleType     => \@CustomerArticleTypes,
    );

    if(!%Article) {
=Enable only during debug
        $Self->{LogObject}->Log(
            Priority => 'debug',
            Message => "Skipping LastChangeForCustomerUI update because
article id $Param{Data}->{ArticleID} was not found amongst 'Customer'
articles for ticket id $Param{Data}->{TicketID}."
        );
=cut
        return;
    }

    # set the value
    my $Success = $Self->{BackendObject}->ValueSet(
        DynamicFieldConfig => $DynamicFieldConfig,
        ObjectID           => $Param{Data}->{TicketID},
        Value              => $Article{Changed},
        UserID             => $Param{UserID},
    );

    if ( !$Success ) {
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message =>
                "Can not set value to current time for dynamic field
$DynamicFieldConfig->{Name}!"
        );
    }

    return 1;
}

1;
---------------------------------------------------------------------
OTRS mailing list: otrs - Webpage: http://otrs.org/
Archive: http://lists.otrs.org/pipermail/otrs
To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/otrs

Reply via email to