X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FRecord.pm;h=8664e2867dfd0dc98c52fa77144ec245dabbafcd;hb=63a268637b2d51a8766412617724b9436439deb6;hp=b2e1e7110e6fb2377a5ee9282236a0e8b14d932f;hpb=b4b0c7e72d7eaee2fbfc7022022c9698323203dd;p=freeside.git diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm index b2e1e7110..8664e2867 100755 --- a/rt/lib/RT/Record.pm +++ b/rt/lib/RT/Record.pm @@ -1,8 +1,8 @@ # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC +# +# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -45,7 +45,6 @@ # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} - =head1 NAME RT::Record - Base class for RT record objects @@ -56,6 +55,11 @@ =head1 DESCRIPTION +=begin testing + +ok (require RT::Record); + +=end testing =head1 METHODS @@ -66,21 +70,25 @@ package RT::Record; use strict; use warnings; +our @ISA; +use base qw(RT::Base); + use RT::Date; +use RT::I18N; use RT::User; use RT::Attributes; +use DBIx::SearchBuilder::Record::Cachable; use Encode qw(); our $_TABLE_ATTR = { }; -use RT::Base; -my $base = 'DBIx::SearchBuilder::Record::Cachable'; -if ( $RT::Config && $RT::Config->Get('DontCacheSearchBuilderRecords') ) { - $base = 'DBIx::SearchBuilder::Record'; + +if ( $RT::DontCacheSearchBuilderRecords ) { + push (@ISA, 'DBIx::SearchBuilder::Record'); +} else { + push (@ISA, 'DBIx::SearchBuilder::Record::Cachable'); + } -eval "require $base" or die $@; -our @ISA = 'RT::Base'; -push @ISA, $base; # {{{ sub _Init @@ -100,7 +108,10 @@ The primary keys for RT classes is 'id' =cut -sub _PrimaryKeys { return ['id'] } +sub _PrimaryKeys { + my $self = shift; + return ( ['id'] ); +} # }}} @@ -126,6 +137,14 @@ sub Delete { Returns a string which is this object's type. The type is the class, without the "RT::" prefix. +=begin testing + +my $ticket = RT::Ticket->new($RT::SystemUser); +my $group = RT::Group->new($RT::SystemUser); +is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring"); +is($group->ObjectTypeStr, 'Group', "Group returns correct typestring"); + +=end testing =cut @@ -241,7 +260,10 @@ sub FirstAttribute { # {{{ sub _Handle -sub _Handle { return $RT::Handle } +sub _Handle { + my $self = shift; + return ($RT::Handle); +} # }}} @@ -314,6 +336,8 @@ sub Create { } if (UNIVERSAL::isa('errno',$id)) { + exit(0); + warn "It's here!"; return(undef); } @@ -343,29 +367,40 @@ DB is case sensitive sub LoadByCols { my $self = shift; + my %hash = (@_); # We don't want to hang onto this delete $self->{'attributes'}; - return $self->SUPER::LoadByCols( @_ ) unless $self->_Handle->CaseSensitive; - # If this database is case sensitive we need to uncase objects for # explicit loading - my %hash = (@_); - foreach my $key ( keys %hash ) { - - # If we've been passed an empty value, we can't do the lookup. - # We don't need to explicitly downcase integers or an id. - if ( $key ne 'id' && defined $hash{ $key } && $hash{ $key } !~ /^\d+$/ ) { - my ($op, $val, $func); - ($key, $op, $val, $func) = - $self->_Handle->_MakeClauseCaseInsensitive( $key, '=', delete $hash{ $key } ); - $hash{$key}->{operator} = $op; - $hash{$key}->{value} = $val; - $hash{$key}->{function} = $func; + if ( $self->_Handle->CaseSensitive ) { + my %newhash; + foreach my $key ( keys %hash ) { + + # If we've been passed an empty value, we can't do the lookup. + # We don't need to explicitly downcase integers or an id. + if ( $key =~ '^id$' + || !defined( $hash{$key} ) + || $hash{$key} =~ /^\d+$/ + ) + { + $newhash{$key} = $hash{$key}; + } + else { + my ($op, $val, $func); + ($key, $op, $val, $func) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key}); + $newhash{$key}->{operator} = $op; + $newhash{$key}->{value} = $val; + $newhash{$key}->{function} = $func; + } } + + # We've clobbered everything we care about. bash the old hash + # and replace it with the new hash + %hash = %newhash; } - return $self->SUPER::LoadByCols( %hash ); + $self->SUPER::LoadByCols(%hash); } # }}} @@ -494,7 +529,7 @@ sub _Set { $msg = $self->loc( "[_1] changed from [_2] to [_3]", - $self->loc( $args{'Field'} ), + $args{'Field'}, ( $old_val ? "'$old_val'" : $self->loc("(no value)") ), '"' . $self->__Value( $args{'Field'}) . '"' ); @@ -627,20 +662,27 @@ sub SQLType { } + sub __Value { my $self = shift; my $field = shift; - my %args = ( decode_utf8 => 1, @_ ); + my %args = ( decode_utf8 => 1, + @_ ); - unless ( $field ) { - $RT::Logger->error("__Value called with undef field"); + unless (defined $field && $field) { + $RT::Logger->error("$self __Value called with undef field"); } + my $value = $self->SUPER::__Value($field); + + return('') if ( !defined($value) || $value eq ''); - my $value = $self->SUPER::__Value( $field ); if( $args{'decode_utf8'} ) { - return Encode::decode_utf8( $value ) unless Encode::is_utf8( $value ); + # XXX: is_utf8 check should be here unless Encode bug would be fixed + # see http://rt.cpan.org/NoAuth/Bug.html?id=14559 + return Encode::decode_utf8($value) unless Encode::is_utf8($value); } else { - return Encode::encode_utf8( $value ) if Encode::is_utf8( $value ); + # check is_utf8 here just to be shure + return Encode::encode_utf8($value) if Encode::is_utf8($value); } return $value; } @@ -658,7 +700,6 @@ sub _CacheConfig { sub _BuildTableAttributes { my $self = shift; - my $class = ref($self) || $self; my $attributes; if ( UNIVERSAL::can( $self, '_CoreAccessible' ) ) { @@ -670,19 +711,37 @@ sub _BuildTableAttributes { foreach my $column (%$attributes) { foreach my $attr ( %{ $attributes->{$column} } ) { - $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr}; + $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr}; + } + } + if ( UNIVERSAL::can( $self, '_OverlayAccessible' ) ) { + $attributes = $self->_OverlayAccessible(); + + foreach my $column (%$attributes) { + foreach my $attr ( %{ $attributes->{$column} } ) { + $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr}; + } } } - foreach my $method ( qw(_OverlayAccessible _VendorAccessible _LocalAccessible) ) { - next unless UNIVERSAL::can( $self, $method ); - $attributes = $self->$method(); + if ( UNIVERSAL::can( $self, '_VendorAccessible' ) ) { + $attributes = $self->_VendorAccessible(); foreach my $column (%$attributes) { foreach my $attr ( %{ $attributes->{$column} } ) { - $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr}; + $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr}; } } } + if ( UNIVERSAL::can( $self, '_LocalAccessible' ) ) { + $attributes = $self->_LocalAccessible(); + + foreach my $column (%$attributes) { + foreach my $attr ( %{ $attributes->{$column} } ) { + $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr}; + } + } + } + } @@ -695,7 +754,7 @@ DBIx::SearchBuilder::Record sub _ClassAccessible { my $self = shift; - return $_TABLE_ATTR->{ref($self) || $self}; + return $_TABLE_ATTR->{ref($self)}; } =head2 _Accessible COLUMN ATTRIBUTE @@ -728,19 +787,19 @@ sub _EncodeLOB { my $ContentEncoding = 'none'; #get the max attachment length from RT - my $MaxSize = RT->Config->Get('MaxAttachmentSize'); + my $MaxSize = $RT::MaxAttachmentSize; #if the current attachment contains nulls and the #database doesn't support embedded nulls - if ( RT->Config->Get('AlwaysUseBase64') or + if ( $RT::AlwaysUseBase64 or ( !$RT::Handle->BinarySafeBLOBs ) && ( $Body =~ /\x00/ ) ) { # set a flag telling us to mimencode the attachment $ContentEncoding = 'base64'; #cut the max attchment size by 25% (for mime-encoding overhead. - $RT::Logger->debug("Max size is $MaxSize"); + $RT::Logger->debug("Max size is $MaxSize\n"); $MaxSize = $MaxSize * 3 / 4; # Some databases (postgres) can't handle non-utf8 data } elsif ( !$RT::Handle->BinarySafeBLOBs @@ -753,7 +812,7 @@ sub _EncodeLOB { if ( ($MaxSize) and ( $MaxSize < length($Body) ) ) { # if we're supposed to truncate large attachments - if (RT->Config->Get('TruncateLongAttachments')) { + if ($RT::TruncateLongAttachments) { # truncate the attachment to that length. $Body = substr( $Body, 0, $MaxSize ); @@ -761,12 +820,13 @@ sub _EncodeLOB { } # elsif we're supposed to drop large attachments on the floor, - elsif (RT->Config->Get('DropLongAttachments')) { + elsif ($RT::DropLongAttachments) { # drop the attachment on the floor $RT::Logger->info( "$self: Dropped an attachment of size " - . length($Body)); - $RT::Logger->info( "It started: " . substr( $Body, 0, 60 ) ); + . length($Body) . "\n" + . "It started: " . substr( $Body, 0, 60 ) . "\n" + ); return ("none", "Large attachment dropped" ); } } @@ -790,8 +850,8 @@ sub _EncodeLOB { sub _DecodeLOB { my $self = shift; - my $ContentType = shift || ''; - my $ContentEncoding = shift || 'none'; + my $ContentType = shift; + my $ContentEncoding = shift; my $Content = shift; if ( $ContentEncoding eq 'base64' ) { @@ -803,12 +863,14 @@ sub _DecodeLOB { elsif ( $ContentEncoding && $ContentEncoding ne 'none' ) { return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) ); } + if ( RT::I18N::IsTextualContentType($ContentType) ) { $Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content); } return ($Content); } +# {{{ LINKDIRMAP # A helper table for links mapping to make it easier # to build and parse links between tickets @@ -882,18 +944,11 @@ sub Update { # This is in an eval block because $object might not exist. # and might not have a Name method. But "can" won't find autoloaded # items. If it fails, we don't care - do { - no warnings "uninitialized"; - local $@; - eval { - my $object = $attribute . "Obj"; - my $name = $self->$object->Name; - next if $name eq $value || $name eq ($value || 0); - }; - next if $value eq $self->$attribute(); - next if ($value || 0) eq $self->$attribute(); + eval { + my $object = $attribute . "Obj"; + next if ($self->$object->Name eq $value); }; - + next if ( $value eq $self->$attribute() ); my $method = "Set$attribute"; my ( $code, $msg ) = $self->$method($value); my ($prefix) = ref($self) =~ /RT(?:.*)::(\w+)/; @@ -901,17 +956,7 @@ sub Update { # Default to $id, but use name if we can get it. my $label = $self->id; $label = $self->Name if (UNIVERSAL::can($self,'Name')); - # this requires model names to be loc'ed. - -=for loc - - "Ticket" # loc - "User" # loc - "Group" # loc - "Queue" # loc -=cut - - push @results, $self->loc( $prefix ) . " $label: ". $msg; + push @results, $self->loc( "$prefix [_1]", $label ) . ': '. $msg; =for loc @@ -1020,10 +1065,54 @@ sub DependedOnBy { =head2 HasUnresolvedDependencies -Takes a paramhash of Type (default to '__any'). Returns the number of -unresolved dependencies, if $self->UnresolvedDependencies returns an -object with one or more members of that type. Returns false -otherwise. + Takes a paramhash of Type (default to '__any'). Returns true if +$self->UnresolvedDependencies returns an object with one or more members +of that type. Returns false otherwise + + +=begin testing + +my $t1 = RT::Ticket->new($RT::SystemUser); +my ($id, $trans, $msg) = $t1->Create(Subject => 'DepTest1', Queue => 'general'); +ok($id, "Created dep test 1 - $msg"); + +my $t2 = RT::Ticket->new($RT::SystemUser); +my ($id2, $trans, $msg2) = $t2->Create(Subject => 'DepTest2', Queue => 'general'); +ok($id2, "Created dep test 2 - $msg2"); +my $t3 = RT::Ticket->new($RT::SystemUser); +my ($id3, $trans, $msg3) = $t3->Create(Subject => 'DepTest3', Queue => 'general', Type => 'approval'); +ok($id3, "Created dep test 3 - $msg3"); +my ($addid, $addmsg); +ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t2->id)); +ok ($addid, $addmsg); +ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t3->id)); + +ok ($addid, $addmsg); +my $link = RT::Link->new($RT::SystemUser); +my ($rv, $msg) = $link->Load($addid); +ok ($rv, $msg); +ok ($link->LocalTarget == $t3->id, "Link LocalTarget is correct"); +ok ($link->LocalBase == $t1->id, "Link LocalBase is correct"); + +ok ($t1->HasUnresolvedDependencies, "Ticket ".$t1->Id." has unresolved deps"); +ok (!$t1->HasUnresolvedDependencies( Type => 'blah' ), "Ticket ".$t1->Id." has no unresolved blahs"); +ok ($t1->HasUnresolvedDependencies( Type => 'approval' ), "Ticket ".$t1->Id." has unresolved approvals"); +ok (!$t2->HasUnresolvedDependencies, "Ticket ".$t2->Id." has no unresolved deps"); +; + +my ($rid, $rmsg)= $t1->Resolve(); +ok(!$rid, $rmsg); +my ($rid2, $rmsg2) = $t2->Resolve(); +ok ($rid2, $rmsg2); +($rid, $rmsg)= $t1->Resolve(); +ok(!$rid, $rmsg); +my ($rid3,$rmsg3) = $t3->Resolve; +ok ($rid3,$rmsg3); +($rid, $rmsg)= $t1->Resolve(); +ok($rid, $rmsg); + + +=end testing =cut @@ -1046,7 +1135,7 @@ sub HasUnresolvedDependencies { } if ($deps->Count > 0) { - return $deps->Count; + return 1; } else { return (undef); @@ -1095,54 +1184,27 @@ dependency search. sub AllDependedOnBy { my $self = shift; - return $self->_AllLinkedTickets( LinkType => 'DependsOn', - Direction => 'Target', @_ ); -} - -=head2 AllDependsOn - -Returns an array of RT::Ticket objects which this ticket (directly or -indirectly) depends on; takes an optional 'Type' argument in the param -hash, which will limit returned tickets to that type, as well as cause -tickets with that type to serve as 'leaf' nodes that stops the -recursive dependency search. - -=cut - -sub AllDependsOn { - my $self = shift; - return $self->_AllLinkedTickets( LinkType => 'DependsOn', - Direction => 'Base', @_ ); -} - -sub _AllLinkedTickets { - my $self = shift; - + my $dep = $self->DependedOnBy; my %args = ( - LinkType => undef, - Direction => undef, Type => undef, _found => {}, _top => 1, @_ ); - my $dep = $self->_Links( $args{Direction}, $args{LinkType}); while (my $link = $dep->Next()) { - my $uri = $args{Direction} eq 'Target' ? $link->BaseURI : $link->TargetURI; - next unless ($uri->IsLocal()); - my $obj = $args{Direction} eq 'Target' ? $link->BaseObj : $link->TargetObj; - next if $args{_found}{$obj->Id}; + next unless ($link->BaseURI->IsLocal()); + next if $args{_found}{$link->BaseObj->Id}; if (!$args{Type}) { - $args{_found}{$obj->Id} = $obj; - $obj->_AllLinkedTickets( %args, _top => 0 ); + $args{_found}{$link->BaseObj->Id} = $link->BaseObj; + $link->BaseObj->AllDependedOnBy( %args, _top => 0 ); } - elsif ($obj->Type eq $args{Type}) { - $args{_found}{$obj->Id} = $obj; + elsif ($link->BaseObj->Type eq $args{Type}) { + $args{_found}{$link->BaseObj->Id} = $link->BaseObj; } else { - $obj->_AllLinkedTickets( %args, _top => 0 ); + $link->BaseObj->AllDependedOnBy( %args, _top => 0 ); } } @@ -1171,8 +1233,37 @@ sub DependsOn { # }}} +# {{{ Customers + +=head2 Customers + + This returns an RT::Links object which references all the customers that this object is a member of. + +=cut +sub Customers { + my( $self, %opt ) = @_; + my $Debug = $opt{'Debug'}; + + unless ( $self->{'Customers'} ) { + + $self->{'Customers'} = $self->MemberOf->Clone; + + $self->{'Customers'}->Limit( + FIELD => 'Target', + OPERATOR => 'STARTSWITH', + VALUE => 'freeside://freeside/cust_main/', + ); + } + warn "->Customers method called on $self; returning ". + ref($self->{'Customers'}). ' object' + if $Debug; + + return $self->{'Customers'}; +} + +# }}} # {{{ sub _Links @@ -1214,50 +1305,6 @@ sub _Links { # }}} -# {{{ sub FormatType - -=head2 FormatType - -Takes a Type and returns a string that is more human readable. - -=cut - -sub FormatType{ - my $self = shift; - my %args = ( Type => '', - @_ - ); - $args{Type} =~ s/([A-Z])/" " . lc $1/ge; - $args{Type} =~ s/^\s+//; - return $args{Type}; -} - - -# }}} - -# {{{ sub FormatLink - -=head2 FormatLink - -Takes either a Target or a Base and returns a string of human friendly text. - -=cut - -sub FormatLink { - my $self = shift; - my %args = ( Object => undef, - FallBack => '', - @_ - ); - my $text = "URI " . $args{FallBack}; - if ($args{Object} && $args{Object}->isa("RT::Ticket")) { - $text = "Ticket " . $args{Object}->id; - } - return $text; -} - -# }}} - # {{{ sub _AddLink =head2 _AddLink @@ -1269,6 +1316,7 @@ Returns C, C and C flag. =cut + sub _AddLink { my $self = shift; my %args = ( Target => '', @@ -1283,7 +1331,7 @@ sub _AddLink { my $direction; if ( $args{'Base'} and $args{'Target'} ) { - $RT::Logger->debug( "$self tried to create a link. both base and target were specified" ); + $RT::Logger->debug( "$self tried to create a link. both base and target were specified\n" ); return ( 0, $self->loc("Can't specifiy both base and target") ); } elsif ( $args{'Base'} ) { @@ -1325,14 +1373,10 @@ sub _AddLink { return ( 0, $self->loc("Link could not be created") ); } - my $basetext = $self->FormatLink(Object => $link->BaseObj, - FallBack => $args{Base}); - my $targettext = $self->FormatLink(Object => $link->TargetObj, - FallBack => $args{Target}); - my $typetext = $self->FormatType(Type => $args{Type}); my $TransString = - "$basetext $typetext $targettext."; - return ( $linkid, $TransString ) ; + "Record $args{'Base'} $args{Type} record $args{'Target'}."; + + return ( $linkid, $self->loc( "Link created ([_1])", $TransString ) ); } # }}} @@ -1363,7 +1407,7 @@ sub _DeleteLink { my $remote_link; if ( $args{'Base'} and $args{'Target'} ) { - $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target"); + $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target\n"); return ( 0, $self->loc("Can't specifiy both base and target") ); } elsif ( $args{'Base'} ) { @@ -1377,32 +1421,28 @@ sub _DeleteLink { $direction='Base'; } else { - $RT::Logger->error("Base or Target must be specified"); + $RT::Logger->error("Base or Target must be specified\n"); return ( 0, $self->loc('Either base or target must be specified') ); } my $link = new RT::Link( $self->CurrentUser ); - $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} ); + $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} . "\n" ); $link->LoadByParams( Base=> $args{'Base'}, Type=> $args{'Type'}, Target=> $args{'Target'} ); #it's a real link. - if ( $link->id ) { - my $basetext = $self->FormatLink(Object => $link->BaseObj, - FallBack => $args{Base}); - my $targettext = $self->FormatLink(Object => $link->TargetObj, - FallBack => $args{Target}); - my $typetext = $self->FormatType(Type => $args{Type}); + my $linkid = $link->id; $link->Delete(); - my $TransString = "$basetext no longer $typetext $targettext."; - return ( 1, $TransString); + + my $TransString = "Record $args{'Base'} no longer $args{Type} record $args{'Target'}."; + return ( 1, $self->loc("Link deleted ([_1])", $TransString)); } #if it's not a link we can find else { - $RT::Logger->debug("Couldn't find that link"); + $RT::Logger->debug("Couldn't find that link\n"); return ( 0, $self->loc("Link not found") ); } } @@ -1481,7 +1521,7 @@ sub _NewTransaction { if ( defined $args{'TimeTaken'} and $self->can('_UpdateTimeTaken')) { $self->_UpdateTimeTaken( $args{'TimeTaken'} ); } - if ( RT->Config->Get('UseTransactionBatch') and $transaction ) { + if ( $RT::UseTransactionBatch and $transaction ) { push @{$self->{_TransactionBatch}}, $trans if $args{'CommitScrips'}; } return ( $transaction, $msg, $trans ); @@ -1524,13 +1564,11 @@ sub Transactions { sub CustomFields { my $self = shift; my $cfs = RT::CustomFields->new( $self->CurrentUser ); - - $cfs->SetContextObject( $self ); + # XXX handle multiple types properly $cfs->LimitToLookupType( $self->CustomFieldLookupType ); $cfs->LimitToGlobalOrObjectId( - $self->_LookupId( $self->CustomFieldLookupType ) - ); + $self->_LookupId( $self->CustomFieldLookupType ) ); return $cfs; } @@ -1563,18 +1601,27 @@ sub CustomFieldLookupType { return ref($self); } +#TODO Deprecated API. Destroy in 3.6 +sub _LookupTypes { + my $self = shift; + $RT::Logger->warning("_LookupTypes call is deprecated at (". join(":",caller)."). Replace with CustomFieldLookupType"); + + return($self->CustomFieldLookupType); + +} + # {{{ AddCustomFieldValue =head2 AddCustomFieldValue { Field => FIELD, Value => VALUE } -VALUE should be a string. FIELD can be any identifier of a CustomField -supported by L method. +VALUE should be a string. +FIELD can be a CustomField object OR a CustomField ID. -Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field, -deletes the old value. + +Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field, +deletes the old value. If VALUE is not a valid value for the custom field, returns -(0, 'Error message' ) otherwise, returns ($id, 'Success Message') where -$id is ID of created L object. +(0, 'Error message' ) otherwise, returns (1, 'Success Message') =cut @@ -1588,13 +1635,12 @@ sub _AddCustomFieldValue { my %args = ( Field => undef, Value => undef, - LargeContent => undef, - ContentType => undef, RecordTransaction => 1, @_ ); my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'}); + unless ( $cf->Id ) { return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) ); } @@ -1610,11 +1656,8 @@ sub _AddCustomFieldValue { ) ); } - - # empty string is not correct value of any CF, so undef it - foreach ( qw(Value LargeContent) ) { - $args{ $_ } = undef if defined $args{ $_ } && !length $args{ $_ }; - } + # Load up a ObjectCustomFieldValues object for this custom field and this ticket + my $values = $cf->ValuesForObject($self); unless ( $cf->ValidateValue( $args{'Value'} ) ) { return ( 0, $self->loc("Invalid value for custom field") ); @@ -1622,14 +1665,11 @@ sub _AddCustomFieldValue { # If the custom field only accepts a certain # of values, delete the existing # value and record a "changed from foo to bar" transaction - unless ( $cf->UnlimitedValues ) { - - # Load up a ObjectCustomFieldValues object for this custom field and this ticket - my $values = $cf->ValuesForObject($self); + unless ( $cf->UnlimitedValues) { - # We need to whack any old values here. In most cases, the custom field should - # only have one value to delete. In the pathalogical case, this custom field - # used to be a multiple and we have many values to whack.... + # We need to whack any old values here. In most cases, the custom field should + # only have one value to delete. In the pathalogical case, this custom field + # used to be a multiple and we have many values to whack.... my $cf_values = $values->Count; if ( $cf_values > $cf->MaxValues ) { @@ -1658,27 +1698,8 @@ sub _AddCustomFieldValue { my ( $old_value, $old_content ); if ( $old_value = $values->First ) { - $old_content = $old_value->Content; - $old_content = undef if defined $old_content && !length $old_content; - - my $is_the_same = 1; - if ( defined $args{'Value'} ) { - $is_the_same = 0 unless defined $old_content - && lc $old_content eq lc $args{'Value'}; - } else { - $is_the_same = 0 if defined $old_content; - } - if ( $is_the_same ) { - my $old_content = $old_value->LargeContent; - if ( defined $args{'LargeContent'} ) { - $is_the_same = 0 unless defined $old_content - && $old_content eq $args{'LargeContent'}; - } else { - $is_the_same = 0 if defined $old_content; - } - } - - return $old_value->id if $is_the_same; + $old_content = $old_value->Content(); + return (1) if( $old_content eq $args{'Value'} && $old_value->LargeContent eq $args{'LargeContent'});; } my ( $new_value_id, $value_msg ) = $cf->AddValueForObject( @@ -1688,17 +1709,19 @@ sub _AddCustomFieldValue { ContentType => $args{'ContentType'}, ); - unless ( $new_value_id ) { - return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg ) ); + unless ($new_value_id) { + return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) ); } my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser ); - $new_value->Load( $new_value_id ); + $new_value->Load($new_value_id); # now that adding the new value was successful, delete the old one - if ( $old_value ) { + if ($old_value) { my ( $val, $msg ) = $old_value->Delete(); - return ( 0, $msg ) unless $val; + unless ($val) { + return ( 0, $msg ); + } } if ( $args{'RecordTransaction'} ) { @@ -1711,45 +1734,47 @@ sub _AddCustomFieldValue { ); } - my $new_content = $new_value->Content; - unless ( defined $old_content && length $old_content ) { - return ( $new_value_id, $self->loc( "[_1] [_2] added", $cf->Name, $new_content )); + if ( $old_value eq '' ) { + return ( 1, $self->loc( "[_1] [_2] added", $cf->Name, $new_value->Content )); } - elsif ( !defined $new_content || !length $new_content ) { - return ( $new_value_id, - $self->loc( "[_1] [_2] deleted", $cf->Name, $old_content ) ); + elsif ( $new_value->Content eq '' ) { + return ( 1, + $self->loc( "[_1] [_2] deleted", $cf->Name, $old_value->Content ) ); } else { - return ( $new_value_id, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_content)); + return ( 1, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_value->Content)); } } # otherwise, just add a new value and record "new value added" else { - my ($new_value_id, $msg) = $cf->AddValueForObject( + my ($new_value_id, $value_msg) = $cf->AddValueForObject( Object => $self, Content => $args{'Value'}, LargeContent => $args{'LargeContent'}, ContentType => $args{'ContentType'}, ); - unless ( $new_value_id ) { - return ( 0, $self->loc( "Could not add new custom field value: [_1]", $msg ) ); + unless ($new_value_id) { + return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) ); } if ( $args{'RecordTransaction'} ) { - my ( $tid, $msg ) = $self->_NewTransaction( + my ( $TransactionId, $Msg, $TransactionObj ) = + $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, NewReference => $new_value_id, ReferenceType => 'RT::ObjectCustomFieldValue', - ); - unless ( $tid ) { - return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $msg ) ); + ); + unless ($TransactionId) { + return ( 0, + $self->loc( "Couldn't create a transaction: [_1]", $Msg ) ); } } - return ( $new_value_id, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name ) ); + return ( 1, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name)); } + } # }}} @@ -1777,10 +1802,10 @@ sub DeleteCustomFieldValue { ); my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'}); + unless ( $cf->Id ) { return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) ); } - my ( $val, $msg ) = $cf->DeleteValueForObject( Object => $self, Id => $args{'ValueId'}, @@ -1789,7 +1814,6 @@ sub DeleteCustomFieldValue { unless ($val) { return ( 0, $msg ); } - my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, @@ -1823,34 +1847,15 @@ Takes a field id or name sub FirstCustomFieldValue { my $self = shift; my $field = shift; + my $values = $self->CustomFieldValues($field); + if ($values->First) { + return $values->First->Content; + } else { + return undef; + } - my $values = $self->CustomFieldValues( $field ); - return undef unless my $first = $values->First; - return $first->Content; } -=head2 CustomFieldValuesAsString FIELD - -Return the content of the CustomField FIELD for this ticket. -If this is a multi-value custom field, values will be joined with newlines. - -Takes a field id or name as the first argument - -Takes an optional Separator => "," second and third argument -if you want to join the values using something other than a newline - -=cut - -sub CustomFieldValuesAsString { - my $self = shift; - my $field = shift; - my %args = @_; - my $separator = $args{Separator} || "\n"; - - my $values = $self->CustomFieldValues( $field ); - return join ($separator, grep { defined $_ } - map { $_->Content } @{$values->ItemsArrayRef}); -} # {{{ CustomFieldValues @@ -1868,12 +1873,11 @@ sub CustomFieldValues { my $self = shift; my $field = shift; - if ( $field ) { - my $cf = $self->LoadCustomFieldByIdentifier( $field ); + if ($field) { + my $cf = $self->LoadCustomFieldByIdentifier($field); - # we were asked to search on a custom field we couldn't find + # we were asked to search on a custom field we couldn't fine unless ( $cf->id ) { - $RT::Logger->warning("Couldn't load custom field by '$field' identifier"); return RT::ObjectCustomFieldValues->new( $self->CurrentUser ); } return ( $cf->ValuesForObject($self) ); @@ -1881,11 +1885,12 @@ sub CustomFieldValues { # we're not limiting to a specific custom field; my $ocfs = RT::ObjectCustomFieldValues->new( $self->CurrentUser ); - $ocfs->LimitToObject( $self ); + $ocfs->LimitToObject($self); return $ocfs; + } -=head2 LoadCustomFieldByIdentifier IDENTIFER +=head2 CustomField IDENTIFER Find the custom field has id or name IDENTIFIER for this object. @@ -1897,32 +1902,35 @@ sub LoadCustomFieldByIdentifier { my $self = shift; my $field = shift; - my $cf; + my $cf = RT::CustomField->new($self->CurrentUser); + if ( UNIVERSAL::isa( $field, "RT::CustomField" ) ) { - $cf = RT::CustomField->new($self->CurrentUser); - $cf->SetContextObject( $self ); $cf->LoadById( $field->id ); } elsif ($field =~ /^\d+$/) { $cf = RT::CustomField->new($self->CurrentUser); - $cf->SetContextObject( $self ); - $cf->LoadById($field); + $cf->Load($field); } else { my $cfs = $self->CustomFields($self->CurrentUser); - $cfs->SetContextObject( $self ); $cfs->Limit(FIELD => 'Name', VALUE => $field, CASESENSITIVE => 0); $cf = $cfs->First || RT::CustomField->new($self->CurrentUser); } return $cf; } -sub ACLEquivalenceObjects { } -sub BasicColumns { } +# }}} + +# }}} + +# }}} + +sub BasicColumns { +} sub WikiBase { - return RT->Config->Get('WebPath'). "/index.html?q="; + return $RT::WebPath. "/index.html?q="; } eval "require RT::Record_Vendor";