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};
214 $newhash{ "lower(" . $key . ")" } = lc( $hash{$key} );
218 # We've clobbered everything we care about. bash the old hash
219 # and replace it with the new hash
222 $self->SUPER::LoadByCols(%hash);
229 # There is room for optimizations in most of those subs:
235 my $obj = new RT::Date( $self->CurrentUser );
237 $obj->Set( Format => 'sql', Value => $self->LastUpdated );
247 my $obj = new RT::Date( $self->CurrentUser );
249 $obj->Set( Format => 'sql', Value => $self->Created );
258 # TODO: This should be deprecated
262 return ( $self->CreatedObj->AgeAsString() );
267 # {{{ LastUpdatedAsString
269 # TODO this should be deprecated
271 sub LastUpdatedAsString {
273 if ( $self->LastUpdated ) {
274 return ( $self->LastUpdatedObj->AsString() );
284 # {{{ CreatedAsString
286 # TODO This should be deprecated
288 sub CreatedAsString {
290 return ( $self->CreatedObj->AsString() );
295 # {{{ LongSinceUpdateAsString
297 # TODO This should be deprecated
299 sub LongSinceUpdateAsString {
301 if ( $self->LastUpdated ) {
303 return ( $self->LastUpdatedObj->AgeAsString() );
326 #if the user is trying to modify the record
327 # TODO: document _why_ this code is here
329 if ( ( !defined( $args{'Field'} ) ) || ( !defined( $args{'Value'} ) ) ) {
333 $self->_SetLastUpdated();
334 my ( $val, $msg ) = $self->SUPER::_Set(
335 Field => $args{'Field'},
336 Value => $args{'Value'},
337 IsSQL => $args{'IsSQL'}
343 # {{{ sub _SetLastUpdated
345 =head2 _SetLastUpdated
347 This routine updates the LastUpdated and LastUpdatedBy columns of the row in question
348 It takes no options. Arguably, this is a bug
352 sub _SetLastUpdated {
355 my $now = new RT::Date( $self->CurrentUser );
358 if ( $self->_Accessible( 'LastUpdated', 'auto' ) ) {
359 my ( $msg, $val ) = $self->__Set(
360 Field => 'LastUpdated',
364 if ( $self->_Accessible( 'LastUpdatedBy', 'auto' ) ) {
365 my ( $msg, $val ) = $self->__Set(
366 Field => 'LastUpdatedBy',
367 Value => $self->CurrentUser->id
378 Returns an RT::User object with the RT account of the creator of this row
384 unless ( exists $self->{'CreatorObj'} ) {
386 $self->{'CreatorObj'} = RT::User->new( $self->CurrentUser );
387 $self->{'CreatorObj'}->Load( $self->Creator );
389 return ( $self->{'CreatorObj'} );
394 # {{{ sub LastUpdatedByObj
396 =head2 LastUpdatedByObj
398 Returns an RT::User object of the last user to touch this object
402 sub LastUpdatedByObj {
404 unless ( exists $self->{LastUpdatedByObj} ) {
405 $self->{'LastUpdatedByObj'} = RT::User->new( $self->CurrentUser );
406 $self->{'LastUpdatedByObj'}->Load( $self->LastUpdatedBy );
408 return $self->{'LastUpdatedByObj'};
414 require Encode::compat if $] < 5.007001;
420 my %args = ( decode_utf8 => 1,
423 unless (defined $field && $field) {
424 $RT::Logger->error("$self __Value called with undef field");
426 my $value = $self->SUPER::__Value($field);
428 return('') if ( !defined($value) || $value eq '');
430 return Encode::decode_utf8($value) || $value if $args{'decode_utf8'};
434 # Set up defaults for DBIx::SearchBuilder::Record::Cachable
439 'fast_update_p' => 1,
440 'cache_for_sec' => 30,
446 When passed a string will "decode" it int a proper UTF-8 string
450 eval "require RT::Record_Vendor";
451 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Vendor.pm});
452 eval "require RT::Record_Local";
453 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Local.pm});