X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FTransaction_Overlay.pm;h=b179084c888a2226c6b280af3547d2700a348945;hp=54bb326a93287d9cf2fcba59e4c0fbe25c91739a;hb=673b9a458d9138523026963df6fa3b4683e09bae;hpb=945721f48f74d5cfffef7c7cf3a3d6bc2521f5dd diff --git a/rt/lib/RT/Transaction_Overlay.pm b/rt/lib/RT/Transaction_Overlay.pm index 54bb326a9..b179084c8 100644 --- a/rt/lib/RT/Transaction_Overlay.pm +++ b/rt/lib/RT/Transaction_Overlay.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# BEGIN BPS TAGGED BLOCK {{{ # -# Copyright (c) 1996-2003 Jesse Vincent +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC +# # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have @@ -14,13 +20,30 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# # +# CONTRIBUTION SUBMISSION POLICY: # -# END LICENSE BLOCK +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + =head1 NAME RT::Transaction - RT\'s transaction object @@ -48,10 +71,16 @@ ok(require RT::Transaction); =cut + +package RT::Transaction; + use strict; no warnings qw(redefine); +use vars qw( %_BriefDescriptions ); + use RT::Attachments; +use RT::Scrips; # {{{ sub Create @@ -59,8 +88,9 @@ use RT::Attachments; Create a new transaction. -This routine should _never_ be called anything other Than RT::Ticket. It should not be called -from client code. Ever. Not ever. If you do this, we will hunt you down. and break your kneecaps. +This routine should _never_ be called by anything other than RT::Ticket. +It should not be called +from client code. Ever. Not ever. If you do this, we will hunt you down and break your kneecaps. Then the unpleasant stuff will start. TODO: Document what gets passed to this @@ -72,7 +102,6 @@ sub Create { my %args = ( id => undef, TimeTaken => 0, - Ticket => 0, Type => 'undefined', Data => '', Field => undef, @@ -80,24 +109,37 @@ sub Create { NewValue => undef, MIMEObj => undef, ActivateScrips => 1, + CommitScrips => 1, + ObjectType => 'RT::Ticket', + ObjectId => 0, + ReferenceType => undef, + OldReference => undef, + NewReference => undef, @_ ); + $args{ObjectId} ||= $args{Ticket}; + #if we didn't specify a ticket, we need to bail - unless ( $args{'Ticket'} ) { - return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify a ticket id")); + unless ( $args{'ObjectId'} && $args{'ObjectType'}) { + return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify an object type and id")); } #lets create our transaction - my %params = (Ticket => $args{'Ticket'}, + my %params = ( Type => $args{'Type'}, Data => $args{'Data'}, Field => $args{'Field'}, OldValue => $args{'OldValue'}, NewValue => $args{'NewValue'}, - Created => $args{'Created'} + Created => $args{'Created'}, + ObjectType => $args{'ObjectType'}, + ObjectId => $args{'ObjectId'}, + ReferenceType => $args{'ReferenceType'}, + OldReference => $args{'OldReference'}, + NewReference => $args{'NewReference'}, ); # Parameters passed in during an import that we probably don't want to touch, otherwise @@ -107,84 +149,79 @@ sub Create { my $id = $self->SUPER::Create(%params); $self->Load($id); - $self->_Attach( $args{'MIMEObj'} ) - if defined $args{'MIMEObj'}; + $self->_Attach( $args{'MIMEObj'} ) if defined $args{'MIMEObj'}; + #Provide a way to turn off scrips if we need to - if ( $args{'ActivateScrips'} ) { + $RT::Logger->debug('About to think about scrips for transaction #' .$self->Id); + if ( $args{'ActivateScrips'} and $args{'ObjectType'} eq 'RT::Ticket' ) { + $self->{'scrips'} = RT::Scrips->new($RT::SystemUser); + + $RT::Logger->debug('About to prepare scrips for transaction #' .$self->Id); - #We're really going to need a non-acled ticket for the scrips to work - my $TicketAsSystem = RT::Ticket->new($RT::SystemUser); - $TicketAsSystem->Load( $args{'Ticket'} ) - || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}\n"); + $self->{'scrips'}->Prepare( + Stage => 'TransactionCreate', + Type => $args{'Type'}, + Ticket => $args{'ObjectId'}, + Transaction => $self->id, + ); + if ($args{'CommitScrips'} ) { + $RT::Logger->debug('About to commit scrips for transaction #' .$self->Id); + $self->{'scrips'}->Commit(); + } + } - my $TransAsSystem = RT::Transaction->new($RT::SystemUser); - $TransAsSystem->Load( $self->id ) - || $RT::Logger->err( - "$self couldn't load a copy of itself as superuser\n"); - # {{{ Deal with Scrips + return ( $id, $self->loc("Transaction Created") ); +} - use RT::Scrips; - my $PossibleScrips = RT::Scrips->new($RT::SystemUser); +# }}} - $PossibleScrips->LimitToQueue( $TicketAsSystem->QueueObj->Id ) - ; #Limit it to $Ticket->QueueObj->Id - $PossibleScrips->LimitToGlobal() - unless $TicketAsSystem->QueueObj->Disabled; # or to "global" +=head2 Scrips +Returns the Scrips object for this transaction. +This routine is only useful on a freshly created transaction object. +Scrips do not get persisted to the database with transactions. - $PossibleScrips->Limit(FIELD => "Stage", VALUE => "TransactionCreate"); +=cut - my $ConditionsAlias = $PossibleScrips->NewAlias('ScripConditions'); - $PossibleScrips->Join( - ALIAS1 => 'main', - FIELD1 => 'ScripCondition', - ALIAS2 => $ConditionsAlias, - FIELD2 => 'id' - ); +sub Scrips { + my $self = shift; + return($self->{'scrips'}); +} - #We only want things where the scrip applies to this sort of transaction - $PossibleScrips->Limit( - ALIAS => $ConditionsAlias, - FIELD => 'ApplicableTransTypes', - OPERATOR => 'LIKE', - VALUE => $args{'Type'}, - ENTRYAGGREGATOR => 'OR', - ); - # Or where the scrip applies to any transaction - $PossibleScrips->Limit( - ALIAS => $ConditionsAlias, - FIELD => 'ApplicableTransTypes', - OPERATOR => 'LIKE', - VALUE => "Any", - ENTRYAGGREGATOR => 'OR', - ); +# {{{ sub Delete - #Iterate through each script and check it's applicability. +=head2 Delete - while ( my $Scrip = $PossibleScrips->Next() ) { - $Scrip->Apply (TicketObj => $TicketAsSystem, - TransactionObj => $TransAsSystem); - } +Delete this transaction. Currently DOES NOT CHECK ACLS - # }}} +=cut - } +sub Delete { + my $self = shift; - return ( $id, $self->loc("Transaction Created") ); -} -# }}} + $RT::Handle->BeginTransaction(); -# {{{ sub Delete + my $attachments = $self->Attachments; -sub Delete { - my $self = shift; - return ( 0, - $self->loc('Deleting this object could break referential integrity') ); + while (my $attachment = $attachments->Next) { + my ($id, $msg) = $attachment->Delete(); + unless ($id) { + $RT::Handle->Rollback(); + return($id, $self->loc("System Error: [_1]", $msg)); + } + } + my ($id,$msg) = $self->SUPER::Delete(); + unless ($id) { + $RT::Handle->Rollback(); + return($id, $self->loc("System Error: [_1]", $msg)); + } + $RT::Handle->Commit(); + return ($id,$msg); } # }}} @@ -254,7 +291,7 @@ sub Content { if ( $args{'Quote'} ) { # Remove quoted signature. - $content =~ s/\n-- \n(.*)$//s; + $content =~ s/\n-- \n(.*?)$//s; # What's the longest line like? my $max = 0; @@ -389,7 +426,7 @@ sub Attachments { } #if they ain't got rights to see, return an empty object - else { + elsif ($self->__Value('ObjectType') eq "RT::Ticket") { unless ( $self->CurrentUserHasRight('ShowTicket') ) { return ( $self->{'attachments'} ); } @@ -460,15 +497,17 @@ sub Description { my $self = shift; #Check those ACLs - #If it's a comment, we need to be extra special careful - if ( $self->__Value('Type') eq 'Comment' ) { + #If it's a comment or a comment email record, + # we need to be extra special careful + + if ( $self->__Value('Type') =~ /^Comment/ ) { unless ( $self->CurrentUserHasRight('ShowTicketComments') ) { return ( $self->loc("Permission Denied") ); } } #if they ain't got rights to see, don't let em - else { + elsif ($self->__Value('ObjectType') eq "RT::Ticket") { unless ( $self->CurrentUserHasRight('ShowTicket') ) { return ($self->loc("Permission Denied") ); } @@ -494,57 +533,97 @@ Returns a text string which briefly describes this transaction sub BriefDescription { my $self = shift; - - #Check those ACLs - #If it's a comment, we need to be extra special careful - if ( $self->__Value('Type') eq 'Comment' ) { + #If it's a comment or a comment email record, + # we need to be extra special careful + if ( $self->__Value('Type') =~ /^Comment/ ) { unless ( $self->CurrentUserHasRight('ShowTicketComments') ) { return ( $self->loc("Permission Denied") ); } } #if they ain't got rights to see, don't let em - else { + elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) { unless ( $self->CurrentUserHasRight('ShowTicket') ) { return ( $self->loc("Permission Denied") ); } } - my $type = $self->Type; #cache this, rather than calling it 30 times + my $type = $self->Type; #cache this, rather than calling it 30 times - if ( !defined( $type ) ) { + if ( !defined($type) ) { return $self->loc("No transaction type specified"); } + my $obj_type = $self->FriendlyObjectType; + if ( $type eq 'Create' ) { - return ($self->loc("Ticket created")); + return ( $self->loc( "[_1] created", $obj_type ) ); } elsif ( $type =~ /Status/ ) { if ( $self->Field eq 'Status' ) { if ( $self->NewValue eq 'deleted' ) { - return ($self->loc("Ticket deleted")); + return ( $self->loc( "[_1] deleted", $obj_type ) ); } else { - return ( $self->loc("Status changed from [_1] to [_2]", $self->loc($self->OldValue), $self->loc($self->NewValue) )); + return ( + $self->loc( + "Status changed from [_1] to [_2]", + "'" . $self->loc( $self->OldValue ) . "'", + "'" . $self->loc( $self->NewValue ) . "'" + ) + ); } } # Generic: - my $no_value = $self->loc("(no value)"); - return ( $self->loc( "[_1] changed from [_2] to [_3]", $self->Field , ( $self->OldValue || $no_value ) , $self->NewValue )); - } - - if ( $type eq 'Correspond' ) { - return $self->loc("Correspondence added"); + my $no_value = $self->loc("(no value)"); + return ( + $self->loc( + "[_1] changed from [_2] to [_3]", + $self->Field, + ( $self->OldValue ? "'" . $self->OldValue . "'" : $no_value ), + "'" . $self->NewValue . "'" + ) + ); } - elsif ( $type eq 'Comment' ) { - return $self->loc("Comments added"); + if ( my $code = $_BriefDescriptions{$type} ) { + return $code->($self); } - elsif ( $type eq 'CustomField' ) { + return $self->loc( + "Default: [_1]/[_2] changed from [_3] to [_4]", + $type, + $self->Field, + ( + $self->OldValue + ? "'" . $self->OldValue . "'" + : $self->loc("(no value)") + ), + "'" . $self->NewValue . "'" + ); +} +%_BriefDescriptions = ( + CommentEmailRecord => sub { + my $self = shift; + return $self->loc("Outgoing email about a comment recorded"); + }, + EmailRecord => sub { + my $self = shift; + return $self->loc("Outgoing email recorded"); + }, + Correspond => sub { + my $self = shift; + return $self->loc("Correspondence added"); + }, + Comment => sub { + my $self = shift; + return $self->loc("Comments added"); + }, + CustomField => sub { + my $self = shift; my $field = $self->loc('CustomField'); if ( $self->Field ) { @@ -563,111 +642,134 @@ sub BriefDescription { else { return $self->loc("[_1] [_2] changed to [_3]", $field, $self->OldValue, $self->NewValue ); } - } - - elsif ( $type eq 'Untake' ) { + }, + Untake => sub { + my $self = shift; return $self->loc("Untaken"); - } - - elsif ( $type eq "Take" ) { + }, + Take => sub { + my $self = shift; return $self->loc("Taken"); - } - - elsif ( $type eq "Force" ) { + }, + Force => sub { + my $self = shift; my $Old = RT::User->new( $self->CurrentUser ); $Old->Load( $self->OldValue ); my $New = RT::User->new( $self->CurrentUser ); $New->Load( $self->NewValue ); return $self->loc("Owner forcibly changed from [_1] to [_2]" , $Old->Name , $New->Name); - } - elsif ( $type eq "Steal" ) { + }, + Steal => sub { + my $self = shift; my $Old = RT::User->new( $self->CurrentUser ); $Old->Load( $self->OldValue ); - return $self->loc("Stolen from [_1] ", $Old->Name); - } - - elsif ( $type eq "Give" ) { + return $self->loc("Stolen from [_1]", $Old->Name); + }, + Give => sub { + my $self = shift; my $New = RT::User->new( $self->CurrentUser ); $New->Load( $self->NewValue ); return $self->loc( "Given to [_1]", $New->Name ); - } - - elsif ( $type eq 'AddWatcher' ) { + }, + AddWatcher => sub { + my $self = shift; my $principal = RT::Principal->new($self->CurrentUser); $principal->Load($self->NewValue); return $self->loc( "[_1] [_2] added", $self->Field, $principal->Object->Name); - } - - elsif ( $type eq 'DelWatcher' ) { + }, + DelWatcher => sub { + my $self = shift; my $principal = RT::Principal->new($self->CurrentUser); $principal->Load($self->OldValue); return $self->loc( "[_1] [_2] deleted", $self->Field, $principal->Object->Name); - } - - elsif ( $type eq 'Subject' ) { + }, + Subject => sub { + my $self = shift; return $self->loc( "Subject changed to [_1]", $self->Data ); - } + }, + AddLink => sub { + my $self = shift; + my $value; + if ( $self->NewValue ) { + my $URI = RT::URI->new( $self->CurrentUser ); + $URI->FromURI( $self->NewValue ); + if ( $URI->Resolver ) { + $value = $URI->Resolver->AsString; + } + else { + $value = $self->NewValue; + } + if ( $self->Field eq 'DependsOn' ) { + return $self->loc( "Dependency on [_1] added", $value ); + } + elsif ( $self->Field eq 'DependedOnBy' ) { + return $self->loc( "Dependency by [_1] added", $value ); - elsif ( $type eq 'AddLink' ) { + } + elsif ( $self->Field eq 'RefersTo' ) { + return $self->loc( "Reference to [_1] added", $value ); + } + elsif ( $self->Field eq 'ReferredToBy' ) { + return $self->loc( "Reference by [_1] added", $value ); + } + elsif ( $self->Field eq 'MemberOf' ) { + return $self->loc( "Membership in [_1] added", $value ); + } + elsif ( $self->Field eq 'HasMember' ) { + return $self->loc( "Member [_1] added", $value ); + } + elsif ( $self->Field eq 'MergedInto' ) { + return $self->loc( "Merged into [_1]", $value ); + } + } + else { + return ( $self->Data ); + } + }, + DeleteLink => sub { + my $self = shift; my $value; - if ($self->NewValue) { - my $URI = RT::URI->new($self->CurrentUser); - $URI->FromURI($self->NewValue); - if ($URI->Resolver) { - $value = $URI->Resolver->AsString; - } else { - $value = $self->NewValue; - } - } - if ($self->Field eq 'DependsOn') { - return $self->loc("Dependency on [_1] added",$value); - } elsif ($self->Field eq 'DependedOnBy') { - return $self->loc("Dependency by [_1] added",$value); - - } elsif ($self->Field eq 'RefersTo') { - return $self->loc("Reference to [_1] added",$value); - } elsif ($self->Field eq 'ReferredToBy') { - return $self->loc("Reference by [_1] added",$value); - } elsif ($self->Field eq 'MemberOf') { - return $self->loc("Membership in [_1] added",$value); - } elsif ($self->Field eq 'HasMember') { - return $self->loc("Member [_1] added",$value); - } else { - return ( $self->Data ); - } - } - elsif ( $type eq 'DeleteLink' ) { - my $value; - if ($self->OldValue) { - my $URI = RT::URI->new($self->CurrentUser); - $URI->FromURI($self->OldValue); - if ($URI->Resolver) { - $value = $URI->Resolver->AsString; - } else { - $value = $self->OldValue; - } - } - - if ($self->Field eq 'DependsOn') { - return $self->loc("Dependency on [_1] deleted",$value); - } elsif ($self->Field eq 'DependedOnBy') { - return $self->loc("Dependency by [_1] deleted",$value); - - } elsif ($self->Field eq 'RefersTo') { - return $self->loc("Reference to [_1] deleted",$value); - } elsif ($self->Field eq 'ReferredToBy') { - return $self->loc("Reference by [_1] deleted",$value); - } elsif ($self->Field eq 'MemberOf') { - return $self->loc("Membership in [_1] deleted",$value); - } elsif ($self->Field eq 'HasMember') { - return $self->loc("Member [_1] deleted",$value); - } else { - return ( $self->Data ); - } - } - elsif ( $type eq 'Set' ) { - if ( $self->Field eq 'Queue' ) { + if ( $self->OldValue ) { + my $URI = RT::URI->new( $self->CurrentUser ); + $URI->FromURI( $self->OldValue ); + if ( $URI->Resolver ) { + $value = $URI->Resolver->AsString; + } + else { + $value = $self->OldValue; + } + + if ( $self->Field eq 'DependsOn' ) { + return $self->loc( "Dependency on [_1] deleted", $value ); + } + elsif ( $self->Field eq 'DependedOnBy' ) { + return $self->loc( "Dependency by [_1] deleted", $value ); + + } + elsif ( $self->Field eq 'RefersTo' ) { + return $self->loc( "Reference to [_1] deleted", $value ); + } + elsif ( $self->Field eq 'ReferredToBy' ) { + return $self->loc( "Reference by [_1] deleted", $value ); + } + elsif ( $self->Field eq 'MemberOf' ) { + return $self->loc( "Membership in [_1] deleted", $value ); + } + elsif ( $self->Field eq 'HasMember' ) { + return $self->loc( "Member [_1] deleted", $value ); + } + } + else { + return ( $self->Data ); + } + }, + Set => sub { + my $self = shift; + if ( $self->Field eq 'Password' ) { + return $self->loc('Password changed'); + } + elsif ( $self->Field eq 'Queue' ) { my $q1 = new RT::Queue( $self->CurrentUser ); $q1->Load( $self->OldValue ); my $q2 = new RT::Queue( $self->CurrentUser ); @@ -676,25 +778,22 @@ sub BriefDescription { } # Write the date/time change at local time: - elsif ($self->Field =~ /Due|Starts|Started|Told/) { - my $t1 = new RT::Date($self->CurrentUser); - $t1->Set(Format => 'ISO', Value => $self->NewValue); - my $t2 = new RT::Date($self->CurrentUser); - $t2->Set(Format => 'ISO', Value => $self->OldValue); - return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString ); - } + elsif ($self->Field =~ /Due|Starts|Started|Told/) { + my $t1 = new RT::Date($self->CurrentUser); + $t1->Set(Format => 'ISO', Value => $self->NewValue); + my $t2 = new RT::Date($self->CurrentUser); + $t2->Set(Format => 'ISO', Value => $self->OldValue); + return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString ); + } else { - return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $self->OldValue, $self->NewValue ); + return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" ); } - } - elsif ( $type eq 'PurgeTransaction' ) { + }, + PurgeTransaction => sub { + my $self = shift; return $self->loc("Transaction [_1] purged", $self->Data); - } - else { - return $self->loc( "Default: [_1]/[_2] changed from [_3] to [_4]", $type, $self->Field, $self->OldValue, $self->NewValue ); - - } -} + }, +); # }}} @@ -711,6 +810,7 @@ Returns false otherwise sub IsInbound { my $self = shift; + $self->ObjectType eq 'RT::Ticket' or return undef; return ( $self->TicketObj->IsRequestor( $self->CreatorObj->PrincipalId ) ); } @@ -718,23 +818,11 @@ sub IsInbound { # }}} -sub _ClassAccessible { +sub _OverlayAccessible { { - id => { read => 1, type => 'int(11)', default => '' }, - EffectiveTicket => - { read => 1, write => 1, type => 'int(11)', default => '' }, - Ticket => - { read => 1, public => 1, type => 'int(11)', default => '' }, - TimeTaken => { read => 1, type => 'int(11)', default => '' }, - Type => { read => 1, type => 'varchar(20)', default => '' }, - Field => { read => 1, type => 'varchar(40)', default => '' }, - OldValue => { read => 1, type => 'varchar(255)', default => '' }, - NewValue => { read => 1, type => 'varchar(255)', default => '' }, - Data => { read => 1, type => 'varchar(100)', default => '' }, - Creator => { read => 1, auto => 1, type => 'int(11)', default => '' }, - Created => - { read => 1, auto => 1, type => 'datetime', default => '' }, + ObjectType => { public => 1}, + ObjectId => { public => 1}, } }; @@ -778,9 +866,29 @@ sub _Value { return (undef); } } + elsif ( $self->__Value('Type') eq 'CommentEmailRecord' ) { + unless ( $self->CurrentUserHasRight('ShowTicketComments') + && $self->CurrentUserHasRight('ShowOutgoingEmail') ) { + return (undef); + } + + } + elsif ( $self->__Value('Type') eq 'EmailRecord' ) { + unless ( $self->CurrentUserHasRight('ShowOutgoingEmail')) { + return (undef); + } + + } + # Make sure the user can see the custom field before showing that it changed + elsif ( ( $self->__Value('Type') eq 'CustomField' ) && $self->__Value('Field') ) { + my $cf = RT::CustomField->new( $self->CurrentUser ); + $cf->Load( $self->__Value('Field') ); + return (undef) unless ( $cf->CurrentUserHasRight('SeeCustomField') ); + } + #if they ain't got rights to see, don't let em - else { + elsif ($self->__Value('ObjectType') eq "RT::Ticket") { unless ( $self->CurrentUserHasRight('ShowTicket') ) { return (undef); } @@ -814,4 +922,156 @@ sub CurrentUserHasRight { # }}} +sub Ticket { + my $self = shift; + return $self->ObjectId; +} + +sub TicketObj { + my $self = shift; + return $self->Object; +} + +sub OldValue { + my $self = shift; + if (my $type = $self->__Value('ReferenceType')) { + my $Object = $type->new($self->CurrentUser); + $Object->Load($self->__Value('OldReference')); + return $Object->Content; + } + else { + return $self->__Value('OldValue'); + } +} + +sub NewValue { + my $self = shift; + if (my $type = $self->__Value('ReferenceType')) { + my $Object = $type->new($self->CurrentUser); + $Object->Load($self->__Value('NewReference')); + return $Object->Content; + } + else { + return $self->__Value('NewValue'); + } +} + +sub Object { + my $self = shift; + my $Object = $self->__Value('ObjectType')->new($self->CurrentUser); + $Object->Load($self->__Value('ObjectId')); + return($Object); +} + +sub FriendlyObjectType { + my $self = shift; + my $type = $self->ObjectType or return undef; + $type =~ s/^RT:://; + return $self->loc($type); +} + +=head2 UpdateCustomFields + + Takes a hash of + + CustomField-<> => Value + or + + Object-RT::Transaction-CustomField-<> => Value parameters to update + this transaction's custom fields + +=cut + +sub UpdateCustomFields { + my $self = shift; + my %args = (@_); + + # This method used to have an API that took a hash of a single + # value "ARGSRef", which was a reference to a hash of arguments. + # This was insane. The next few lines of code preserve that API + # while giving us something saner. + + + # TODO: 3.6: DEPRECATE OLD API + + my $args; + + if ($args{'ARGSRef'}) { + $args = $args{ARGSRef}; + } else { + $args = \%args; + } + + foreach my $arg ( keys %$args ) { + next + unless ( $arg =~ + /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ ); + next if $arg =~ /-Magic$/; + my $cfid = $1; + my $values = $args->{$arg}; + foreach + my $value ( UNIVERSAL::isa( $values, 'ARRAY' ) ? @$values : $values ) + { + next unless length($value); + $self->_AddCustomFieldValue( + Field => $cfid, + Value => $value, + RecordTransaction => 0, + ); + } + } +} + + + +=head2 CustomFieldValues + + Do name => id mapping (if needed) before falling back to RT::Record's CustomFieldValues + + See L + +=cut + +sub CustomFieldValues { + my $self = shift; + my $field = shift; + + if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) { + + unless ( $field =~ /^\d+$/o ) { + my $CFs = RT::CustomFields->new( $self->CurrentUser ); + $CFs->Limit( FIELD => 'Name', VALUE => $field); + $CFs->LimitToLookupType($self->CustomFieldLookupType); + $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id); + $field = $CFs->First->id if $CFs->First; + } + } + return $self->SUPER::CustomFieldValues($field); +} + +# }}} + +# {{{ sub CustomFieldLookupType + +=head2 CustomFieldLookupType + +Returns the RT::Transaction lookup type, which can +be passed to RT::CustomField->Create() via the 'LookupType' hash key. + +=cut + +# }}} + +sub CustomFieldLookupType { + "RT::Queue-RT::Ticket-RT::Transaction"; +} + +# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets. +sub _CacheConfig { + { + 'cache_p' => 1, + 'fast_update_p' => 1, + 'cache_for_sec' => 6000, + } +} 1;