Add "next ticket" button in AgentTicketZoom

English! place to talk about development, programming and coding
Post Reply
juanman80
Znuny newbie
Posts: 44
Joined: 11 Nov 2011, 10:30
Znuny Version: 5.0.15

Add "next ticket" button in AgentTicketZoom

Post by juanman80 »

Hello,

We are thinking of adding "next"|"back" buttons in AgentTicketZoom that would go to the next|previous ticket in the last search of the Agent, if the current ticket appears in that search.
Has anyone tried something similar? Anyone has any suggestion as to how to proceed?

Kind regards,
OTRS 5.0.15 on CentOSLinux with MariaDB database connected to an Active Directory for Agents and Customers.
Daniel Obee
Moderator
Posts: 644
Joined: 19 Jun 2007, 17:11
Znuny Version: various
Real Name: Daniel Obée
Location: Berlin

Re: Add "next ticket" button in AgentTicketZoom

Post by Daniel Obee »

The idea is brilliant and I thought about it quite a time. Alas, the whole thing is quite tricky and beyond my skills.

I could imagine one could somehow add a "save to list" button to the search. The list could be saved (or just a part of it if to long) as string to the agent preferences. Adding the buttons shouldn't be that complicated.

Greets
Daniel
juanman80
Znuny newbie
Posts: 44
Joined: 11 Nov 2011, 10:30
Znuny Version: 5.0.15

Re: Add "next ticket" button in AgentTicketZoom

Post by juanman80 »

wow! I think I got it... and I think quite effective...
Bear in mind that I'm working with OTRS 3.0... some changes may be needed for OTRS 3.1.
All these modification should be done in your Custom folder (for the pm files) or in your skin (for the dtl files).

First, AgentTicketSearch transforms the array of results in a hash format and caches this hash.

Code: Select all

## around line 1110, before $Output .= $Self->{LayoutObject}->TicketListShow
            my $ValPre=-1;
            my $ValCur=-1;
            my %SearchResult;
            my @TempSearchResult = @ViewableTicketIDs;
            
            while (@TempSearchResult) {
                my $ValCur = shift(@TempSearchResult);
                if ($ValPre>0){
                    $SearchResult{$ValCur}->{'PRE'}=$ValPre;
                    $SearchResult{$ValPre}->{'POST'}=$ValCur;
                }
                $ValPre=$ValCur;
            }
            
            $Self->{CacheObject}->Set(
                Type  => 'SearchResult',
                Key   => "TicketSearch::" . $Self->{UserID} . "::last-result",
                Value => \%SearchResult,
                TTL   => 3600,
            );
Then, AgentTicketZoom gets this hash and, if the current ticket is in the hash, shows the previous or next buttons:

Code: Select all

## in AgentTicketZoom.pm, in sub MaskAgentZoom, before return output

    if ( $Self->{LastScreenOverview} =~ /AgentTicketSearch/ ){
        my $ViewableTicketIDs = $Self->{CacheObject}->Get(
            Type => 'SearchResult',
            Key  => "TicketSearch::" . $Self->{UserID} . "::last-result",
        );
        if ( $ViewableTicketIDs->{ $Self->{TicketID} } ){
            my $NumberOfNeighbors = scalar keys %{ $ViewableTicketIDs->{ $Self->{TicketID} } };
            
            if ( $ViewableTicketIDs->{ $Self->{TicketID} }->{'PRE'} ){
                $Self->{LayoutObject}->Block(
                    Name => 'PrevTicket',
                    Data => { 
                        PrevTicketID => $ViewableTicketIDs->{ $Self->{TicketID} }->{'PRE'}
                    },
                );
            }
            if ( $NumberOfNeighbors == 2 ){
                $Self->{LayoutObject}->Block(
                    Name => 'PrevNextSpacing',
                );
            }
            if ( $ViewableTicketIDs->{ $Self->{TicketID} }->{'POST'} ){
                $Self->{LayoutObject}->Block(
                    Name => 'NextTicket',
                    Data => { 
                        NextTicketID => $ViewableTicketIDs->{ $Self->{TicketID} }->{'POST'}
                    },
                );
            }
        }
    }

## in AgentTicketZoom.dtl, after <h1 title...>...</h1>
        <div class="AdditionalInformation">
