3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
5 # (Except where explictly superceded by other copyright notices)
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
26 RT::Record - Base class for RT record objects
36 ok (require RT::Record);
49 use DBIx::SearchBuilder::Record::Cachable;
56 if ($RT::DontCacheSearchBuilderRecords ) {
57 push (@ISA, 'DBIx::SearchBuilder::Record');
59 push (@ISA, 'DBIx::SearchBuilder::Record::Cachable');
67 $self->CurrentUser(@_);
77 The primary keys for RT classes is 'id'
98 =item Create PARAMHASH
100 Takes a PARAMHASH of Column -> Value pairs.
101 If any Column has a Validate$PARAMNAME subroutine defined and the
102 value provided doesn't pass validation, this routine returns
105 If this object's table has any of the following atetributes defined as
106 'Auto', this routine will automatically fill in their values.
113 foreach my $key ( keys %attribs ) {
114 my $method = "Validate$key";
115 unless ( $self->$method( $attribs{$key} ) ) {
117 return ( 0, $self->loc('Invalid value for [_1]', $key) );
124 my $now = RT::Date->new( $self->CurrentUser );
125 $now->Set( Format => 'unix', Value => time );
126 $attribs{'Created'} = $now->ISO() if ( $self->_Accessible( 'Created', 'auto' ) && !$attribs{'Created'});
128 if ($self->_Accessible( 'Creator', 'auto' ) && !$attribs{'Creator'}) {
129 $attribs{'Creator'} = $self->CurrentUser->id || '0';
131 $attribs{'LastUpdated'} = $now->ISO()
132 if ( $self->_Accessible( 'LastUpdated', 'auto' ) && !$attribs{'LastUpdated'});
134 $attribs{'LastUpdatedBy'} = $self->CurrentUser->id || '0'
135 if ( $self->_Accessible( 'LastUpdatedBy', 'auto' ) && !$attribs{'LastUpdatedBy'});
137 my $id = $self->SUPER::Create(%attribs);
138 if ( UNIVERSAL::isa( $id, 'Class::ReturnValue' ) ) {
142 $self->loc( "Internal Error: [_1]", $id->{error_message} ) );
149 # If the object was created in the database,
150 # load it up now, so we're sure we get what the database
151 # has. Arguably, this should not be necessary, but there
152 # isn't much we can do about it.
156 return ( $id, $self->loc('Object could not be created') );
164 if (UNIVERSAL::isa('errno',$id)) {
170 $self->Load($id) if ($id);
175 return ( $id, $self->loc('Object created') );
189 Override DBIx::SearchBuilder::LoadByCols to do case-insensitive loads if the
198 # If this database is case sensitive we need to uncase objects for
200 if ( $self->_Handle->CaseSensitive ) {
202 foreach my $key ( keys %hash ) {
204 # If we've been passed an empty value, we can't do the lookup.
205 # We don't need to explicitly downcase integers or an id.
207 || !defined( $hash{$key} )
208 || $hash{$key} =~ /^\d+$/
211 $newhash{$key} = $hash{$key};
215 ($key, $op, $val) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key});
216 $newhash{$key}->{operator} = $op;
217 $newhash{$key}->{value} = $val;
221 # We've clobbered everything we care about. bash the old hash
222 # and replace it with the new hash
225 $self->SUPER::LoadByCols(%hash);
232 # There is room for optimizations in most of those subs:
238 my $obj = new RT::Date( $self->CurrentUser );
240 $obj->Set( Format => 'sql', Value => $self->LastUpdated );
250 my $obj = new RT::Date( $self->CurrentUser );
252 $obj->Set( Format => 'sql', Value => $self->Created );
261 # TODO: This should be deprecated
265 return ( $self->CreatedObj->AgeAsString() );
270 # {{{ LastUpdatedAsString
272 # TODO this should be deprecated
274 sub LastUpdatedAsString {
276 if ( $self->LastUpdated ) {
277 return ( $self->LastUpdatedObj->AsString() );
287 # {{{ CreatedAsString
289 # TODO This should be deprecated
291 sub CreatedAsString {
293 return ( $self->CreatedObj->AsString() );
298 # {{{ LongSinceUpdateAsString
300 # TODO This should be deprecated
302 sub LongSinceUpdateAsString {
304 if ( $self->LastUpdated ) {
306 return ( $self->LastUpdatedObj->AgeAsString() );
329 #if the user is trying to modify the record
330 # TODO: document _why_ this code is here
332 if ( ( !defined( $args{'Field'} ) ) || ( !defined( $args{'Value'} ) ) ) {
336 $self->_SetLastUpdated();
337 my ( $val, $msg ) = $self->SUPER::_Set(
338 Field => $args{'Field'},
339 Value => $args{'Value'},
340 IsSQL => $args{'IsSQL'}
346 # {{{ sub _SetLastUpdated
348 =head2 _SetLastUpdated
350 This routine updates the LastUpdated and LastUpdatedBy columns of the row in question
351 It takes no options. Arguably, this is a bug
355 sub _SetLastUpdated {
358 my $now = new RT::Date( $self->CurrentUser );
361 if ( $self->_Accessible( 'LastUpdated', 'auto' ) ) {
362 my ( $msg, $val ) = $self->__Set(
363 Field => 'LastUpdated',
367 if ( $self->_Accessible( 'LastUpdatedBy', 'auto' ) ) {
368 my ( $msg, $val ) = $self->__Set(
369 Field => 'LastUpdatedBy',
370 Value => $self->CurrentUser->id
381 Returns an RT::User object with the RT account of the creator of this row
387 unless ( exists $self->{'CreatorObj'} ) {
389 $self->{'CreatorObj'} = RT::User->new( $self->CurrentUser );
390 $self->{'CreatorObj'}->Load( $self->Creator );
392 return ( $self->{'CreatorObj'} );
397 # {{{ sub LastUpdatedByObj
399 =head2 LastUpdatedByObj
401 Returns an RT::User object of the last user to touch this object
405 sub LastUpdatedByObj {
407 unless ( exists $self->{LastUpdatedByObj} ) {
408 $self->{'LastUpdatedByObj'} = RT::User->new( $self->CurrentUser );
409 $self->{'LastUpdatedByObj'}->Load( $self->LastUpdatedBy );
411 return $self->{'LastUpdatedByObj'};
417 require Encode::compat if $] < 5.007001;
423 my %args = ( decode_utf8 => 1,
426 unless (defined $field && $field) {
427 $RT::Logger->error("$self __Value called with undef field");
429 my $value = $self->SUPER::__Value($field);
431 return('') if ( !defined($value) || $value eq '');
433 return Encode::decode_utf8($value) || $value if $args{'decode_utf8'};
437 # Set up defaults for DBIx::SearchBuilder::Record::Cachable
442 'fast_update_p' => 1,
443 'cache_for_sec' => 30,
449 When passed a string will "decode" it int a proper UTF-8 string
453 eval "require RT::Record_Vendor";
454 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Vendor.pm});
455 eval "require RT::Record_Local";
456 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Local.pm});