diff options
Diffstat (limited to 'rt/lib/RT/Record.pm')
-rwxr-xr-x | rt/lib/RT/Record.pm | 644 |
1 files changed, 23 insertions, 621 deletions
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm index d3a826808..57a5ea7fc 100755 --- a/rt/lib/RT/Record.pm +++ b/rt/lib/RT/Record.pm @@ -1,8 +1,8 @@ -# BEGIN BPS TAGGED BLOCK {{{ +# {{{ BEGIN BPS TAGGED BLOCK # # COPYRIGHT: # -# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC # <jesse@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -42,8 +42,7 @@ # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # -# END BPS TAGGED BLOCK }}} - +# }}} END BPS TAGGED BLOCK =head1 NAME RT::Record - Base class for RT record objects @@ -108,48 +107,6 @@ sub _PrimaryKeys { # }}} -=head2 Delete - -Delete this record object from the database. - -=cut - -sub Delete { - my $self = shift; - my ($rv) = $self->SUPER::Delete; - if ($rv) { - return ($rv, $self->loc("Object deleted")); - } else { - - return(0, $self->loc("Object could not be deleted")) - } -} - -=head2 ObjectTypeStr - -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 - -sub ObjectTypeStr { - my $self = shift; - if (ref($self) =~ /^.*::(\w+)$/) { - return $self->loc($1); - } else { - return $self->loc(ref($self)); - } -} - =head2 Attributes Return this object's attributes as an RT::Attributes object @@ -188,9 +145,7 @@ sub AddAttribute { Description => $args{'Description'}, Content => $args{'Content'} ); - - # XXX TODO: Why won't RedoSearch work here? - $self->Attributes->_DoSearch; + $self->Attributes->RedoSearch; return ($id, $msg); } @@ -258,7 +213,7 @@ sub _Handle { # {{{ sub Create -=head2 Create PARAMHASH +=item Create PARAMHASH Takes a PARAMHASH of Column -> Value pairs. If any Column has a Validate$PARAMNAME subroutine defined and the @@ -377,11 +332,10 @@ sub LoadByCols { $newhash{$key} = $hash{$key}; } else { - my ($op, $val, $func); - ($key, $op, $val, $func) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key}); + my ($op, $val); + ($key, $op, $val) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key}); $newhash{$key}->{operator} = $op; $newhash{$key}->{value} = $val; - $newhash{$key}->{function} = $func; } } @@ -483,7 +437,6 @@ sub LongSinceUpdateAsString { # }}} Datehandling # {{{ sub _Set -# sub _Set { my $self = shift; @@ -501,33 +454,12 @@ sub _Set { $args{'Value'} = 0; } - my $old_val = $self->__Value($args{'Field'}); - $self->_SetLastUpdated(); - my $ret = $self->SUPER::_Set( + $self->_SetLastUpdated(); + my ( $val, $msg ) = $self->SUPER::_Set( Field => $args{'Field'}, Value => $args{'Value'}, IsSQL => $args{'IsSQL'} ); - my ($status, $msg) = $ret->as_array(); - - # @values has two values, a status code and a message. - - # $ret is a Class::ReturnValue object. as such, in a boolean context, it's a bool - # we want to change the standard "success" message - if ($status) { - $msg = - $self->loc( - "[_1] changed from [_2] to [_3]", - $args{'Field'}, - ( $old_val ? "'$old_val'" : $self->loc("(no value)") ), - '"' . $self->__Value( $args{'Field'}) . '"' - ); - } else { - - $msg = $self->CurrentUser->loc_fuzzy($msg); - } - return wantarray ? ($status, $msg) : $ret; - } # }}} @@ -617,22 +549,8 @@ sub URI { } # }}} + -=head2 ValidateName NAME - -Validate the name of the record we're creating. Mostly, just make sure it's not a numeric ID, which is invalid for Name - -=cut - -sub ValidateName { - my $self = shift; - my $value = shift; - if ($value && $value=~ /^\d+$/) { - return(0); - } else { - return (1); - } -} @@ -832,26 +750,6 @@ sub _EncodeLOB { } -sub _DecodeLOB { - my $self = shift; - my $ContentType = shift; - my $ContentEncoding = shift; - my $Content = shift; - - if ( $ContentEncoding eq 'base64' ) { - $Content = MIME::Base64::decode_base64($Content); - } - elsif ( $ContentEncoding eq 'quoted-printable' ) { - $Content = MIME::QuotedPrint::decode($Content); - } - elsif ( $ContentEncoding && $ContentEncoding ne 'none' ) { - return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) ); - } - if ( $ContentType eq 'text/plain' ) { - $Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content); - } - return ($Content); -} # {{{ LINKDIRMAP # A helper table for links mapping to make it easier @@ -895,7 +793,8 @@ sub Update { && defined( $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute } ) - ) { + ) + { $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }; } @@ -919,15 +818,14 @@ sub Update { next if ( $value eq $self->$attribute() ); my $method = "Set$attribute"; my ( $code, $msg ) = $self->$method($value); - my ($prefix) = ref($self) =~ /RT::(\w+)/; - # Default to $id, but use name if we can get it. - my $label = $self->id; - $label = $self->Name if (UNIVERSAL::can($self,'Name')); - push @results, $self->loc( "$prefix [_1]", $label ) . ': '. $msg; + my ($prefix) = ref($self) =~ /RT::(\w+)/; + push @results, + $self->loc( "$prefix [_1]", $self->id ) . ': ' + . $self->loc($attribute) . ': ' + . $self->CurrentUser->loc_fuzzy($msg); =for loc - "[_1] could not be set to [_2].", # loc "That is already the current value", # loc "No value sent to _Set!\n", # loc @@ -940,7 +838,6 @@ sub Update { "Couldn't find row", # loc "Missing a primary key?: [_1]", # loc "Found Object", # loc - =cut } @@ -948,7 +845,7 @@ sub Update { return @results; } -# {{{ Routines dealing with Links +# {{{ Routines dealing with Links between tickets # {{{ Link Collections @@ -1056,12 +953,6 @@ 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"); @@ -1070,12 +961,10 @@ 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); +ok($t2->Resolve); ($rid, $rmsg)= $t1->Resolve(); ok(!$rid, $rmsg); -my ($rid3,$rmsg3) = $t3->Resolve; -ok ($rid3,$rmsg3); +ok($t3->Resolve); ($rid, $rmsg)= $t1->Resolve(); ok($rid, $rmsg); @@ -1206,14 +1095,6 @@ sub DependsOn { # {{{ sub _Links -=head2 Links DIRECTION TYPE - -return links to/from this object. - -=cut - -*Links = \&_Links; - sub _Links { my $self = shift; @@ -1263,7 +1144,8 @@ 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\n" ); + $RT::Logger->debug( +"$self tried to delete a link. both base and target were specified\n" ); return ( 0, $self->loc("Can't specifiy both base and target") ); } elsif ( $args{'Base'} ) { @@ -1310,7 +1192,7 @@ sub _AddLink { my $TransString = "Record $args{'Base'} $args{Type} record $args{'Target'}."; - return ( $linkid, $self->loc( "Link created ([_1])", $TransString ) ); + return ( 1, $self->loc( "Link created ([_1])", $TransString ) ); } # }}} @@ -1383,486 +1265,6 @@ sub _DeleteLink { # }}} -# }}} - -# {{{ Routines dealing with transactions - -# {{{ sub _NewTransaction - -=head2 _NewTransaction PARAMHASH - -Private function to create a new RT::Transaction object for this ticket update - -=cut - -sub _NewTransaction { - my $self = shift; - my %args = ( - TimeTaken => undef, - Type => undef, - OldValue => undef, - NewValue => undef, - OldReference => undef, - NewReference => undef, - ReferenceType => undef, - Data => undef, - Field => undef, - MIMEObj => undef, - ActivateScrips => 1, - CommitScrips => 1, - @_ - ); - - my $old_ref = $args{'OldReference'}; - my $new_ref = $args{'NewReference'}; - my $ref_type = $args{'ReferenceType'}; - if ($old_ref or $new_ref) { - $ref_type ||= ref($old_ref) || ref($new_ref); - if (!$ref_type) { - $RT::Logger->error("Reference type not specified for transaction"); - return; - } - $old_ref = $old_ref->Id if ref($old_ref); - $new_ref = $new_ref->Id if ref($new_ref); - } - - require RT::Transaction; - my $trans = new RT::Transaction( $self->CurrentUser ); - my ( $transaction, $msg ) = $trans->Create( - ObjectId => $self->Id, - ObjectType => ref($self), - TimeTaken => $args{'TimeTaken'}, - Type => $args{'Type'}, - Data => $args{'Data'}, - Field => $args{'Field'}, - NewValue => $args{'NewValue'}, - OldValue => $args{'OldValue'}, - NewReference => $new_ref, - OldReference => $old_ref, - ReferenceType => $ref_type, - MIMEObj => $args{'MIMEObj'}, - ActivateScrips => $args{'ActivateScrips'}, - CommitScrips => $args{'CommitScrips'}, - ); - - # Rationalize the object since we may have done things to it during the caching. - $self->Load($self->Id); - - $RT::Logger->warning($msg) unless $transaction; - - $self->_SetLastUpdated; - - if ( defined $args{'TimeTaken'} ) { - $self->_UpdateTimeTaken( $args{'TimeTaken'} ); - } - if ( $RT::UseTransactionBatch and $transaction ) { - push @{$self->{_TransactionBatch}}, $trans; - } - return ( $transaction, $msg, $trans ); -} - -# }}} - -# {{{ sub Transactions - -=head2 Transactions - - Returns an RT::Transactions object of all transactions on this record object - -=cut - -sub Transactions { - my $self = shift; - - use RT::Transactions; - my $transactions = RT::Transactions->new( $self->CurrentUser ); - - #If the user has no rights, return an empty object - $transactions->Limit( - FIELD => 'ObjectId', - VALUE => $self->id, - ); - $transactions->Limit( - FIELD => 'ObjectType', - VALUE => ref($self), - ); - - return ($transactions); -} - -# }}} -# }}} -# -# {{{ Routines dealing with custom fields - -sub CustomFields { - my $self = shift; - my $cfs = RT::CustomFields->new( $self->CurrentUser ); - - # XXX handle multiple types properly - $cfs->LimitToLookupType( $self->CustomFieldLookupType ); - $cfs->LimitToGlobalOrObjectId( - $self->_LookupId( $self->CustomFieldLookupType ) ); - - return $cfs; -} - -# TODO: This _only_ works for RT::Class classes. it doesn't work, for example, for RT::FM classes. - -sub _LookupId { - my $self = shift; - my $lookup = shift; - my @classes = ($lookup =~ /RT::(\w+)-/g); - - my $object = $self; - foreach my $class (reverse @classes) { - my $method = "${class}Obj"; - $object = $object->$method; - } - - return $object->Id; -} - - -=head2 CustomFieldLookupType - -Returns the path RT uses to figure out which custom fields apply to this object. - -=cut - -sub CustomFieldLookupType { - my $self = shift; - return ref($self); -} - -#TODO Deprecated API. Destroy in 3.6 -sub _LookupTypes { - my $self = shift; - $RT::Logger->warning("_LookupTypes call is deprecated. Replace with CustomFieldLookupType"); - $RT::Logger->warning("Besides, it was a private API. Were you doing using it?"); - - return($self->CustomFieldLookupType); - -} - -# {{{ AddCustomFieldValue - -=head2 AddCustomFieldValue { Field => FIELD, Value => VALUE } - -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. -If VALUE is not a valid value for the custom field, returns -(0, 'Error message' ) otherwise, returns (1, 'Success Message') - -=cut - -sub AddCustomFieldValue { - my $self = shift; - $self->_AddCustomFieldValue(@_); -} - -sub _AddCustomFieldValue { - my $self = shift; - my %args = ( - Field => undef, - Value => undef, - RecordTransaction => 1, - @_ - ); - - my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'}); - - unless ( $cf->Id ) { - return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) ); - } - - my $OCFs = $self->CustomFields; - $OCFs->Limit( FIELD => 'id', VALUE => $cf->Id ); - unless ( $OCFs->Count ) { - return ( - 0, - $self->loc( - "Custom field [_1] does not apply to this object", - $args{'Field'} - ) - ); - } - # 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") ); - } - - # 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) { - - # 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 ) { - my $i = 0; #We want to delete all but the max we can currently have , so we can then - # execute the same code to "change" the value from old to new - while ( my $value = $values->Next ) { - $i++; - if ( $i < $cf_values ) { - my ( $val, $msg ) = $cf->DeleteValueForObject( - Object => $self, - Content => $value->Content - ); - unless ($val) { - return ( 0, $msg ); - } - my ( $TransactionId, $Msg, $TransactionObj ) = - $self->_NewTransaction( - Type => 'CustomField', - Field => $cf->Id, - OldReference => $value, - ); - } - } - } - - my ( $old_value, $old_content ); - if ( $old_value = $cf->ValuesForObject($self)->First ) { - $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( - 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] ",, $value_msg)); - } - - my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser ); - $new_value->Load($new_value_id); - - # now that adding the new value was successful, delete the old one - if ($old_value) { - my ( $val, $msg ) = $old_value->Delete(); - unless ($val) { - return ( 0, $msg ); - } - } - - if ( $args{'RecordTransaction'} ) { - my ( $TransactionId, $Msg, $TransactionObj ) = - $self->_NewTransaction( - Type => 'CustomField', - Field => $cf->Id, - OldReference => $old_value, - NewReference => $new_value, - ); - } - - if ( $old_value eq '' ) { - return ( 1, $self->loc( "[_1] [_2] added", $cf->Name, $new_value->Content )); - } - elsif ( $new_value->Content eq '' ) { - return ( 1, - $self->loc( "[_1] [_2] deleted", $cf->Name, $old_value->Content ) ); - } - else { - 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) = $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. ") ); - } - if ( $args{'RecordTransaction'} ) { - my ( $TransactionId, $Msg, $TransactionObj ) = - $self->_NewTransaction( - Type => 'CustomField', - Field => $cf->Id, - NewReference => $new_value_id, - ReferenceType => 'RT::ObjectCustomFieldValue', - ); - unless ($TransactionId) { - return ( 0, - $self->loc( "Couldn't create a transaction: [_1]", $Msg ) ); - } - } - return ( 1, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name)); - } - -} - -# }}} - -# {{{ DeleteCustomFieldValue - -=head2 DeleteCustomFieldValue { Field => FIELD, Value => VALUE } - -Deletes VALUE as a value of CustomField FIELD. - -VALUE can be a string, a CustomFieldValue or a ObjectCustomFieldValue. - -If VALUE is not a valid value for the custom field, returns -(0, 'Error message' ) otherwise, returns (1, 'Success Message') - -=cut - -sub DeleteCustomFieldValue { - my $self = shift; - my %args = ( - Field => undef, - Value => undef, - ValueId => undef, - @_ - ); - - 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'}, - Content => $args{'Value'}, - ); - unless ($val) { - return ( 0, $msg ); - } - my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( - Type => 'CustomField', - Field => $cf->Id, - OldReference => $val, - ReferenceType => 'RT::ObjectCustomFieldValue', - ); - unless ($TransactionId) { - return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $Msg ) ); - } - - return ( - $TransactionId, - $self->loc( - "[_1] is no longer a value for custom field [_2]", - $TransactionObj->OldValue, $cf->Name - ) - ); -} - -# }}} - -# {{{ FirstCustomFieldValue - -=head2 FirstCustomFieldValue FIELD - -Return the content of the first value of CustomField FIELD for this ticket -Takes a field id or name - -=cut - -sub FirstCustomFieldValue { - my $self = shift; - my $field = shift; - my $values = $self->CustomFieldValues($field); - if ($values->First) { - return $values->First->Content; - } else { - return undef; - } - -} - - - -# {{{ CustomFieldValues - -=head2 CustomFieldValues FIELD - -Return a ObjectCustomFieldValues object of all values of the CustomField whose -id or Name is FIELD for this record. - -Returns an RT::ObjectCustomFieldValues object - -=cut - -sub CustomFieldValues { - my $self = shift; - my $field = shift; - - if ($field) { - my $cf = $self->LoadCustomFieldByIdentifier($field); - - # we were asked to search on a custom field we couldn't fine - unless ( $cf->id ) { - return RT::ObjectCustomFieldValues->new( $self->CurrentUser ); - } - return ( $cf->ValuesForObject($self) ); - } - - # we're not limiting to a specific custom field; - my $ocfs = RT::ObjectCustomFieldValues->new( $self->CurrentUser ); - $ocfs->LimitToObject($self); - return $ocfs; - -} - -=head2 CustomField IDENTIFER - -Find the custom field has id or name IDENTIFIER for this object. - -If no valid field is found, returns an empty RT::CustomField object. - -=cut - -sub LoadCustomFieldByIdentifier { - my $self = shift; - my $field = shift; - - my $cf = RT::CustomField->new($self->CurrentUser); - - if ( UNIVERSAL::isa( $field, "RT::CustomField" ) ) { - $cf->LoadById( $field->id ); - } - elsif ($field =~ /^\d+$/) { - $cf = RT::CustomField->new($self->CurrentUser); - $cf->Load($field); - } else { - - my $cfs = $self->CustomFields($self->CurrentUser); - $cfs->Limit(FIELD => 'Name', VALUE => $field); - $cf = $cfs->First || RT::CustomField->new($self->CurrentUser); - } - return $cf; -} - - -# }}} - -# }}} - -# }}} - -sub BasicColumns { -} - eval "require RT::Record_Vendor"; die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Vendor.pm}); eval "require RT::Record_Local"; |