<!-- dtl:block:PrevTicket -->
            <a href="/agente?Action=AgentTicketZoom;TicketID=$Data{"PrevTicketID"}">
                $Text{"Previous Ticket"}
            </a>
<!-- dtl:block:PrevTicket -->
<!-- dtl:block:PrevNextSpacing -->
            <span>&mdash;</span>
<!-- dtl:block:PrevNextSpacing -->
<!-- dtl:block:NextTicket -->
            <a href="/agente?Action=AgentTicketZoom;TicketID=$Data{"NextTicketID"}">
                $Text{"Next Ticket"}
            </a>
<!-- dtl:block:NextTicket -->
        </div>
This way, if you arrive to AgentTicketZoom from AgentTicketSearch, you get one or two buttons to move through the search results. You don't get these buttons if you arrive from Queue View (I find that it may be confussing if you arrive from queue view to a ticket that is present in your last search, because you would get previous|next buttons but they would be from the search results, not from the queue view).

So, please, tell me what you think
OTRS 5.0.15 on CentOSLinux with MariaDB database connected to an Active Directory for Agents and Customers.
medfreeman
Znuny newbie
Posts: 4
Joined: 21 Jun 2014, 21:56
Znuny Version: 3.3.10
Real Name: Mehdi Lahlou
Company: Garagecube

Re: Add "next ticket" button in AgentTicketZoom

Post by medfreeman »

Hi,

thanks for your code, i was looking for something like this.

That works pretty good, except for the placement of the snippet in AgentTicketZoom.pm, i have to put it just after (l.707) :

Code: Select all

$Self->{LayoutObject}->Block(
    Name => 'Header',
    Data => { %Param, %Ticket, %AclAction },
);
I didn't try it in 3.0, but in 3.3.3 the links don't show at all if i place the code just before the return output, didn't have time to find why exactly.

However i'm trying to "standardize" this, i'm wondering if this could be done avoiding completely the "Custom" folder.

I plan on making this work after queue view too, perhaps specifying where agent is coming from in the link text (i.e. search or queue view)

I'll report when i'll have found answers.

Thanks
OTRS 3.3.10 on Debian Wheezy (7.4) with MySQL 5.5.35
medfreeman
Znuny newbie
Posts: 4
Joined: 21 Jun 2014, 21:56
Znuny Version: 3.3.10
Real Name: Mehdi Lahlou
Company: Garagecube

Re: Add "next ticket" button in AgentTicketZoom

Post by medfreeman »

I just rewrote this as a FilterElementPre OutputFilter on AgentTicketZoom, works beautifully.

Works by finding the "Header" block in AgentTicketZoom template, and then appending my template file just after the closing </h1> tag.
That should not change much between releases.

I wonder now if i can override AgentTicketSearch without using the Custom folder..

I'll publish the module here when finished if it interests anyone..
OTRS 3.3.10 on Debian Wheezy (7.4) with MySQL 5.5.35
tgroh
Znuny newbie
Posts: 12
Joined: 20 Apr 2013, 18:28
Znuny Version: 3.2.10
Real Name: Tobias Groh
Company: IGZ mbH

Re: Add "next ticket" button in AgentTicketZoom

Post by tgroh »

Hello madfreemen,

I am pleased that in the forum someone considers this function to be important.

Unfortunately, I have 3.2.9 and have not yet found the right place to import the three codes.

I get no search results displayed at a time.

Please can you describe exactly the places so that I can find it in 3.2.9?

thx
Produktiv: OTRS 3.2.9 + KIX4OTRS 5.1.2 auf Windows Server 2008 SP1
Testsystem: OTRS 3.2.9 + KIX4OTRS 5.1.2 auf Windows Server 2008 SP1
medfreeman
Znuny newbie
Posts: 4
Joined: 21 Jun 2014, 21:56
Znuny Version: 3.3.10
Real Name: Mehdi Lahlou
Company: Garagecube

Re: Add "next ticket" button in AgentTicketZoom

Post by medfreeman »

Hi,

i'll be as verbose as possible !

