display customer agent, class, tag in ticket search, #8784
[freeside.git] / rt / share / html / Elements / RT__Ticket / ColumnMap
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
6 %#                                          <sales@bestpractical.com>
7 %#
8 %# (Except where explicitly superseded by other copyright notices)
9 %#
10 %#
11 %# LICENSE:
12 %#
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
16 %# from www.gnu.org.
17 %#
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 %# General Public License for more details.
22 %#
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 %#
29 %#
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %#
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
37 %#
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
46 %#
47 %# END BPS TAGGED BLOCK }}}
48 <%ARGS>
49 $Name => undef
50 $Attr => undef
51 </%ARGS>
52
53
54 <%ONCE>
55 my $COLUMN_MAP;
56
57 my $LinkCallback = sub {
58     my $method = shift;
59
60     my $mode            = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
61     my $type            = $RT::Ticket::LINKTYPEMAP{$method}{Type};
62     my $other_mode      = ($mode eq "Target" ? "Base" : "Target");
63     my $mode_uri        = $mode.'URI';
64     my $local_type      = 'Local'.$mode;
65
66     return sub {
67         map {
68             \'<a href="',
69             $_->$mode_uri->Resolver->HREF,
70             \'">',
71             ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
72             \'</a><br />',
73         } @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }
74     }
75 };
76
77 $COLUMN_MAP = {
78     Queue => {
79         attribute => 'Queue',
80         title     => 'Queue id', # loc
81         value     => sub { return $_[0]->Queue }
82     },
83     QueueName => {
84         attribute => 'Queue',
85         title     => 'Queue', # loc
86         value     => sub { return $_[0]->QueueObj->Name }
87     },
88     OwnerName => {
89         title     => 'Owner', # loc
90         attribute => 'Owner',
91         value     => sub { return $_[0]->OwnerObj->Name }
92     },
93     Status => {
94         title     => 'Status', # loc
95         attribute => 'Status',
96         value     => sub { return loc($_[0]->Status) }
97     },
98     Subject => {
99         title     => 'Subject', # loc
100         attribute => 'Subject',
101         value     => sub { return $_[0]->Subject || "(" . loc('No subject') . ")" }
102     },
103     ExtendedStatus => {
104         title     => 'Status', # loc
105         attribute => 'Status',
106         value     => sub {
107             my $Ticket = shift;
108
109             if ( my $count = $Ticket->HasUnresolvedDependencies ) {
110                 if (   $Ticket->HasUnresolvedDependencies( Type => 'approval' )
111                     or $Ticket->HasUnresolvedDependencies( Type => 'code' ) )
112                 {
113                     return \'<em>', loc('(pending approval)'), \'</em>';
114                 }
115                 else {
116                     my $Query = "DependedOnBy = " . $Ticket->id;
117                     $Query .= " AND (" . join(" OR ", map { "Status = '$_'" } RT::Queue->ActiveStatusArray) . ")";
118
119                     my $SearchURL = RT->Config->Get('WebPath') . '/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query);
120
121                     return \'<a href="',$SearchURL,\'">', loc('(pending [quant,_1,other ticket])',$count), \'</a>';
122                 }
123             }
124             else {
125                 return loc( $Ticket->Status );
126             }
127
128           }
129     },
130     Priority => {
131         title     => 'Priority', # loc
132         attribute => 'Priority',
133         value     => sub { return $_[0]->Priority }
134     },
135     InitialPriority => {
136         title     => 'InitialPriority', # loc
137         attribute => 'InitialPriority',
138         name      => 'Initial Priority',
139         value     => sub { return $_[0]->InitialPriority }
140     },
141     FinalPriority => {
142         title     => 'FinalPriority', # loc
143         attribute => 'FinalPriority',
144         name      => 'Final Priority',
145         value     => sub { return $_[0]->FinalPriority }
146     },
147     EffectiveId => {
148         title     => 'EffectiveId', # loc
149         attribute => 'EffectiveId',
150         value     => sub { return $_[0]->EffectiveId }
151     },
152     Type => {
153         title     => 'Type', # loc
154         attribute => 'Type',
155         value     => sub { return $_[0]->Type }
156     },
157     TimeWorked => {
158         attribute => 'TimeWorked',
159         title     => 'Time Worked', # loc
160         value     => sub { return $_[0]->TimeWorked }
161     },
162     TimeLeft => {
163         attribute => 'TimeLeft',
164         title     => 'Time Left', # loc
165         value     => sub { return $_[0]->TimeLeftAsString }
166     },
167     TimeEstimated => {
168         attribute => 'TimeEstimated',
169         title     => 'Time Estimated', # loc
170         value     => sub { return $_[0]->TimeEstimated }
171     },
172     Requestors => {
173         title     => 'Requestors', # loc
174         attribute => 'Requestor.EmailAddress',
175         value     => sub { return $_[0]->Requestors->MemberEmailAddressesAsString }
176     },
177     Cc => {
178         title     => 'Cc', # loc
179         attribute => 'Cc.EmailAddress',
180         value     => sub { return $_[0]->Cc->MemberEmailAddressesAsString }
181     },
182     AdminCc => {
183         title     => 'AdminCc', # loc
184         attribute => 'AdminCc.EmailAddress',
185         value     => sub { return $_[0]->AdminCc->MemberEmailAddressesAsString }
186     },
187     StartsRelative => {
188         title     => 'Starts', # loc
189         attribute => 'Starts',
190         value     => sub { return $_[0]->StartsObj->AgeAsString }
191     },
192     StartedRelative => {
193         title     => 'Started', # loc
194         attribute => 'Started',
195         value     => sub { return $_[0]->StartedObj->AgeAsString }
196     },
197     ToldRelative => {
198         title     => 'Told', # loc
199         attribute => 'Told',
200         value     => sub { return $_[0]->ToldObj->AgeAsString }
201     },
202     DueRelative => {
203         title     => 'Due', # loc
204         attribute => 'Due',
205         value     => sub { 
206             my $date = $_[0]->DueObj;
207             # Highlight the date if it was due in the past, and it's still active
208             if ( $date && $date->Unix > 0 && $date->Diff < 0 && $_[0]->QueueObj->IsActiveStatus($_[0]->Status)) {
209                 return (\'<span class="overdue">' , $date->AgeAsString , \'</span>');
210             } else {
211                 return $date->AgeAsString;
212             }
213         }
214     },
215     ResolvedRelative => {
216         title     => 'Resolved', # loc
217         attribute => 'Resolved',
218         value     => sub { return $_[0]->ResolvedObj->AgeAsString }
219     },
220     Starts => {
221         title     => 'Starts', # loc
222         attribute => 'Starts',
223         value     => sub { return $_[0]->StartsObj->AsString }
224     },
225     Started => {
226         title     => 'Started', # loc
227         attribute => 'Started',
228         value     => sub { return $_[0]->StartedObj->AsString }
229     },
230     Told => {
231         title     => 'Told', # loc
232         attribute => 'Told',
233         value     => sub { return $_[0]->ToldObj->AsString }
234     },
235     Due => {
236         title     => 'Due', # loc
237         attribute => 'Due',
238         value     => sub { return $_[0]->DueObj->AsString }
239     },
240     Resolved => {
241         title     => 'Resolved', # loc
242         attribute => 'Resolved',
243         value     => sub { return $_[0]->ResolvedObj->AsString }
244     },
245     UpdateStatus => {
246         title => 'New messages', # loc
247         value => sub {
248             my $txn = $_[0]->SeenUpTo or return $_[0]->loc('No');
249             return \('<a href="'. RT->Config->Get('WebPath') .'/Ticket/Display.html?id='
250                 . $_[0]->id .'#txn-'. $txn->id .'">'),
251                 $_[0]->loc('New'), \'</a>';
252         },
253     },
254     KeyRequestors => {
255         title     => 'Requestors', # loc
256         attribute => 'Requestor.EmailAddress',
257         value => sub {
258             my $t = shift;
259             my @requestors = $t->Requestors->MemberEmailAddresses;
260             for my $email (@requestors)
261             {
262                 my %key = RT::Crypt::GnuPG::GetKeyInfo($email);
263                 if (!defined $key{'info'}) {
264                     $email .= loc(" (no pubkey!)");
265                 }
266                 elsif ($key{'info'}{'TrustLevel'} == 0) {
267                     $email .= loc(" (untrusted!)");
268                 }
269             }
270             return join ', ', @requestors;
271         }
272     },
273     KeyOwnerName => {
274         title     => 'Owner', # loc
275         attribute => 'Owner',
276         value     => sub {
277             my $t = shift;
278             my $name = $t->OwnerObj->Name;
279             my %key = RT::Crypt::GnuPG::GetKeyInfo($t->OwnerObj->EmailAddress);
280             if (!defined $key{'info'}) {
281                 $name .= ' '. loc("(no pubkey!)");
282             }
283             elsif ($key{'info'}{'TrustLevel'} == 0) {
284                 $name .= ' '. loc("(untrusted!)");
285             }
286
287             return $name;
288         }
289     },
290
291     # Everything from LINKTYPEMAP
292     (map {
293         $_ => { value => $LinkCallback->( $_ ) }
294     } keys %RT::Ticket::LINKTYPEMAP),
295
296     '_CLASS' => {
297         value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
298     },
299     '_CHECKBOX' => {
300         attribute => 'checkbox',
301         title => 'Update', # loc
302         align     => 'right',
303         value     => sub { return \('<input type="checkbox" class="checkbox" name="UpdateTicket'.$_[0]->id.'" value="1" checked="checked" />') }
304     },
305
306     Bookmark => {
307         title => ' ',
308         value => sub {
309             my $bookmark = $m->scomp( '/Ticket/Elements/Bookmark', id => $_[0]->id );
310             # the CollectionAsTable/Row template replaces newlines with <br>
311             $bookmark =~ s/\n//g;
312
313             return \$bookmark;
314         },
315     },
316
317     Customer => {
318         title     => 'Customer', #loc
319         attribute => 'Customer.Number', #title/attribute/name... what does it all mean?
320         value     => sub {
321             my $Ticket = shift;
322             my @return = ();
323             foreach my $c (ticket_cust_resolvers($Ticket)) {
324                 push @return, \'<A HREF="', $c->HREF, \'">', 
325                               $c->AsString,
326                               \'</A>',
327                               \'<BR>';
328             }
329             pop @return;
330             @return;
331         },
332     },
333     # For future reference:
334     # hash key = name of the column in the format string 
335     #   (see /Search/Elements/BuildFormatString)
336     # title = displayed name in the table header
337     # attribute = the field to ORDER BY when sorting on this column
338     Agent => {
339         title     => 'Agent',
340         attribute => 'Customer.Agent',
341         value => sub {
342             my $Ticket = shift;
343             my @return = ();
344             foreach my $c (ticket_cust_resolvers($Ticket)) {
345                 push @return, $c->AgentName, \'<BR>';
346             }
347             pop @return;
348             @return;
349         },
350     },
351     CustomerClass => {
352         title     => 'Class',
353         attribute => 'Customer.Class',
354         value     => sub {
355             my $Ticket = shift;
356             my @return = ();
357             foreach my $c (ticket_cust_resolvers($Ticket)) {
358                 push @return, $c->CustomerClass, \'<BR>';
359             }
360             pop @return;
361             @return;
362         },
363     },
364     CustomerTags => {
365         title     => '',
366         attribute => '',
367         value     => sub {
368             my $Ticket = shift;
369             my @return = ();
370             foreach my $c (ticket_cust_resolvers($Ticket)) {
371                 my @tags = sort { $a->{'name'} cmp $b->{'name'} }
372                             $c->CustomerTags;
373                 foreach my $t (@tags) {
374                     push @return, \'<SPAN style="background-color:#',
375                                   $t->{'color'},
376                                   \';">&nbsp;',
377                                   $t->{'name'},
378                                   \'&nbsp;</SPAN>',
379                                   \'&nbsp;'
380                                   ;
381                 }
382                 pop @return;
383                 push @return, \'<BR>';
384             }
385             pop @return;
386             @return;
387         },
388     },
389 };
390
391 sub ticket_cust_resolvers {
392     my $Ticket = shift;
393     my @Customers = @{ $Ticket->Customers->ItemsArrayRef };
394     return map $_->TargetURI->Resolver, @Customers;
395 }
396
397 # if no GPG support, then KeyOwnerName and KeyRequestors fall back to the regular
398 # versions
399 if (RT->Config->Get('GnuPG')->{'Enable'}) {
400     require RT::Crypt::GnuPG;
401 }
402 else {
403     $COLUMN_MAP->{KeyOwnerName} = $COLUMN_MAP->{OwnerName};
404     $COLUMN_MAP->{KeyRequestors} = $COLUMN_MAP->{Requestors};
405 }
406 </%ONCE>
407 <%init>
408 $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
409 # backward compatibility
410 $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap' );
411 return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
412 </%init>