1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
55 This module should never be instantiated directly by client code. it's an internal
56 module which should only be instantiated through exported APIs in Ticket, Queue and other
66 package RT::Attachment;
67 use base 'RT::Record';
69 sub Table {'Attachments'}
80 use MIME::QuotedPrint;
82 use RT::Util 'mime_recommended_filename';
84 sub _OverlayAccessible {
86 TransactionId => { 'read'=>1, 'public'=>1, 'write' => 0 },
87 MessageId => { 'read'=>1, 'write' => 0 },
88 Parent => { 'read'=>1, 'write' => 0 },
89 ContentType => { 'read'=>1, 'write' => 0 },
90 Subject => { 'read'=>1, 'write' => 0 },
91 Content => { 'read'=>1, 'write' => 0 },
92 ContentEncoding => { 'read'=>1, 'write' => 0 },
93 Headers => { 'read'=>1, 'write' => 0 },
94 Filename => { 'read'=>1, 'write' => 0 },
95 Creator => { 'read'=>1, 'auto'=>1, },
96 Created => { 'read'=>1, 'auto'=>1, },
102 Create a new attachment. Takes a paramhash:
104 'Attachment' Should be a single MIME body with optional subparts
105 'Parent' is an optional id of the parent attachment
106 'TransactionId' is the mandatory id of the transaction this attachment is associated with.;
112 my %args = ( id => 0,
118 # For ease of reference
119 my $Attachment = $args{'Attachment'};
121 # if we didn't specify a ticket, we need to bail
122 unless ( $args{'TransactionId'} ) {
123 $RT::Logger->crit( "RT::Attachment->Create couldn't, as you didn't specify a transaction" );
127 # If we possibly can, collapse it to a singlepart
128 $Attachment->make_singlepart;
130 my $head = $Attachment->head;
133 my $Subject = Encode::decode( 'UTF-8', $head->get( 'subject' ) );
134 $Subject = '' unless defined $Subject;
138 my $MessageId = Encode::decode( "UTF-8", $head->get( 'Message-ID' ) );
139 defined($MessageId) or $MessageId = '';
141 $MessageId =~ s/^<(.*?)>$/$1/o;
144 my $Filename = mime_recommended_filename($Attachment);
147 $Filename =~ s!.*/!! if $Filename;
150 unless ( $head->get('Content-Length') ) {
152 $length = length $Attachment->bodyhandle->as_string
153 if defined $Attachment->bodyhandle;
154 $head->replace( 'Content-Length' => Encode::encode( "UTF-8", $length ) );
156 $head = $head->as_string;
158 # MIME::Head doesn't support perl strings well and can return
159 # octets which later will be double encoded in low-level code
160 $head = Encode::decode( 'UTF-8', $head );
162 # If a message has no bodyhandle, that means that it has subparts (or appears to)
163 # and we should act accordingly.
164 unless ( defined $Attachment->bodyhandle ) {
165 my ($id) = $self->SUPER::Create(
166 TransactionId => $args{'TransactionId'},
167 Parent => $args{'Parent'},
168 ContentType => $Attachment->mime_type,
170 MessageId => $MessageId,
175 $RT::Logger->crit("Attachment insert failed - ". $RT::Handle->dbh->errstr);
179 foreach my $part ( $Attachment->parts ) {
180 my $SubAttachment = RT::Attachment->new( $self->CurrentUser );
181 my ($id) = $SubAttachment->Create(
182 TransactionId => $args{'TransactionId'},
187 $RT::Logger->crit("Attachment insert failed: ". $RT::Handle->dbh->errstr);
194 #If it's not multipart
197 my ( $encoding, $type, $note_args );
198 ( $encoding, $content, $type, $Filename, $note_args ) =
199 $self->_EncodeLOB( $Attachment->bodyhandle->as_string, $Attachment->mime_type, $Filename, );
201 my $id = $self->SUPER::Create(
202 TransactionId => $args{'TransactionId'},
203 ContentType => $type,
204 ContentEncoding => $encoding,
205 Parent => $args{'Parent'},
209 Filename => $Filename,
210 MessageId => $MessageId,
215 $self->TransactionObj->Object->_NewTransaction( %$note_args );
219 $RT::Logger->crit("Attachment insert failed: ". $RT::Handle->dbh->errstr);
225 =head2 TransactionObj
227 Returns the transaction object asscoiated with this attachment.
234 unless ( $self->{_TransactionObj} ) {
235 $self->{_TransactionObj} = RT::Transaction->new( $self->CurrentUser );
236 $self->{_TransactionObj}->Load( $self->TransactionId );
239 unless ($self->{_TransactionObj}->Id) {
240 $RT::Logger->crit( "Attachment ". $self->id
241 ." can't find transaction ". $self->TransactionId
242 ." which it is ostensibly part of. That's bad");
244 return $self->{_TransactionObj};
249 Returns a parent's L<RT::Attachment> object if this attachment
250 has a parent, otherwise returns undef.
256 return undef unless $self->Parent;
258 my $parent = RT::Attachment->new( $self->CurrentUser );
259 $parent->LoadById( $self->Parent );
265 Takes a MIME type as a string or regex. Returns an L<RT::Attachment> object
266 for the nearest containing part with a matching L</ContentType>. Strings must
267 match exactly and all matches are done case insensitively. Strings ending in a
268 C</> must only match the first part of the MIME type. For example:
270 # Find the nearest multipart/* container
271 my $container = $attachment->Closest("multipart/");
273 Returns undef if no such object is found.
280 my $part = $self->ParentObj or return undef;
282 $type = qr/^\Q$type\E$/
283 unless ref $type eq "REGEX";
285 while (lc($part->ContentType) !~ $type) {
286 $part = $part->ParentObj or last;
289 return ($part and $part->id) ? $part : undef;
294 Returns an L<RT::Attachments> object which is preloaded with
295 all attachments objects with this attachment's Id as their
303 my $kids = RT::Attachments->new( $self->CurrentUser );
304 $kids->ChildrenOf( $self->Id );
310 Returns an L<RT::Attachments> object containing all the attachments sharing
311 the same immediate parent as the current object, excluding the current
314 If the current attachment is a top-level part (i.e. Parent == 0) then a
315 guaranteed empty L<RT::Attachments> object is returned.
321 my $siblings = RT::Attachments->new( $self->CurrentUser );
323 $siblings->ChildrenOf( $self->Parent );
324 $siblings->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
327 $siblings->Limit( SUBCLAUSE => 'empty', FIELD => 'id', VALUE => 0 );
334 Returns the attachment's content. if it's base64 encoded, decode it
341 return $self->_DecodeLOB(
342 $self->GetHeader('Content-Type'), # Includes charset, unlike ->ContentType
343 $self->ContentEncoding,
344 $self->_Value('Content', decode_utf8 => 0),
348 =head2 OriginalContent
350 Returns the attachment's content as octets before RT's mangling.
351 Generally this just means restoring text content back to its
354 If the attachment has a C<message/*> Content-Type, its children attachments
355 are reconstructed and returned as a string.
359 sub OriginalContent {
362 # message/* content types represent raw messages. Since we break them
363 # apart when they come in, we'll reconstruct their child attachments when
364 # you ask for the OriginalContent of the message/ part.
365 if ($self->IsMessageContentType) {
366 # There shouldn't be more than one "subpart" to a message/* attachment
367 my $child = $self->Children->First;
368 return $self->Content unless $child and $child->id;
369 return $child->ContentAsMIME(Children => 1)->as_string;
372 return $self->Content unless RT::I18N::IsTextualContentType($self->ContentType);
375 if ( !$self->ContentEncoding || $self->ContentEncoding eq 'none' ) {
376 $content = $self->_Value('Content', decode_utf8 => 0);
377 } elsif ( $self->ContentEncoding eq 'base64' ) {
378 $content = MIME::Base64::decode_base64($self->_Value('Content', decode_utf8 => 0));
379 } elsif ( $self->ContentEncoding eq 'quoted-printable' ) {
380 $content = MIME::QuotedPrint::decode($self->_Value('Content', decode_utf8 => 0));
382 return( $self->loc("Unknown ContentEncoding [_1]", $self->ContentEncoding));
385 my $entity = MIME::Entity->new();
386 $entity->head->add("Content-Type", $self->GetHeader("Content-Type"));
387 $entity->bodyhandle( MIME::Body::Scalar->new( $content ) );
388 my $from = RT::I18N::_FindOrGuessCharset($entity);
389 $from = 'utf-8' if not $from or not Encode::find_encoding($from);
391 my $to = RT::I18N::_CanonicalizeCharset(
392 $self->OriginalEncoding || 'utf-8'
396 eval { Encode::from_to($content, $from => $to) };
398 $RT::Logger->error("Could not convert attachment from $from to $to: ".$@);
403 =head2 OriginalEncoding
405 Returns the attachment's original encoding.
409 sub OriginalEncoding {
411 return $self->GetHeader('X-RT-Original-Encoding');
416 Returns length of L</Content> in bytes.
423 return undef unless $self->TransactionObj->CurrentUserCanSee;
425 my $len = $self->GetHeader('Content-Length');
426 unless ( defined $len ) {
428 no warnings 'uninitialized';
429 $len = length($self->Content) || 0;
430 $self->SetHeader('Content-Length' => $len);
435 =head2 FriendlyContentLength
437 Returns L</ContentLength> in bytes, kilobytes, or megabytes as most
438 appropriate. The size is suffixed with C<MiB>, C<KiB>, or C<B> and the returned
441 Returns the empty string if the L</ContentLength> is 0 or undefined.
445 sub FriendlyContentLength {
447 my $size = $self->ContentLength;
448 return '' unless $size;
451 if ( $size > 1024*1024 ) {
452 $res = $self->loc( "[_1]MiB", int( $size / 1024 / 102.4 ) / 10 );
454 elsif ( $size > 1024 ) {
455 $res = $self->loc( "[_1]KiB", int( $size / 102.4 ) / 10 );
458 $res = $self->loc( "[_1]B", $size );
463 =head2 ContentAsMIME [Children => 1]
465 Returns MIME entity built from this attachment.
467 If the optional parameter C<Children> is set to a true value, the children are
468 recursively added to the entity.
479 my $entity = MIME::Entity->new();
480 foreach my $header ($self->SplitHeaders) {
481 my ($h_key, $h_val) = split /:/, $header, 2;
482 $entity->head->add( $h_key, RT::Interface::Email::EncodeToMIME( String => $h_val ) );
485 # since we want to return original content, let's use original encoding
486 $entity->head->mime_attr(
487 "Content-Type.charset" => $self->OriginalEncoding )
488 if $self->OriginalEncoding;
491 MIME::Body::Scalar->new( $self->OriginalContent )
494 if ($opts{'Children'} and not $self->IsMessageContentType) {
495 my $children = $self->Children;
496 while (my $child = $children->Next) {
497 $entity->make_multipart unless $entity->is_multipart;
498 $entity->add_part( $child->ContentAsMIME(%opts) );
505 =head2 IsMessageContentType
507 Returns a boolean indicating if the Content-Type of this attachment is a
512 sub IsMessageContentType {
514 return $self->ContentType =~ m{^\s*message/}i ? 1 : 0;
519 Returns a hashref of all addresses related to this attachment.
520 The keys of the hash are C<From>, C<To>, C<Cc>, C<Bcc>, C<RT-Send-Cc>
521 and C<RT-Send-Bcc>. The values are references to lists of
522 L<Email::Address> objects.
526 our @ADDRESS_HEADERS = qw(From To Cc Bcc RT-Send-Cc RT-Send-Bcc);
532 my $current_user_address = lc($self->CurrentUser->EmailAddress || '');
533 foreach my $hdr (@ADDRESS_HEADERS) {
535 my $line = $self->GetHeader($hdr);
537 foreach my $AddrObj ( Email::Address->parse( $line )) {
538 my $address = $AddrObj->address;
539 $address = lc RT::User->CanonicalizeEmailAddress($address);
540 next if $current_user_address eq $address;
541 next if RT::EmailParser->IsRTAddress($address);
542 push @Addresses, $AddrObj ;
544 $data{$hdr} = \@Addresses;
551 Returns a multi-line string of the To, From, Cc, Date and Subject headers.
558 my @hdrs = $self->_SplitHeaders;
559 while (my $str = shift @hdrs) {
560 next unless $str =~ /^(To|From|RT-Send-Cc|Cc|Bcc|Date|Subject):/i;
561 $hdrs .= $str . "\n";
562 $hdrs .= shift( @hdrs ) . "\n" while ($hdrs[0] =~ /^[ \t]+/);
569 Returns this object's headers as a string. This method specifically
570 removes the RT-Send-Bcc: header, so as to never reveal to whom RT sent a Bcc.
571 We need to record the RT-Send-Cc and RT-Send-Bcc values so that we can actually send
572 out mail. The mailing rules are separated from the ticket update code by
573 an abstraction barrier that makes it impossible to pass this data directly.
578 return join("\n", $_[0]->SplitHeaders);
581 =head2 EncodedHeaders
583 Takes encoding as argument and returns the attachment's headers as octets in encoded
586 This is not protection using quoted printable or base64 encoding.
592 my $encoding = shift || 'utf8';
593 return Encode::encode( $encoding, $self->Headers );
596 =head2 GetHeader $TAG
598 Returns the value of the header Tag as a string. This bypasses the weeding out
599 done in Headers() above.
606 foreach my $line ($self->_SplitHeaders) {
607 next unless $line =~ /^\Q$tag\E:\s+(.*)$/si;
609 #if we find the header, return its value
613 # we found no header. return an empty string
617 =head2 DelHeader $TAG
619 Delete a field from the attachment's headers.
628 foreach my $line ($self->_SplitHeaders) {
629 next if $line =~ /^\Q$tag\E:\s+/i;
630 $newheader .= "$line\n";
632 return $self->__Set( Field => 'Headers', Value => $newheader);
635 =head2 AddHeader $TAG, $VALUE, ...
637 Add one or many fields to the attachment's headers.
644 my $newheader = $self->__Value( 'Headers' );
645 while ( my ($tag, $value) = splice @_, 0, 2 ) {
646 $value = $self->_CanonicalizeHeaderValue($value);
647 $newheader .= "$tag: $value\n";
649 return $self->__Set( Field => 'Headers', Value => $newheader);
652 =head2 SetHeader ( 'Tag', 'Value' )
654 Replace or add a Header to the attachment's headers.
661 my $value = $self->_CanonicalizeHeaderValue(shift);
665 foreach my $line ( $self->_SplitHeaders ) {
666 if ( $line =~ /^\Q$tag\E:\s+/i ) {
667 # replace first instance, skip all the rest
669 $newheader .= "$tag: $value\n";
673 $newheader .= "$line\n";
677 $newheader .= "$tag: $value\n" unless $replaced;
678 $self->__Set( Field => 'Headers', Value => $newheader);
681 sub _CanonicalizeHeaderValue {
685 $value = '' unless defined $value;
687 $value =~ s/\r*\n/\n /g;
694 Returns an array of this attachment object's headers, with one header
695 per array entry. Multiple lines are folded.
697 B<Never> returns C<RT-Send-Bcc> field.
703 return (grep !/^RT-Send-Bcc/i, $self->_SplitHeaders(@_) );
708 Returns an array of this attachment object's headers, with one header
709 per array entry. multiple lines are folded.
716 my $headers = (shift || $self->_Value('Headers'));
718 # XXX TODO: splitting on \n\w is _wrong_ as it treats \n[ as a valid
719 # continuation, which it isn't. The correct split pattern, per RFC 2822,
720 # is /\n(?=[^ \t]|\z)/. That is, only "\n " or "\n\t" is a valid
721 # continuation. Older values of X-RT-GnuPG-Status contain invalid
722 # continuations and rely on this bogus split pattern, however, so it is
723 # left as-is for now.
724 for (split(/\n(?=\w|\z)/,$headers)) {
735 my $txn = $self->TransactionObj;
736 return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
737 return (0, $self->loc('Permission Denied'))
738 unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
739 return (0, $self->loc('Cryptography is disabled'))
740 unless RT->Config->Get('Crypt')->{'Enable'};
741 return (0, $self->loc('Attachments encryption is disabled'))
742 unless RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'};
744 my $type = $self->ContentType;
745 if ( $type =~ /^x-application-rt\/[^-]+-encrypted/i ) {
746 return (1, $self->loc('Already encrypted'));
747 } elsif ( $type =~ /^multipart\//i ) {
748 return (1, $self->loc('No need to encrypt'));
751 my $queue = $txn->TicketObj->QueueObj;
753 foreach my $address ( grep $_,
754 $queue->CorrespondAddress,
755 $queue->CommentAddress,
756 RT->Config->Get('CorrespondAddress'),
757 RT->Config->Get('CommentAddress'),
759 my %res = RT::Crypt->GetKeysInfo( Key => $address, Type => 'private' );
760 next if $res{'exit_code'} || !$res{'info'};
761 %res = RT::Crypt->GetKeysForEncryption( $address );
762 next if $res{'exit_code'} || !$res{'info'};
763 $encrypt_for = $address;
765 unless ( $encrypt_for ) {
766 return (0, $self->loc('No key suitable for encryption'));
769 my $content = $self->Content;
770 my %res = RT::Crypt->SignEncryptContent(
771 Content => \$content,
774 Recipients => [ $encrypt_for ],
776 if ( $res{'exit_code'} ) {
777 return (0, $self->loc('Encryption error; contact the administrator'));
780 my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
782 return ($status, $self->loc("Couldn't replace content with encrypted data: [_1]", $msg));
785 $type = qq{x-application-rt\/$res{'Protocol'}-encrypted; original-type="$type"};
786 $self->__Set( Field => 'ContentType', Value => $type );
787 $self->SetHeader( 'Content-Type' => $type );
789 return (1, $self->loc('Successfuly encrypted data'));
795 my $txn = $self->TransactionObj;
796 return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
797 return (0, $self->loc('Permission Denied'))
798 unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
799 return (0, $self->loc('Cryptography is disabled'))
800 unless RT->Config->Get('Crypt')->{'Enable'};
802 my $type = $self->ContentType;
804 if ( $type =~ /^x-application-rt\/([^-]+)-encrypted/i ) {
806 $protocol =~ s/gpg/gnupg/; # backwards compatibility
807 ($type) = ($type =~ /original-type="(.*)"/i);
808 $type ||= 'application/octet-stream';
810 return (1, $self->loc('Is not encrypted'));
813 my $queue = $txn->TicketObj->QueueObj;
815 $queue->CorrespondAddress,
816 $queue->CommentAddress,
817 RT->Config->Get('CorrespondAddress'),
818 RT->Config->Get('CommentAddress')
821 my $content = $self->Content;
822 my %res = RT::Crypt->DecryptContent(
823 Protocol => $protocol,
824 Content => \$content,
825 Recipients => \@addresses,
827 if ( $res{'exit_code'} ) {
828 return (0, $self->loc('Decryption error; contact the administrator'));
831 my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
833 return ($status, $self->loc("Couldn't replace content with decrypted data: [_1]", $msg));
835 $self->__Set( Field => 'ContentType', Value => $type );
836 $self->SetHeader( 'Content-Type' => $type );
838 return (1, $self->loc('Successfuly decrypted data'));
843 Takes the name of a table column.
844 Returns its value as a string, if the user passes an ACL check
852 #if the field is public, return it.
853 if ( $self->_Accessible( $field, 'public' ) ) {
854 return ( $self->__Value( $field, @_ ) );
857 return undef unless $self->TransactionObj->CurrentUserCanSee;
858 return $self->__Value( $field, @_ );
861 # Transactions don't change. by adding this cache congif directiove,
862 # we don't lose pathalogically on long tickets.
866 'fast_update_p' => 1,
867 'cache_for_sec' => 180,
876 Returns the current value of id.
877 (In the database, id is stored as int(11).)
885 Returns the current value of TransactionId.
886 (In the database, TransactionId is stored as int(11).)
890 =head2 SetTransactionId VALUE
893 Set TransactionId to VALUE.
894 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
895 (In the database, TransactionId will be stored as a int(11).)
903 Returns the current value of Parent.
904 (In the database, Parent is stored as int(11).)
908 =head2 SetParent VALUE
912 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
913 (In the database, Parent will be stored as a int(11).)
921 Returns the current value of MessageId.
922 (In the database, MessageId is stored as varchar(160).)
926 =head2 SetMessageId VALUE
929 Set MessageId to VALUE.
930 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
931 (In the database, MessageId will be stored as a varchar(160).)
939 Returns the current value of Subject.
940 (In the database, Subject is stored as varchar(255).)
944 =head2 SetSubject VALUE
947 Set Subject to VALUE.
948 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
949 (In the database, Subject will be stored as a varchar(255).)
957 Returns the current value of Filename.
958 (In the database, Filename is stored as varchar(255).)
962 =head2 SetFilename VALUE
965 Set Filename to VALUE.
966 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
967 (In the database, Filename will be stored as a varchar(255).)
975 Returns the current value of ContentType.
976 (In the database, ContentType is stored as varchar(80).)
980 =head2 SetContentType VALUE
983 Set ContentType to VALUE.
984 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
985 (In the database, ContentType will be stored as a varchar(80).)
991 =head2 ContentEncoding
993 Returns the current value of ContentEncoding.
994 (In the database, ContentEncoding is stored as varchar(80).)
998 =head2 SetContentEncoding VALUE
1001 Set ContentEncoding to VALUE.
1002 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1003 (In the database, ContentEncoding will be stored as a varchar(80).)
1011 Returns the current value of Content.
1012 (In the database, Content is stored as longblob.)
1016 =head2 SetContent VALUE
1019 Set Content to VALUE.
1020 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1021 (In the database, Content will be stored as a longblob.)
1029 Returns the current value of Headers.
1030 (In the database, Headers is stored as longtext.)
1034 =head2 SetHeaders VALUE
1037 Set Headers to VALUE.
1038 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1039 (In the database, Headers will be stored as a longtext.)
1047 Returns the current value of Creator.
1048 (In the database, Creator is stored as int(11).)
1056 Returns the current value of Created.
1057 (In the database, Created is stored as datetime.)
1064 sub _CoreAccessible {
1068 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
1070 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
1072 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1074 {read => 1, write => 1, sql_type => 12, length => 160, is_blob => 0, is_numeric => 0, type => 'varchar(160)', default => ''},
1076 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
1078 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
1080 {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
1082 {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
1084 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longblob', default => ''},
1086 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
1088 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1090 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
1095 sub FindDependencies {
1097 my ($walker, $deps) = @_;
1099 $self->SUPER::FindDependencies($walker, $deps);
1100 $deps->Add( out => $self->TransactionObj );
1103 RT::Base->_ImportOverlays();