First you have to make a copy of Kernel/Modules/AgentTicketSearch.pm to Custom/Kernel/Modules/AgentTicketSearch.pm, so you don't modify the file directly.
The next steps change from the original topic, since i have found a way to alter the rendered HTML without actually changing the template, using an OutputFilter (http://otrs.github.io/doc/manual/develo ... tputfilter). This feature is available since otrs 2.4.

in your newly copied AgentTicketSearch.pm, add this snippet (in 3.3.3 it's on line 54) just under all the $Self->{} definitions :

Code: Select all

$Self->{CacheObject}         = Kernel::System::Cache->new( %{$Self} );
to initialize CacheObject

then (in 3.3.3 it's on line ~1045) add, just before $Output .= $Self->{LayoutObject}->TicketListShow( :

Code: Select all

            my $ValPre=-1;
            my $ValCur=-1;
            my %SearchResult;
            my @TempSearchResult = @ViewableTicketIDs;
            
            while (@TempSearchResult) {
                my $ValCur = shift(@TempSearchResult);
                if ($ValPre>0){
                    $SearchResult{$ValCur}->{'PRE'}=$ValPre;
                    $SearchResult{$ValPre}->{'POST'}=$ValCur;
                }
                $ValPre=$ValCur;
            }
            
            #$Self->{LayoutObject}->FatalError( Message => "TicketSearch : " . $Self->{MainObject}->Dump( \%SearchResult ) );
            
            $Self->{CacheObject}->Set(
                Type  => 'SearchResult',
                Key   => "TicketSearch::" . $Self->{UserID} . "::last-result",
                Value => \%SearchResult,
                TTL   => 3600,
            );
You can uncomment the line

Code: Select all

$Self->{LayoutObject}->FatalError( Message => "TicketSearch : " . $Self->{MainObject}->Dump( \%SearchResult ) );
to get an error message showing the array with next and previous tickets numbers.
So you can know if this part of the code works. If yes, continue.

Now create the file Kernel/Output/HTML/OutputFilterAdjacentTicketsZoomView.pm, and put this code inside :

Code: Select all

# --
# Kernel/Output/HTML/OutputFilterAdjacentTicketsZoomView.pm
# --
# $Id: OutputFilterAdjacentTicketsZoomView.pm,v 1.0 2014/06/26 14:00:30 rb Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --

package Kernel::Output::HTML::OutputFilterAdjacentTicketsZoomView;

use strict;
use warnings;

use vars qw($VERSION);
$VERSION = qw($Revision: 1.0 $) [1];

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

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

    # get needed objects
    for my $Object (qw(ParamObject ConfigObject MainObject LogObject LayoutObject EncodeObject)) {
        $Self->{$Object} = $Param{$Object} || die "Got no $Object!";
    }
    
    $Self->{CacheObject} = Kernel::System::Cache->new( %{$Self} );
    $Self->{UserID} = $Param{UserID};
    $Self->{TicketID} = $Param{TicketID};

    return $Self;
}

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

    # check data
    return if !$Param{Data};
    return if ref $Param{Data} ne 'SCALAR';
    return if !${ $Param{Data} };
    return if !$Param{TemplateFile};

    # get allowed template names
    my $ValidTemplates
        = $Self->{ConfigObject}->Get('Frontend::Output::FilterElementPre')->{AdjacentTicketsZoomView}->{Templates};

    # check template name
    return if !$ValidTemplates->{ $Param{TemplateFile} };
    
    if ( $Self->{LayoutObject}->{LastScreenOverview} =~ /AgentTicketSearch/ ){
        my $ViewableTicketIDs = $Self->{CacheObject}->Get(
            Type => 'SearchResult',
            Key  => "TicketSearch::" . $Self->{UserID} . "::last-result",
        );
        if ( $ViewableTicketIDs->{ $Self->{TicketID} } ){
            my $NumberOfNeighbors = scalar keys %{ $ViewableTicketIDs->{ $Self->{TicketID} } };
            
            if ( $ViewableTicketIDs->{ $Self->{TicketID} }->{'PRE'} ){
                $Self->{LayoutObject}->Block(
                    Name => 'PrevTicket',
                    Data => { 
                        PrevTicketID => $ViewableTicketIDs->{ $Self->{TicketID} }->{'PRE'},
                    },
                );
            }
            if ( $NumberOfNeighbors == 2 ){
                $Self->{LayoutObject}->Block(
                    Name => 'PrevNextSpacing',
                );
            }
            if ( $ViewableTicketIDs->{ $Self->{TicketID} }->{'POST'} ){
                $Self->{LayoutObject}->Block(
                    Name => 'NextTicket',
                    Data => { 
                       NextTicketID => $ViewableTicketIDs->{ $Self->{TicketID} }->{'POST'},
                    },
                );
            }
        }
    }
    
    my $StartPattern    = '<!-- [ ] dtl:block:Header [ ] --> .+?';
    
    #$Self->{LayoutObject}->FatalError( Message => "Module Test !" );
    
    # add links to Header block
    if ( ${ $Param{Data} } =~ m{ $StartPattern }ixms ) {
        my $FinishPattern = '</h1>';
        
        my $Replace = $Self->{LayoutObject}->Output(
            TemplateFile => 'AgentTicketZoomAdjacentTickets',
        );
        
        ${ $Param{Data} } =~ s{ ($StartPattern) $FinishPattern }{$1$FinishPattern$Replace}ixms;
        return ${ $Param{Data} };
    }

    return 1;
}

1;
This is an OutputFilter that basically gets the previously saved searched adjacent tickets, then finds the previous and next tickets of the current displayed ticket, and inserts the links in the TicketZoomView (ref. in Kernel/Output/HTML/Standard/AgentTicketZoom.dtl ~l.79 in v3.3) on the fly. It first looks for the

Code: Select all

<!-- dtl:block:Header -->
tag, and inserts the previous and next links just after the closing

Code: Select all

</h1>
tag, using the contents from the template file AgentTicketZoomAdjacentTickets.dtl, that we'll create next.

Now create the file Kernel/Output/HTML/Standard/AgentTicketZoomAdjacentTickets.dtl, with this code in it :

Code: Select all

# --
# AgentTicketZoomAdjacentTickets.dtl - provides HTML for OutputFilterAdjacentTickets.pm
# --
# $Id: AgentTicketZoomAdjacentTickets.dtl,v 1.0 2014/06/26 14:00:30 rb Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
        <div class="AdditionalInformation">
<!-- dtl:block:PrevTicket -->
            <a href="$Env{"Baselink"}Action=AgentTicketZoom;TicketID=$LQData{"PrevTicketID"}">
                $Text{"Previous Ticket"}
            </a>
<!-- dtl:block:PrevTicket -->
<!-- dtl:block:PrevNextSpacing -->
            <span>&mdash;</span>
<!-- dtl:block:PrevNextSpacing -->
<!-- dtl:block:NextTicket -->
            <a href="$Env{"Baselink"}Action=AgentTicketZoom;TicketID=$LQData{"NextTicketID"}">
                $Text{"Next Ticket"}
            </a>
<!-- dtl:block:NextTicket -->
        </div>
It's the actual html code.

Then you have to register the filter in sysconfig, so it is actually applied to the TicketZoomView template.
Create the file Kernel/Config/Files/AgentTicketZoomAdjacentTickets.xml, with this code inside :

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<otrs_config version="1.0" init="Application">
    <ConfigItem Name="Frontend::Output::FilterElementPre###AdjacentTicketsZoomView" Required="0" Valid="1">
        <Description Lang="en">Module to show previous and next tickets in zoomview, coming from search.</Description>
        <Description Lang="fr">Module pour afficher les boutons tickets précédent et suivant dans la vue détaillée du ticket, en provenance de la recherche.</Description>
        <Group>Ticket</Group>
        <SubGroup>Frontend::Agent</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::Output::HTML::OutputFilterAdjacentTicketsZoomView</Item>
                <Item Key="Debug">0</Item>
                <Item Key="Templates">
                    <Hash>
                        <Item Key="AgentTicketZoom">1</Item>
                    </Hash>
                </Item>
            </Hash>
        </Setting>
    </ConfigItem>
</otrs_config>
Feel free to uncomment or add some

Code: Select all

$Self->{LayoutObject}->FatalError( Message => "Module Test !" );
kind of lines in .pm files so you can actually see what happens with the code.

Please ask if you have more questions

Best
OTRS 3.3.10 on Debian Wheezy (7.4) with MySQL 5.5.35
Post Reply