X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FRecord.pm;h=6601a0df21224bd9b984ee425f70d7a7c729a13e;hb=7588a4ac90a9b07c08a3107cd1107d773be1c991;hp=e134178beb7f944d2d2733110d84372a76275467;hpb=f3c4966ed1f6ec3db7accd6dcdd3a5a3821d72a7;p=freeside.git diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm index e134178be..6601a0df2 100755 --- a/rt/lib/RT/Record.pm +++ b/rt/lib/RT/Record.pm @@ -2,7 +2,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -501,19 +501,24 @@ sub _Set { # $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]", - $self->loc( $args{'Field'} ), - ( $old_val ? '"' . $old_val . '"' : $self->loc("(no value)") ), - '"' . $self->__Value( $args{'Field'}) . '"' - ); - } else { - - $msg = $self->CurrentUser->loc_fuzzy($msg); + if ($self->SQLType( $args{'Field'}) =~ /text/) { + $msg = $self->loc( + "[_1] updated", + $self->loc( $args{'Field'} ), + ); + } else { + $msg = $self->loc( + "[_1] changed from [_2] to [_3]", + $self->loc( $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; + return wantarray ? ($status, $msg) : $ret; } @@ -639,6 +644,8 @@ sub __Value { my $value = $self->SUPER::__Value($field); + return undef if (!defined $value); + if ( $args{'decode_utf8'} ) { if ( !utf8::is_utf8($value) ) { utf8::decode($value); @@ -886,6 +893,8 @@ sub Update { $value =~ s/\r\n/\n/gs; + my $truncated_value = $self->TruncateValue($attribute, $value); + # If Queue is 'General', we want to resolve the queue name for # the object. @@ -900,8 +909,12 @@ sub Update { 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(); + + my $current = $self->$attribute(); + # RT::Queue->Lifecycle returns a Lifecycle object instead of name + $current = eval { $current->Name } if ref $current; + next if $truncated_value eq $current; + next if ( $truncated_value || 0 ) eq $current; }; $new_values{$attribute} = $value; @@ -1416,7 +1429,7 @@ sub _AddLink { Delete a link. takes a paramhash of Base, Target and Type. Either Base or Target must be null. The null value will -be replaced with this ticket\'s id +be replaced with this ticket's id =cut @@ -1481,8 +1494,35 @@ sub _DeleteLink { } +=head1 LockForUpdate + +In a database transaction, gains an exclusive lock on the row, to +prevent race conditions. On SQLite, this is a "RESERVED" lock on the +entire database. +=cut +sub LockForUpdate { + my $self = shift; + + my $pk = $self->_PrimaryKey; + my $id = @_ ? $_[0] : $self->$pk; + $self->_expire if $self->isa("DBIx::SearchBuilder::Record::Cachable"); + if (RT->Config->Get('DatabaseType') eq "SQLite") { + # SQLite does DB-level locking, upgrading the transaction to + # "RESERVED" on the first UPDATE/INSERT/DELETE. Do a no-op + # UPDATE to force the upgade. + return RT->DatabaseHandle->dbh->do( + "UPDATE " .$self->Table. + " SET $pk = $pk WHERE 1 = 0"); + } else { + return $self->_LoadFromSQL( + "SELECT * FROM ".$self->Table + ." WHERE $pk = ? FOR UPDATE", + $id, + ); + } +} =head2 _NewTransaction PARAMHASH @@ -1510,6 +1550,11 @@ sub _NewTransaction { @_ ); + my $in_txn = RT->DatabaseHandle->TransactionDepth; + RT->DatabaseHandle->BeginTransaction unless $in_txn; + + $self->LockForUpdate; + my $old_ref = $args{'OldReference'}; my $new_ref = $args{'NewReference'}; my $ref_type = $args{'ReferenceType'}; @@ -1557,6 +1602,9 @@ sub _NewTransaction { if ( RT->Config->Get('UseTransactionBatch') and $transaction ) { push @{$self->{_TransactionBatch}}, $trans if $args{'CommitScrips'}; } + + RT->DatabaseHandle->Commit unless $in_txn; + return ( $transaction, $msg, $trans ); } @@ -1596,29 +1644,37 @@ sub CustomFields { $cfs->SetContextObject( $self ); # XXX handle multiple types properly $cfs->LimitToLookupType( $self->CustomFieldLookupType ); - $cfs->LimitToGlobalOrObjectId( - $self->_LookupId( $self->CustomFieldLookupType ) - ); + $cfs->LimitToGlobalOrObjectId( $self->CustomFieldLookupId ); $cfs->ApplySortOrder; return $cfs; } -# TODO: This _only_ works for RT::Class classes. it doesn't work, for example, -# for RT::IR classes. +# TODO: This _only_ works for RT::Foo classes. it doesn't work, for +# example, for RT::IR::Foo classes. -sub _LookupId { +sub CustomFieldLookupId { my $self = shift; - my $lookup = shift; + my $lookup = shift || $self->CustomFieldLookupType; my @classes = ($lookup =~ /RT::(\w+)-/g); + # Work on "RT::Queue", for instance + return $self->Id unless @classes; + my $object = $self; + # Save a ->Load call by not calling ->FooObj->Id, just ->Foo + my $final = shift @classes; foreach my $class (reverse @classes) { my $method = "${class}Obj"; $object = $object->$method; } - return $object->Id; + my $id = $object->$final; + unless (defined $id) { + my $method = "${final}Obj"; + $id = $object->$method->Id; + } + return $id; } @@ -1675,7 +1731,7 @@ sub _AddCustomFieldValue { 0, $self->loc( "Custom field [_1] does not apply to this object", - $args{'Field'} + ref $args{'Field'} ? $args{'Field'}->id : $args{'Field'} ) ); }