import rt 3.2.2
[freeside.git] / rt / lib / RT / Transaction_Overlay.pm
1 # {{{ BEGIN BPS TAGGED BLOCK
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC 
6 #                                          <jesse@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., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27
28 # CONTRIBUTION SUBMISSION POLICY:
29
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
35
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
44
45 # }}} END BPS TAGGED BLOCK
46 =head1 NAME
47
48   RT::Transaction - RT\'s transaction object
49
50 =head1 SYNOPSIS
51
52   use RT::Transaction;
53
54
55 =head1 DESCRIPTION
56
57
58 Each RT::Transaction describes an atomic change to a ticket object 
59 or an update to an RT::Ticket object.
60 It can have arbitrary MIME attachments.
61
62
63 =head1 METHODS
64
65 =begin testing
66
67 ok(require RT::Transaction);
68
69 =end testing
70
71 =cut
72
73 use strict;
74 no warnings qw(redefine);
75
76 use vars qw( %_BriefDescriptions );
77
78 use RT::Attachments;
79 use RT::Scrips;
80
81 # {{{ sub Create 
82
83 =head2 Create
84
85 Create a new transaction.
86
87 This routine should _never_ be called anything other Than RT::Ticket. It should not be called 
88 from client code. Ever. Not ever.  If you do this, we will hunt you down. and break your kneecaps.
89 Then the unpleasant stuff will start.
90
91 TODO: Document what gets passed to this
92
93 =cut
94
95 sub Create {
96     my $self = shift;
97     my %args = (
98         id             => undef,
99         TimeTaken      => 0,
100         Ticket         => 0,
101         Type           => 'undefined',
102         Data           => '',
103         Field          => undef,
104         OldValue       => undef,
105         NewValue       => undef,
106         MIMEObj        => undef,
107         ActivateScrips => 1,
108         CommitScrips => 1,
109         @_
110     );
111
112     #if we didn't specify a ticket, we need to bail
113     unless ( $args{'Ticket'} ) {
114         return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify a ticket id"));
115     }
116
117
118
119     #lets create our transaction
120     my %params = (Ticket    => $args{'Ticket'},
121         Type      => $args{'Type'},
122         Data      => $args{'Data'},
123         Field     => $args{'Field'},
124         OldValue  => $args{'OldValue'},
125         NewValue  => $args{'NewValue'},
126         Created   => $args{'Created'}
127     );
128
129     # Parameters passed in during an import that we probably don't want to touch, otherwise
130     foreach my $attr qw(id Creator Created LastUpdated TimeTaken LastUpdatedBy) {
131         $params{$attr} = $args{$attr} if ($args{$attr});
132     }
133  
134     my $id = $self->SUPER::Create(%params);
135     $self->Load($id);
136     $self->_Attach( $args{'MIMEObj'} ) if defined $args{'MIMEObj'};
137
138
139     #Provide a way to turn off scrips if we need to
140         $RT::Logger->debug('About to think about scrips for transaction' .$self->Id);            
141     if ( $args{'ActivateScrips'} ) {
142        $self->{'scrips'} = RT::Scrips->new($RT::SystemUser);
143
144         $RT::Logger->debug('About to prepare scrips for transaction' .$self->Id);            
145
146         $self->{'scrips'}->Prepare(
147             Stage       => 'TransactionCreate',
148             Type        => $args{'Type'},
149             Ticket      => $args{'Ticket'},
150             Transaction => $self->id,
151         );
152         if ($args{'CommitScrips'} ) {
153             $RT::Logger->debug('About to commit scrips for transaction' .$self->Id);
154             $self->{'scrips'}->Commit();
155         }
156     }
157
158     return ( $id, $self->loc("Transaction Created") );
159 }
160
161 # }}}
162
163 =head2 Scrips
164
165 Returns the Scrips object for this transaction.
166 This routine is only useful on a freshly created transaction object.
167 Scrips do not get persisted to the database with transactions.
168
169
170 =cut
171
172
173 sub Scrips {
174     my $self = shift;
175     return($self->{'scrips'});
176 }
177
178
179 # {{{ sub Delete
180
181 sub Delete {
182     my $self = shift;
183     return ( 0,
184         $self->loc('Deleting this object could break referential integrity') );
185 }
186
187 # }}}
188
189 # {{{ Routines dealing with Attachments
190
191 # {{{ sub Message 
192
193 =head2 Message
194
195   Returns the RT::Attachments Object which contains the "top-level"object
196   attachment for this transaction
197
198 =cut
199
200 sub Message {
201
202     my $self = shift;
203     
204     if ( !defined( $self->{'message'} ) ) {
205
206         $self->{'message'} = new RT::Attachments( $self->CurrentUser );
207         $self->{'message'}->Limit(
208             FIELD => 'TransactionId',
209             VALUE => $self->Id
210         );
211
212         $self->{'message'}->ChildrenOf(0);
213     }
214     return ( $self->{'message'} );
215 }
216
217 # }}}
218
219 # {{{ sub Content
220
221 =head2 Content PARAMHASH
222
223 If this transaction has attached mime objects, returns the first text/plain part.
224 Otherwise, returns undef.
225
226 Takes a paramhash.  If the $args{'Quote'} parameter is set, wraps this message 
227 at $args{'Wrap'}.  $args{'Wrap'} defaults to 70.
228
229
230 =cut
231
232 sub Content {
233     my $self = shift;
234     my %args = (
235         Quote => 0,
236         Wrap  => 70,
237         @_
238     );
239
240     my $content;
241     my $content_obj = $self->ContentObj;
242     if ($content_obj) {
243         $content = $content_obj->Content;
244     }
245
246     # If all else fails, return a message that we couldn't find any content
247     else {
248         $content = $self->loc('This transaction appears to have no content');
249     }
250
251     if ( $args{'Quote'} ) {
252
253         # Remove quoted signature.
254         $content =~ s/\n-- \n(.*?)$//s;
255
256         # What's the longest line like?
257         my $max = 0;
258         foreach ( split ( /\n/, $content ) ) {
259             $max = length if ( length > $max );
260         }
261
262         if ( $max > 76 ) {
263             require Text::Wrapper;
264             my $wrapper = new Text::Wrapper(
265                 columns    => $args{'Wrap'},
266                 body_start => ( $max > 70 * 3 ? '   ' : '' ),
267                 par_start  => ''
268             );
269             $content = $wrapper->wrap($content);
270         }
271
272         $content = '['
273           . $self->CreatorObj->Name() . ' - '
274           . $self->CreatedAsString() . "]:\n\n" . $content . "\n\n";
275         $content =~ s/^/> /gm;
276
277     }
278
279     return ($content);
280 }
281
282 # }}}
283
284 # {{{ ContentObj
285
286 =head2 ContentObj 
287
288 Returns the RT::Attachment object which contains the content for this Transaction
289
290 =cut
291
292
293
294 sub ContentObj {
295
296     my $self = shift;
297
298     # If we don\'t have any content, return undef now.
299     unless ( $self->Attachments->First ) {
300         return (undef);
301     }
302
303     # Get the set of toplevel attachments to this transaction.
304     my $Attachment = $self->Attachments->First();
305
306     # If it's a message or a plain part, just return the
307     # body.
308     if ( $Attachment->ContentType() =~ '^(text/plain$|message/)' ) {
309         return ($Attachment);
310     }
311
312     # If it's a multipart object, first try returning the first
313     # text/plain part.
314
315     elsif ( $Attachment->ContentType() =~ '^multipart/' ) {
316         my $plain_parts = $Attachment->Children();
317         $plain_parts->ContentType( VALUE => 'text/plain' );
318
319         # If we actully found a part, return its content
320         if ( $plain_parts->First && $plain_parts->First->Content ne '' ) {
321             return ( $plain_parts->First );
322         }
323
324         # If that fails, return the  first text/plain or message/ part
325         # which has some content.
326
327         else {
328             my $all_parts = $Attachment->Children();
329             while ( my $part = $all_parts->Next ) {
330                 if (( $part->ContentType() =~ '^(text/plain$|message/)' ) &&  $part->Content()  ) {
331                     return ($part);
332                 }
333             }
334         }
335
336     }
337
338     # We found no content. suck
339     return (undef);
340 }
341
342 # }}}
343
344 # {{{ sub Subject
345
346 =head2 Subject
347
348 If this transaction has attached mime objects, returns the first one's subject
349 Otherwise, returns null
350   
351 =cut
352
353 sub Subject {
354     my $self = shift;
355     if ( $self->Attachments->First ) {
356         return ( $self->Attachments->First->Subject );
357     }
358     else {
359         return (undef);
360     }
361 }
362
363 # }}}
364
365 # {{{ sub Attachments 
366
367 =head2 Attachments
368
369   Returns all the RT::Attachment objects which are attached
370 to this transaction. Takes an optional parameter, which is
371 a ContentType that Attachments should be restricted to.
372
373 =cut
374
375 sub Attachments {
376     my $self = shift;
377
378     unless ( $self->{'attachments'} ) {
379         $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
380
381         #If it's a comment, return an empty object if they don't have the right to see it
382         if ( $self->Type eq 'Comment' ) {
383             unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
384                 return ( $self->{'attachments'} );
385             }
386         }
387
388         #if they ain't got rights to see, return an empty object
389         else {
390             unless ( $self->CurrentUserHasRight('ShowTicket') ) {
391                 return ( $self->{'attachments'} );
392             }
393         }
394
395         $self->{'attachments'}->Limit( FIELD => 'TransactionId',
396                                        VALUE => $self->Id );
397
398         # Get the self->{'attachments'} in the order they're put into
399         # the database.  Arguably, we should be returning a tree
400         # of self->{'attachments'}, not a set...but no current app seems to need
401         # it.
402
403         $self->{'attachments'}->OrderBy( ALIAS => 'main',
404                                          FIELD => 'id',
405                                          ORDER => 'asc' );
406
407     }
408     return ( $self->{'attachments'} );
409
410 }
411
412 # }}}
413
414 # {{{ sub _Attach 
415
416 =head2 _Attach
417
418 A private method used to attach a mime object to this transaction.
419
420 =cut
421
422 sub _Attach {
423     my $self       = shift;
424     my $MIMEObject = shift;
425
426     if ( !defined($MIMEObject) ) {
427         $RT::Logger->error(
428 "$self _Attach: We can't attach a mime object if you don't give us one.\n"
429         );
430         return ( 0, $self->loc("[_1]: no attachment specified", $self) );
431     }
432
433     my $Attachment = new RT::Attachment( $self->CurrentUser );
434     $Attachment->Create(
435         TransactionId => $self->Id,
436         Attachment    => $MIMEObject
437     );
438     return ( $Attachment, $self->loc("Attachment created") );
439
440 }
441
442 # }}}
443
444 # }}}
445
446 # {{{ Routines dealing with Transaction Attributes
447
448 # {{{ sub Description 
449
450 =head2 Description
451
452 Returns a text string which describes this transaction
453
454 =cut
455
456 sub Description {
457     my $self = shift;
458
459     #Check those ACLs
460     #If it's a comment or a comment email record,
461     #  we need to be extra special careful
462
463     if ( $self->__Value('Type') =~ /^Comment/ ) {
464         unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
465             return ( $self->loc("Permission Denied") );
466         }
467     }
468
469     #if they ain't got rights to see, don't let em
470     else {
471         unless ( $self->CurrentUserHasRight('ShowTicket') ) {
472             return ($self->loc("Permission Denied") );
473         }
474     }
475
476     if ( !defined( $self->Type ) ) {
477         return ( $self->loc("No transaction type specified"));
478     }
479
480     return ( $self->loc("[_1] by [_2]",$self->BriefDescription , $self->CreatorObj->Name ));
481 }
482
483 # }}}
484
485 # {{{ sub BriefDescription 
486
487 =head2 BriefDescription
488
489 Returns a text string which briefly describes this transaction
490
491 =cut
492
493 sub BriefDescription {
494     my $self = shift;
495
496
497     #If it's a comment or a comment email record,
498     #  we need to be extra special careful
499     if ( $self->__Value('Type') =~ /^Comment/ ) {
500         unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
501             return ( $self->loc("Permission Denied") );
502         }
503     }
504
505     #if they ain't got rights to see, don't let em
506     else {
507         unless ( $self->CurrentUserHasRight('ShowTicket') ) {
508             return ( $self->loc("Permission Denied") );
509         }
510     }
511
512     my $type = $self->Type; #cache this, rather than calling it 30 times
513
514     if ( !defined( $type ) ) {
515         return $self->loc("No transaction type specified");
516     }
517
518     if ( $type eq 'Create' ) {
519         return ($self->loc("Ticket created"));
520     }
521     elsif ( $type =~ /Status/ ) {
522         if ( $self->Field eq 'Status' ) {
523             if ( $self->NewValue eq 'deleted' ) {
524                 return ($self->loc("Ticket deleted"));
525             }
526             else {
527                 return ( $self->loc("Status changed from [_1] to [_2]", $self->loc($self->OldValue), $self->loc($self->NewValue) ));
528
529             }
530         }
531
532         # Generic:
533        my $no_value = $self->loc("(no value)"); 
534         return ( $self->loc( "[_1] changed from [_2] to [_3]", $self->Field , ( $self->OldValue || $no_value ) ,  $self->NewValue ));
535     }
536
537     if (my $code = $_BriefDescriptions{$type}) {
538         return $code->($self);
539     }
540
541     return $self->loc( "Default: [_1]/[_2] changed from [_3] to [_4]", $type, $self->Field, $self->OldValue, $self->NewValue );
542 }
543
544 %_BriefDescriptions = (
545     CommentEmailRecord => sub {
546         my $self = shift;
547         return $self->loc("Outgoing email about a comment recorded");
548     },
549     EmailRecord => sub {
550         my $self = shift;
551         return $self->loc("Outgoing email recorded");
552     },
553     Correspond => sub {
554         my $self = shift;
555         return $self->loc("Correspondence added");
556     },
557     Comment => sub {
558         my $self = shift;
559         return $self->loc("Comments added");
560     },
561     CustomField => sub {
562         my $self = shift;
563         my $field = $self->loc('CustomField');
564
565         if ( $self->Field ) {
566             my $cf = RT::CustomField->new( $self->CurrentUser );
567             $cf->Load( $self->Field );
568             $field = $cf->Name();
569         }
570
571         if ( $self->OldValue eq '' ) {
572             return ( $self->loc("[_1] [_2] added", $field, $self->NewValue) );
573         }
574         elsif ( $self->NewValue eq '' ) {
575             return ( $self->loc("[_1] [_2] deleted", $field, $self->OldValue) );
576
577         }
578         else {
579             return $self->loc("[_1] [_2] changed to [_3]", $field, $self->OldValue, $self->NewValue );
580         }
581     },
582     Untake => sub {
583         my $self = shift;
584         return $self->loc("Untaken");
585     },
586     Take => sub {
587         my $self = shift;
588         return $self->loc("Taken");
589     },
590     Force => sub {
591         my $self = shift;
592         my $Old = RT::User->new( $self->CurrentUser );
593         $Old->Load( $self->OldValue );
594         my $New = RT::User->new( $self->CurrentUser );
595         $New->Load( $self->NewValue );
596
597         return $self->loc("Owner forcibly changed from [_1] to [_2]" , $Old->Name , $New->Name);
598     },
599     Steal => sub {
600         my $self = shift;
601         my $Old = RT::User->new( $self->CurrentUser );
602         $Old->Load( $self->OldValue );
603         return $self->loc("Stolen from [_1] ",  $Old->Name);
604     },
605     Give => sub {
606         my $self = shift;
607         my $New = RT::User->new( $self->CurrentUser );
608         $New->Load( $self->NewValue );
609         return $self->loc( "Given to [_1]",  $New->Name );
610     },
611     AddWatcher => sub {
612         my $self = shift;
613         my $principal = RT::Principal->new($self->CurrentUser);
614         $principal->Load($self->NewValue);
615         return $self->loc( "[_1] [_2] added", $self->Field, $principal->Object->Name);
616     },
617     DelWatcher => sub {
618         my $self = shift;
619         my $principal = RT::Principal->new($self->CurrentUser);
620         $principal->Load($self->OldValue);
621         return $self->loc( "[_1] [_2] deleted", $self->Field, $principal->Object->Name);
622     },
623     Subject => sub {
624         my $self = shift;
625         return $self->loc( "Subject changed to [_1]", $self->Data );
626     },
627     AddLink => sub {
628         my $self = shift;
629         my $value;
630         if ( $self->NewValue ) {
631             my $URI = RT::URI->new( $self->CurrentUser );
632             $URI->FromURI( $self->NewValue );
633             if ( $URI->Resolver ) {
634                 $value = $URI->Resolver->AsString;
635             }
636             else {
637                 $value = $self->NewValue;
638             }
639             if ( $self->Field eq 'DependsOn' ) {
640                 return $self->loc( "Dependency on [_1] added", $value );
641             }
642             elsif ( $self->Field eq 'DependedOnBy' ) {
643                 return $self->loc( "Dependency by [_1] added", $value );
644
645             }
646             elsif ( $self->Field eq 'RefersTo' ) {
647                 return $self->loc( "Reference to [_1] added", $value );
648             }
649             elsif ( $self->Field eq 'ReferredToBy' ) {
650                 return $self->loc( "Reference by [_1] added", $value );
651             }
652             elsif ( $self->Field eq 'MemberOf' ) {
653                 return $self->loc( "Membership in [_1] added", $value );
654             }
655             elsif ( $self->Field eq 'HasMember' ) {
656                 return $self->loc( "Member [_1] added", $value );
657             }
658             elsif ( $self->Field eq 'MergedInto' ) {
659                 return $self->loc( "Merged into [_1]", $value );
660             }
661         }
662         else {
663             return ( $self->Data );
664         }
665     },
666     DeleteLink => sub {
667         my $self = shift;
668         my $value;
669         if ( $self->OldValue ) {
670             my $URI = RT::URI->new( $self->CurrentUser );
671             $URI->FromURI( $self->OldValue );
672             if ( $URI->Resolver ) {
673                 $value = $URI->Resolver->AsString;
674             }
675             else {
676                 $value = $self->OldValue;
677             }
678
679             if ( $self->Field eq 'DependsOn' ) {
680                 return $self->loc( "Dependency on [_1] deleted", $value );
681             }
682             elsif ( $self->Field eq 'DependedOnBy' ) {
683                 return $self->loc( "Dependency by [_1] deleted", $value );
684
685             }
686             elsif ( $self->Field eq 'RefersTo' ) {
687                 return $self->loc( "Reference to [_1] deleted", $value );
688             }
689             elsif ( $self->Field eq 'ReferredToBy' ) {
690                 return $self->loc( "Reference by [_1] deleted", $value );
691             }
692             elsif ( $self->Field eq 'MemberOf' ) {
693                 return $self->loc( "Membership in [_1] deleted", $value );
694             }
695             elsif ( $self->Field eq 'HasMember' ) {
696                 return $self->loc( "Member [_1] deleted", $value );
697             }
698         }
699         else {
700             return ( $self->Data );
701         }
702     },
703     Set => sub {
704         my $self = shift;
705         if ( $self->Field eq 'Queue' ) {
706             my $q1 = new RT::Queue( $self->CurrentUser );
707             $q1->Load( $self->OldValue );
708             my $q2 = new RT::Queue( $self->CurrentUser );
709             $q2->Load( $self->NewValue );
710             return $self->loc("[_1] changed from [_2] to [_3]", $self->Field , $q1->Name , $q2->Name);
711         }
712
713         # Write the date/time change at local time:
714         elsif ($self->Field =~  /Due|Starts|Started|Told/) {
715             my $t1 = new RT::Date($self->CurrentUser);
716             $t1->Set(Format => 'ISO', Value => $self->NewValue);
717             my $t2 = new RT::Date($self->CurrentUser);
718             $t2->Set(Format => 'ISO', Value => $self->OldValue);
719             return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString );
720         }
721         else {
722             return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $self->OldValue, $self->NewValue );
723         }
724     },
725     PurgeTransaction => sub {
726         my $self = shift;
727         return $self->loc("Transaction [_1] purged", $self->Data);
728     },
729 );
730
731 # }}}
732
733 # {{{ Utility methods
734
735 # {{{ sub IsInbound
736
737 =head2 IsInbound
738
739 Returns true if the creator of the transaction is a requestor of the ticket.
740 Returns false otherwise
741
742 =cut
743
744 sub IsInbound {
745     my $self = shift;
746     return ( $self->TicketObj->IsRequestor( $self->CreatorObj->PrincipalId ) );
747 }
748
749 # }}}
750
751 # }}}
752
753 sub _ClassAccessible {
754     {
755
756         id => { read => 1, type => 'int(11)', default => '' },
757           EffectiveTicket =>
758           { read => 1, write => 1, type => 'int(11)', default => '' },
759           Ticket =>
760           { read => 1, public => 1, type => 'int(11)', default => '' },
761           TimeTaken => { read => 1, type => 'int(11)',      default => '' },
762           Type      => { read => 1, type => 'varchar(20)',  default => '' },
763           Field     => { read => 1, type => 'varchar(40)',  default => '' },
764           OldValue  => { read => 1, type => 'varchar(255)', default => '' },
765           NewValue  => { read => 1, type => 'varchar(255)', default => '' },
766           Data      => { read => 1, type => 'varchar(100)', default => '' },
767           Creator => { read => 1, auto => 1, type => 'int(11)', default => '' },
768           Created =>
769           { read => 1, auto => 1, type => 'datetime', default => '' },
770
771     }
772 };
773
774 # }}}
775
776 # }}}
777
778 # {{{ sub _Set
779
780 sub _Set {
781     my $self = shift;
782     return ( 0, $self->loc('Transactions are immutable') );
783 }
784
785 # }}}
786
787 # {{{ sub _Value 
788
789 =head2 _Value
790
791 Takes the name of a table column.
792 Returns its value as a string, if the user passes an ACL check
793
794 =cut
795
796 sub _Value {
797
798     my $self  = shift;
799     my $field = shift;
800
801     #if the field is public, return it.
802     if ( $self->_Accessible( $field, 'public' ) ) {
803         return ( $self->__Value($field) );
804
805     }
806
807     #If it's a comment, we need to be extra special careful
808     if ( $self->__Value('Type') eq 'Comment' ) {
809         unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
810             return (undef);
811         }
812     }
813     elsif ( $self->__Value('Type') eq 'CommentEmailRecord' ) {
814         unless ( $self->CurrentUserHasRight('ShowTicketComments')
815             && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
816             return (undef);
817         }
818
819     }
820     elsif ( $self->__Value('Type') eq 'EmailRecord' ) {
821         unless ( $self->CurrentUserHasRight('ShowOutgoingEmail')) {
822             return (undef);
823         }
824
825     }
826
827     #if they ain't got rights to see, don't let em
828     else {
829         unless ( $self->CurrentUserHasRight('ShowTicket') ) {
830             return (undef);
831         }
832     }
833
834     return ( $self->__Value($field) );
835
836 }
837
838 # }}}
839
840 # {{{ sub CurrentUserHasRight
841
842 =head2 CurrentUserHasRight RIGHT
843
844 Calls $self->CurrentUser->HasQueueRight for the right passed in here.
845 passed in here.
846
847 =cut
848
849 sub CurrentUserHasRight {
850     my $self  = shift;
851     my $right = shift;
852     return (
853         $self->CurrentUser->HasRight(
854             Right     => "$right",
855             Object => $self->TicketObj
856           )
857     );
858 }
859
860 # }}}
861
862 # Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
863 sub _CacheConfig {
864   {
865      'cache_p'        => 1,
866      'fast_update_p'  => 1,
867      'cache_for_sec'  => 6000,
868   }
869 }
870 1;