X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FTransaction.pm;h=5c903e9f1d06bba305ed461e2e4cc79acc3bc87d;hb=refs%2Fheads%2Frt_28256;hp=1f1bab15a791f0cdae68b8c04d16cacb284b088c;hpb=85e677b86fc37c54e6de2b06340351a28f5a5916;p=freeside.git diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm index 1f1bab15a..5c903e9f1 100755 --- a/rt/lib/RT/Transaction.pm +++ b/rt/lib/RT/Transaction.pm @@ -2,7 +2,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -48,7 +48,7 @@ =head1 NAME - RT::Transaction - RT\'s transaction object + RT::Transaction - RT's transaction object =head1 SYNOPSIS @@ -133,12 +133,6 @@ sub Create { return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify an object type and id")); } - - # Set up any custom fields passed at creation. Has to happen - # before scrips. - - $self->UpdateCustomFields(%{ $args{'CustomFields'} }); - #lets create our transaction my %params = ( Type => $args{'Type'}, @@ -169,6 +163,11 @@ sub Create { } } + # Set up any custom fields passed at creation. Has to happen + # before scrips. + + $self->UpdateCustomFields(%{ $args{'CustomFields'} }); + $self->AddAttribute( Name => 'SquelchMailTo', Content => RT::User->CanonicalizeEmailAddress($_) @@ -369,31 +368,105 @@ sub Content { } if ( $args{'Quote'} ) { + $content = $self->ApplyQuoteWrap(content => $content, + cols => $args{'Wrap'} ); - # What's the longest line like? - my $max = 0; - foreach ( split ( /\n/, $content ) ) { - $max = length if length > $max; - } + $content = $self->QuoteHeader . "\n$content\n\n"; + } - if ( $max > $args{'Wrap'}+6 ) { # 76 ) { - require Text::Wrapper; - my $wrapper = Text::Wrapper->new( - columns => $args{'Wrap'}, - body_start => ( $max > 70 * 3 ? ' ' : '' ), - par_start => '' - ); - $content = $wrapper->wrap($content); - } + return ($content); +} + +=head2 QuoteHeader - $content =~ s/^/> /gm; - $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name) - . "\n$content\n\n"; +Returns text prepended to content when transaction is quoted +(see C argument in L). By default returns +localized "On wrote:\n". + +=cut + +sub QuoteHeader { + my $self = shift; + return $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name); +} + +=head2 ApplyQuoteWrap PARAMHASH + +Wrapper to calculate wrap criteria and apply quote wrapping if needed. + +=cut + +sub ApplyQuoteWrap { + my $self = shift; + my %args = @_; + my $content = $args{content}; + + # What's the longest line like? + my $max = 0; + foreach ( split ( /\n/, $args{content} ) ) { + $max = length if length > $max; } - return ($content); + if ( $max > 76 ) { + require Text::Quoted; + require Text::Wrapper; + + my $structure = Text::Quoted::extract($args{content}); + $content = $self->QuoteWrap(content_ref => $structure, + cols => $args{cols}, + max => $max ); + } + + $content =~ s/^/> /gm; # use regex since string might be multi-line + return $content; } +=head2 QuoteWrap PARAMHASH + +Wrap the contents of transactions based on Wrap settings, maintaining +the quote character from the original. + +=cut + +sub QuoteWrap { + my $self = shift; + my %args = @_; + my $ref = $args{content_ref}; + my $final_string; + + if ( ref $ref eq 'ARRAY' ){ + foreach my $array (@$ref){ + $final_string .= $self->QuoteWrap(content_ref => $array, + cols => $args{cols}, + max => $args{max} ); + } + } + elsif ( ref $ref eq 'HASH' ){ + return $ref->{quoter} . "\n" if $ref->{empty}; # Blank line + + my $col = $args{cols} - (length $ref->{quoter}); + my $wrapper = Text::Wrapper->new( columns => $col ); + + # Wrap on individual lines to honor incoming line breaks + # Otherwise deliberate separate lines (like a list or a sig) + # all get combined incorrectly into single paragraphs. + + my @lines = split /\n/, $ref->{text}; + my $wrap = join '', map { $wrapper->wrap($_) } @lines; + my $quoter = $ref->{quoter}; + + # Only add the space if actually quoting + $quoter .= ' ' if length $quoter; + $wrap =~ s/^/$quoter/mg; # use regex since string might be multi-line + + return $wrap; + } + else{ + $RT::Logger->warning("Can't apply quoting with $ref"); + return; + } + return $final_string; +} =head2 Addresses @@ -641,11 +714,14 @@ sub BriefDescription { return ( $self->loc( "[_1] deleted", $obj_type ) ); } else { + my $canon = $self->Object->can("QueueObj") + ? sub { $self->Object->QueueObj->Lifecycle->CanonicalCase(@_) } + : sub { return $_[0] }; return ( $self->loc( "Status changed from [_1] to [_2]", - "'" . $self->loc( $self->OldValue ) . "'", - "'" . $self->loc( $self->NewValue ) . "'" + "'" . $self->loc( $canon->($self->OldValue) ) . "'", + "'" . $self->loc( $canon->($self->NewValue) ) . "'" ) ); @@ -712,8 +788,9 @@ sub BriefDescription { my $self = shift; my $field = $self->loc('CustomField'); + my $cf; if ( $self->Field ) { - my $cf = RT::CustomField->new( $self->CurrentUser ); + $cf = RT::CustomField->new( $self->CurrentUser ); $cf->SetContextObject( $self->Object ); $cf->Load( $self->Field ); $field = $cf->Name(); @@ -723,6 +800,44 @@ sub BriefDescription { my $new = $self->NewValue; my $old = $self->OldValue; + if ( $cf ) { + + if ( $cf->Type eq 'DateTime' ) { + if ($old) { + my $date = RT::Date->new( $self->CurrentUser ); + $date->Set( Format => 'ISO', Value => $old ); + $old = $date->AsString; + } + + if ($new) { + my $date = RT::Date->new( $self->CurrentUser ); + $date->Set( Format => 'ISO', Value => $new ); + $new = $date->AsString; + } + } + elsif ( $cf->Type eq 'Date' ) { + if ($old) { + my $date = RT::Date->new( $self->CurrentUser ); + $date->Set( + Format => 'unknown', + Value => $old, + Timezone => 'UTC', + ); + $old = $date->AsString( Time => 0, Timezone => 'UTC' ); + } + + if ($new) { + my $date = RT::Date->new( $self->CurrentUser ); + $date->Set( + Format => 'unknown', + Value => $new, + Timezone => 'UTC', + ); + $new = $date->AsString( Time => 0, Timezone => 'UTC' ); + } + } + } + if ( !defined($old) || $old eq '' ) { return $self->loc("[_1] [_2] added", $field, $new); } @@ -783,8 +898,7 @@ sub BriefDescription { my $value; if ( $self->NewValue ) { my $URI = RT::URI->new( $self->CurrentUser ); - $URI->FromURI( $self->NewValue ); - if ( $URI->Resolver ) { + if ( $URI->FromURI( $self->NewValue ) ) { $value = $URI->Resolver->AsString; } else { @@ -822,8 +936,7 @@ sub BriefDescription { my $value; if ( $self->OldValue ) { my $URI = RT::URI->new( $self->CurrentUser ); - $URI->FromURI( $self->OldValue ); - if ( $URI->Resolver ) { + if ( $URI->FromURI( $self->OldValue ) ){ $value = $URI->Resolver->AsString; } else { @@ -884,7 +997,7 @@ sub BriefDescription { } # Write the date/time change at local time: - elsif ($self->Field =~ /Due|Starts|Started|Told/) { + elsif ($self->Field =~ /Due|Starts|Started|Told|WillResolve/) { my $t1 = RT::Date->new($self->CurrentUser); $t1->Set(Format => 'ISO', Value => $self->NewValue); my $t2 = RT::Date->new($self->CurrentUser); @@ -927,7 +1040,8 @@ sub BriefDescription { else { return $self->loc( "[_1] changed from [_2] to [_3]", $self->loc($self->Field), - ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" ); + ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")), + ($self->NewValue? "'".$self->NewValue ."'" : $self->loc("(no value)"))); } }, PurgeTransaction => sub { @@ -1073,6 +1187,11 @@ sub CurrentUserCanSee { $cf->Load( $cf_id ); return 0 unless $cf->CurrentUserHasRight('SeeCustomField'); } + + # Transactions that might have changed the ->Object's visibility to + # the current user are marked readable + return 1 if $self->{ _object_is_readable }; + # Defer to the object in question return $self->Object->CurrentUserCanSee("Transaction"); } @@ -1182,37 +1301,31 @@ sub UpdateCustomFields { } } +=head2 LoadCustomFieldByIdentifier - -=head2 CustomFieldValues - - Do name => id mapping (if needed) before falling back to RT::Record's CustomFieldValues - - See L +Finds and returns the custom field of the given name for the +transaction, overriding L to +look for queue-specific CFs before global ones. =cut -sub CustomFieldValues { +sub LoadCustomFieldByIdentifier { my $self = shift; my $field = shift; - if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) { - - # XXX: $field could be undef when we want fetch values for all CFs - # do we want to cover this situation somehow here? - unless ( defined $field && $field =~ /^\d+$/o ) { - my $CFs = RT::CustomFields->new( $self->CurrentUser ); - $CFs->SetContextObject( $self->Object ); - $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); -} + return $self->SUPER::LoadCustomFieldByIdentifier($field) + if ref $field or $field =~ /^\d+$/; + return $self->SUPER::LoadCustomFieldByIdentifier($field) + unless UNIVERSAL::can( $self->Object, 'QueueObj' ); + my $CFs = RT::CustomFields->new( $self->CurrentUser ); + $CFs->SetContextObject( $self->Object ); + $CFs->Limit( FIELD => 'Name', VALUE => $field ); + $CFs->LimitToLookupType($self->CustomFieldLookupType); + $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id); + return $CFs->First || RT::CustomField->new( $self->CurrentUser ); +} =head2 CustomFieldLookupType