rt 4.0.20 (RT#13852)
[freeside.git] / rt / lib / RT / Template.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
6 #                                          <sales@bestpractical.com>
7 #
8 # (Except where explicitly superseded by other copyright notices)
9 #
10 #
11 # LICENSE:
12 #
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
16 # from www.gnu.org.
17 #
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.
22 #
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.
28 #
29 #
30 # CONTRIBUTION SUBMISSION POLICY:
31 #
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.)
37 #
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.
46 #
47 # END BPS TAGGED BLOCK }}}
48
49 # Portions Copyright 2000 Tobias Brox <tobix@cpan.org> 
50
51 =head1 NAME
52
53   RT::Template - RT's template object
54
55 =head1 SYNOPSIS
56
57   use RT::Template;
58
59 =head1 DESCRIPTION
60
61
62 =head1 METHODS
63
64
65 =cut
66
67
68 package RT::Template;
69
70 use strict;
71 use warnings;
72
73
74
75 use Text::Template;
76 use MIME::Entity;
77 use MIME::Parser;
78 use Scalar::Util 'blessed';
79
80 sub _Accessible {
81     my $self = shift;
82     my %Cols = (
83         id            => 'read',
84         Name          => 'read/write',
85         Description   => 'read/write',
86         Type          => 'read/write',    #Type is one of Perl or Simple
87         Content       => 'read/write',
88         Queue         => 'read/write',
89         Creator       => 'read/auto',
90         Created       => 'read/auto',
91         LastUpdatedBy => 'read/auto',
92         LastUpdated   => 'read/auto'
93     );
94     return $self->SUPER::_Accessible( @_, %Cols );
95 }
96
97 sub _Set {
98     my $self = shift;
99     my %args = (
100         Field => undef,
101         Value => undef,
102         @_,
103     );
104     
105     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
106         return ( 0, $self->loc('Permission Denied') );
107     }
108
109     if (exists $args{Value}) {
110         if ($args{Field} eq 'Queue') {
111             if ($args{Value}) {
112                 # moving to another queue
113                 my $queue = RT::Queue->new( $self->CurrentUser );
114                 $queue->Load($args{Value});
115                 unless ($queue->Id and $queue->CurrentUserHasRight('ModifyTemplate')) {
116                     return ( 0, $self->loc('Permission Denied') );
117                 }
118             } else {
119                 # moving to global
120                 unless ($self->CurrentUser->HasRight( Object => RT->System, Right => 'ModifyTemplate' )) {
121                     return ( 0, $self->loc('Permission Denied') );
122                 }
123             }
124         }
125     }
126
127     return $self->SUPER::_Set( @_ );
128 }
129
130 =head2 _Value
131
132 Takes the name of a table column. Returns its value as a string,
133 if the user passes an ACL check, otherwise returns undef.
134
135 =cut
136
137 sub _Value {
138     my $self  = shift;
139
140     unless ( $self->CurrentUserCanRead() ) {
141         return undef;
142     }
143     return $self->__Value( @_ );
144
145 }
146
147 =head2 Load <identifier>
148
149 Load a template, either by number or by name.
150
151 Note that loading templates by name using this method B<is
152 ambiguous>. Several queues may have template with the same name
153 and as well global template with the same name may exist.
154 Use L</LoadGlobalTemplate> and/or L<LoadQueueTemplate> to get
155 precise result.
156
157 =cut
158
159 sub Load {
160     my $self       = shift;
161     my $identifier = shift;
162     return undef unless $identifier;
163
164     if ( $identifier =~ /\D/ ) {
165         return $self->LoadByCol( 'Name', $identifier );
166     }
167     return $self->LoadById( $identifier );
168 }
169
170 =head2 LoadGlobalTemplate NAME
171
172 Load the global template with the name NAME
173
174 =cut
175
176 sub LoadGlobalTemplate {
177     my $self = shift;
178     my $name = shift;
179
180     return ( $self->LoadQueueTemplate( Queue => 0, Name => $name ) );
181 }
182
183 =head2 LoadQueueTemplate (Queue => QUEUEID, Name => NAME)
184
185 Loads the Queue template named NAME for Queue QUEUE.
186
187 Note that this method doesn't load a global template with the same name
188 if template in the queue doesn't exist. THe following code can be used:
189
190     $template->LoadQueueTemplate( Queue => $queue_id, Name => $template_name );
191     unless ( $template->id ) {
192         $template->LoadGlobalTemplate( $template_name );
193         unless ( $template->id ) {
194             # no template
195             ...
196         }
197     }
198     # ok, template either queue's or global
199     ...
200
201 =cut
202
203 sub LoadQueueTemplate {
204     my $self = shift;
205     my %args = (
206         Queue => undef,
207         Name  => undef,
208         @_
209     );
210
211     return ( $self->LoadByCols( Name => $args{'Name'}, Queue => $args{'Queue'} ) );
212
213 }
214
215 =head2 Create
216
217 Takes a paramhash of Content, Queue, Name and Description.
218 Name should be a unique string identifying this Template.
219 Description and Content should be the template's title and content.
220 Queue should be 0 for a global template and the queue # for a queue-specific 
221 template.
222
223 Returns the Template's id # if the create was successful. Returns undef for
224 unknown database failure.
225
226 =cut
227
228 sub Create {
229     my $self = shift;
230     my %args = (
231         Content     => undef,
232         Queue       => 0,
233         Description => '[no description]',
234         Type        => 'Perl',
235         Name        => undef,
236         @_
237     );
238
239     if ( $args{Type} eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System) ) {
240         return ( undef, $self->loc('Permission Denied') );
241     }
242
243     unless ( $args{'Queue'} ) {
244         unless ( $self->CurrentUser->HasRight(Right =>'ModifyTemplate', Object => $RT::System) ) {
245             return ( undef, $self->loc('Permission Denied') );
246         }
247         $args{'Queue'} = 0;
248     }
249     else {
250         my $QueueObj = RT::Queue->new( $self->CurrentUser );
251         $QueueObj->Load( $args{'Queue'} ) || return ( undef, $self->loc('Invalid queue') );
252     
253         unless ( $QueueObj->CurrentUserHasRight('ModifyTemplate') ) {
254             return ( undef, $self->loc('Permission Denied') );
255         }
256         $args{'Queue'} = $QueueObj->Id;
257     }
258
259     my ( $result, $msg ) = $self->SUPER::Create(
260         Content     => $args{'Content'},
261         Queue       => $args{'Queue'},
262         Description => $args{'Description'},
263         Name        => $args{'Name'},
264         Type        => $args{'Type'},
265     );
266
267     if ( wantarray ) {
268         return ( $result, $msg );
269     } else {
270         return ( $result );
271     }
272
273 }
274
275 =head2 Delete
276
277 Delete this template.
278
279 =cut
280
281 sub Delete {
282     my $self = shift;
283
284     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
285         return ( 0, $self->loc('Permission Denied') );
286     }
287
288     return ( $self->SUPER::Delete(@_) );
289 }
290
291 =head2 IsEmpty
292
293 Returns true value if content of the template is empty, otherwise
294 returns false.
295
296 =cut
297
298 sub IsEmpty {
299     my $self = shift;
300     my $content = $self->Content;
301     return 0 if defined $content && length $content;
302     return 1;
303 }
304
305 =head2 MIMEObj
306
307 Returns L<MIME::Entity> object parsed using L</Parse> method. Returns
308 undef if last call to L</Parse> failed or never be called.
309
310 Note that content of the template is UTF-8, but L<MIME::Parser> is not
311 good at handling it and all data of the entity should be treated as
312 octets and converted to perl strings using Encode::decode_utf8 or
313 something else.
314
315 =cut
316
317 sub MIMEObj {
318     my $self = shift;
319     return ( $self->{'MIMEObj'} );
320 }
321
322 =head2 Parse
323
324 This routine performs L<Text::Template> parsing on the template and then
325 imports the results into a L<MIME::Entity> so we can really use it. Use
326 L</MIMEObj> method to get the L<MIME::Entity> object.
327
328 Takes a hash containing Argument, TicketObj, and TransactionObj and other
329 arguments that will be available in the template's code. TicketObj and
330 TransactionObj are not mandatory, but highly recommended.
331
332 It returns a tuple of (val, message). If val is false, the message contains
333 an error message.
334
335 =cut
336
337 sub Parse {
338     my $self = shift;
339     my ($rv, $msg);
340
341
342     if (not $self->IsEmpty and $self->Content =~ m{^Content-Type:\s+text/html\b}im) {
343         local $RT::Transaction::PreferredContentType = 'text/html';
344         ($rv, $msg) = $self->_Parse(@_);
345     }
346     else {
347         ($rv, $msg) = $self->_Parse(@_);
348     }
349
350     return ($rv, $msg) unless $rv;
351
352     my $mime_type   = $self->MIMEObj->mime_type;
353     if (defined $mime_type and $mime_type eq 'text/html') {
354         $self->_DowngradeFromHTML(@_);
355     }
356
357     return ($rv, $msg);
358 }
359
360 sub _Parse {
361     my $self = shift;
362
363     # clear prev MIME object
364     $self->{'MIMEObj'} = undef;
365
366     #We're passing in whatever we were passed. it's destined for _ParseContent
367     my ($content, $msg) = $self->_ParseContent(@_);
368     return ( 0, $msg ) unless defined $content && length $content;
369
370     if ( $content =~ /^\S/s && $content !~ /^\S+:/ ) {
371         $RT::Logger->error(
372             "Template #". $self->id ." has leading line that doesn't"
373             ." look like header field, if you don't want to override"
374             ." any headers and don't want to see this error message"
375             ." then leave first line of the template empty"
376         );
377         $content = "\n".$content;
378     }
379
380     my $parser = MIME::Parser->new();
381     $parser->output_to_core(1);
382     $parser->tmp_to_core(1);
383     $parser->use_inner_files(1);
384
385     ### Should we forgive normally-fatal errors?
386     $parser->ignore_errors(1);
387     # MIME::Parser doesn't play well with perl strings
388     utf8::encode($content);
389     $self->{'MIMEObj'} = eval { $parser->parse_data( \$content ) };
390     if ( my $error = $@ || $parser->last_error ) {
391         $RT::Logger->error( "$error" );
392         return ( 0, $error );
393     }
394
395     # Unfold all headers
396     $self->{'MIMEObj'}->head->unfold;
397     $self->{'MIMEObj'}->head->modify(1);
398
399     return ( 1, $self->loc("Template parsed") );
400
401 }
402
403 # Perform Template substitutions on the template
404
405 sub _ParseContent {
406     my $self = shift;
407     my %args = (
408         Argument       => undef,
409         TicketObj      => undef,
410         TransactionObj => undef,
411         @_
412     );
413
414     unless ( $self->CurrentUserCanRead() ) {
415         return (undef, $self->loc("Permission Denied"));
416     }
417
418     if ( $self->IsEmpty ) {
419         return ( undef, $self->loc("Template is empty") );
420     }
421
422     my $content = $self->SUPER::_Value('Content');
423     # We need to untaint the content of the template, since we'll be working
424     # with it
425     $content =~ s/^(.*)$/$1/;
426
427     $args{'Ticket'} = delete $args{'TicketObj'} if $args{'TicketObj'};
428     $args{'Transaction'} = delete $args{'TransactionObj'} if $args{'TransactionObj'};
429     $args{'Requestor'} = eval { $args{'Ticket'}->Requestors->UserMembersObj->First->Name }
430         if $args{'Ticket'};
431     $args{'rtname'}    = RT->Config->Get('rtname');
432     if ( $args{'Ticket'} ) {
433         my $t = $args{'Ticket'}; # avoid memory leak
434         $args{'loc'} = sub { $t->loc(@_) };
435     } else {
436         $args{'loc'} = sub { $self->loc(@_) };
437     }
438
439     if ($self->Type eq 'Perl') {
440         return $self->_ParseContentPerl(
441             Content      => $content,
442             TemplateArgs => \%args,
443         );
444     }
445     else {
446         return $self->_ParseContentSimple(
447             Content      => $content,
448             TemplateArgs => \%args,
449         );
450     }
451 }
452
453 # uses Text::Template for Perl templates
454 sub _ParseContentPerl {
455     my $self = shift;
456     my %args = (
457         Content      => undef,
458         TemplateArgs => {},
459         @_,
460     );
461
462     foreach my $key ( keys %{ $args{TemplateArgs} } ) {
463         my $val = $args{TemplateArgs}{ $key };
464         next unless ref $val;
465         next if ref($val) =~ /^(ARRAY|HASH|SCALAR|CODE)$/;
466         $args{TemplateArgs}{ $key } = \$val;
467     }
468
469     my $template = Text::Template->new(
470         TYPE   => 'STRING',
471         SOURCE => $args{Content},
472     );
473     my ($ok) = $template->compile;
474     unless ($ok) {
475         $RT::Logger->error("Template parsing error in @{[$self->Name]} (#@{[$self->id]}): $Text::Template::ERROR");
476         return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) );
477     }
478
479     my $is_broken = 0;
480     my $retval = $template->fill_in(
481         HASH => $args{TemplateArgs},
482         BROKEN => sub {
483             my (%args) = @_;
484             $RT::Logger->error("Template parsing error: $args{error}")
485                 unless $args{error} =~ /^Died at /; # ignore intentional die()
486             $is_broken++;
487             return undef;
488         },
489     );
490     return ( undef, $self->loc('Template parsing error') ) if $is_broken;
491
492     return ($retval);
493 }
494
495 sub _ParseContentSimple {
496     my $self = shift;
497     my %args = (
498         Content      => undef,
499         TemplateArgs => {},
500         @_,
501     );
502
503     $self->_MassageSimpleTemplateArgs(%args);
504
505     my $template = Text::Template->new(
506         TYPE   => 'STRING',
507         SOURCE => $args{Content},
508     );
509     my ($ok) = $template->compile;
510     return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok;
511
512     # copied from Text::Template::fill_in and refactored to be simple variable
513     # interpolation
514     my $fi_r = '';
515     foreach my $fi_item (@{$template->{SOURCE}}) {
516         my ($fi_type, $fi_text, $fi_lineno) = @$fi_item;
517         if ($fi_type eq 'TEXT') {
518             $fi_r .= $fi_text;
519         } elsif ($fi_type eq 'PROG') {
520             my $fi_res;
521             my $original_fi_text = $fi_text;
522
523             # strip surrounding whitespace for simpler regexes
524             $fi_text =~ s/^\s+//;
525             $fi_text =~ s/\s+$//;
526
527             # if the codeblock is a simple $Variable lookup, use the value from
528             # the TemplateArgs hash...
529             if (my ($var) = $fi_text =~ /^\$(\w+)$/) {
530                 if (exists $args{TemplateArgs}{$var}) {
531                     $fi_res = $args{TemplateArgs}{$var};
532                 }
533             }
534
535             # if there was no substitution then just reinsert the codeblock
536             if (!defined $fi_res) {
537                 $fi_res = "{$original_fi_text}";
538             }
539
540             # If the value of the filled-in text really was undef,
541             # change it to an explicit empty string to avoid undefined
542             # value warnings later.
543             $fi_res = '' unless defined $fi_res;
544
545             $fi_r .= $fi_res;
546         }
547     }
548
549     return $fi_r;
550 }
551
552 sub _MassageSimpleTemplateArgs {
553     my $self = shift;
554     my %args = (
555         TemplateArgs => {},
556         @_,
557     );
558
559     my $template_args = $args{TemplateArgs};
560
561     if (my $ticket = $template_args->{Ticket}) {
562         for my $column (qw/Id Subject Type InitialPriority FinalPriority Priority TimeEstimated TimeWorked Status TimeLeft Told Starts Started Due Resolved RequestorAddresses AdminCcAddresses CcAddresses/) {
563             $template_args->{"Ticket".$column} = $ticket->$column;
564         }
565
566         $template_args->{"TicketQueueId"}   = $ticket->Queue;
567         $template_args->{"TicketQueueName"} = $ticket->QueueObj->Name;
568
569         $template_args->{"TicketOwnerId"}    = $ticket->Owner;
570         $template_args->{"TicketOwnerName"}  = $ticket->OwnerObj->Name;
571         $template_args->{"TicketOwnerEmailAddress"} = $ticket->OwnerObj->EmailAddress;
572
573         my $cfs = $ticket->CustomFields;
574         while (my $cf = $cfs->Next) {
575             $template_args->{"TicketCF" . $cf->Name} = $ticket->CustomFieldValuesAsString($cf->Name);
576         }
577     }
578
579     if (my $txn = $template_args->{Transaction}) {
580         for my $column (qw/Id TimeTaken Type Field OldValue NewValue Data Content Subject Description BriefDescription/) {
581             $template_args->{"Transaction".$column} = $txn->$column;
582         }
583
584         my $cfs = $txn->CustomFields;
585         while (my $cf = $cfs->Next) {
586             $template_args->{"TransactionCF" . $cf->Name} = $txn->CustomFieldValuesAsString($cf->Name);
587         }
588     }
589 }
590
591 sub _DowngradeFromHTML {
592     my $self = shift;
593     my $orig_entity = $self->MIMEObj;
594
595     my $new_entity = $orig_entity->dup; # this will fail badly if we go away from InCore parsing
596     $new_entity->head->mime_attr( "Content-Type" => 'text/plain' );
597     $new_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' );
598
599     $orig_entity->head->mime_attr( "Content-Type" => 'text/html' );
600     $orig_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' );
601     $orig_entity->make_multipart('alternative', Force => 1);
602
603     require HTML::FormatText;
604     require HTML::TreeBuilder;
605     require Encode;
606     # need to decode_utf8, see the doc of MIMEObj method
607     my $tree = HTML::TreeBuilder->new_from_content(
608         Encode::decode_utf8($new_entity->bodyhandle->as_string)
609     );
610     $new_entity->bodyhandle(MIME::Body::InCore->new(
611         \(scalar HTML::FormatText->new(
612             leftmargin  => 0,
613             rightmargin => 78,
614         )->format( $tree ))
615     ));
616     $tree->delete;
617
618     $orig_entity->add_part($new_entity, 0); # plain comes before html
619     $self->{MIMEObj} = $orig_entity;
620
621     return;
622 }
623
624 =head2 CurrentUserHasQueueRight
625
626 Helper function to call the template's queue's CurrentUserHasQueueRight with the passed in args.
627
628 =cut
629
630 sub CurrentUserHasQueueRight {
631     my $self = shift;
632     return ( $self->QueueObj->CurrentUserHasRight(@_) );
633 }
634
635 =head2 SetType
636
637 If setting Type to Perl, require the ExecuteCode right.
638
639 =cut
640
641 sub SetType {
642     my $self    = shift;
643     my $NewType = shift;
644
645     if ($NewType eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
646         return ( undef, $self->loc('Permission Denied') );
647     }
648
649     return $self->_Set( Field => 'Type', Value => $NewType );
650 }
651
652 =head2 SetContent
653
654 If changing content and the type is Perl, require the ExecuteCode right.
655
656 =cut
657
658 sub SetContent {
659     my $self       = shift;
660     my $NewContent = shift;
661
662     if ($self->Type eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
663         return ( undef, $self->loc('Permission Denied') );
664     }
665
666     return $self->_Set( Field => 'Content', Value => $NewContent );
667 }
668
669 sub _UpdateAttributes {
670     my $self = shift;
671     my %args = (
672         NewValues => {},
673         @_,
674     );
675
676     my $type = $args{NewValues}{Type} || $self->Type;
677
678     # forbid updating content when the (possibly new) value of Type is Perl
679     if ($type eq 'Perl' && exists $args{NewValues}{Content}) {
680         if (!$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
681             return $self->loc('Permission Denied');
682         }
683     }
684
685     return $self->SUPER::_UpdateAttributes(%args);
686 }
687
688 =head2 CompileCheck
689
690 If the template's Type is Perl, then compile check all the codeblocks to see if
691 they are syntactically valid. We eval them in a codeblock to avoid actually
692 executing the code.
693
694 Returns an (ok, message) pair.
695
696 =cut
697
698 sub CompileCheck {
699     my $self = shift;
700
701     return (1, $self->loc("Template does not include Perl code"))
702         unless $self->Type eq 'Perl';
703
704     my $content = $self->Content;
705     $content = '' if !defined($content);
706
707     my $template = Text::Template->new(
708         TYPE   => 'STRING',
709         SOURCE => $content,
710     );
711     my ($ok) = $template->compile;
712     return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok;
713
714     # copied from Text::Template::fill_in and refactored to be compile checks
715     foreach my $fi_item (@{$template->{SOURCE}}) {
716         my ($fi_type, $fi_text, $fi_lineno) = @$fi_item;
717         next unless $fi_type eq 'PROG';
718
719         do {
720             no strict 'vars';
721             eval "sub { $fi_text }";
722         };
723         next if !$@;
724
725         my $error = $@;
726
727         # provide a (hopefully) useful line number for the error, but clean up
728         # all the other extraneous garbage
729         $error =~ s/\(eval \d+\) line (\d+).*/"template line " . ($1+$fi_lineno-1)/es;
730
731         return (0, $self->loc("Couldn't compile template codeblock '[_1]': [_2]", $fi_text, $error));
732     }
733
734     return (1, $self->loc("Template compiles"));
735 }
736
737 =head2 CurrentUserCanRead
738
739 =cut
740
741 sub CurrentUserCanRead {
742     my $self =shift;
743
744     return 1 if $self->CurrentUserHasQueueRight('ShowTemplate');
745
746     return $self->CurrentUser->HasRight( Right =>'ShowGlobalTemplates', Object => $RT::System )
747         if !$self->QueueObj->Id;
748
749     return;
750 }
751
752 1;
753
754 use RT::Queue;
755 use base 'RT::Record';
756
757 sub Table {'Templates'}
758
759
760
761
762
763
764 =head2 id
765
766 Returns the current value of id.
767 (In the database, id is stored as int(11).)
768
769
770 =cut
771
772
773 =head2 Queue
774
775 Returns the current value of Queue.
776 (In the database, Queue is stored as int(11).)
777
778
779
780 =head2 SetQueue VALUE
781
782
783 Set Queue to VALUE.
784 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
785 (In the database, Queue will be stored as a int(11).)
786
787
788 =cut
789
790
791 =head2 QueueObj
792
793 Returns the Queue Object which has the id returned by Queue
794
795
796 =cut
797
798 sub QueueObj {
799         my $self = shift;
800         my $Queue =  RT::Queue->new($self->CurrentUser);
801         $Queue->Load($self->__Value('Queue'));
802         return($Queue);
803 }
804
805 =head2 Name
806
807 Returns the current value of Name.
808 (In the database, Name is stored as varchar(200).)
809
810
811
812 =head2 SetName VALUE
813
814
815 Set Name to VALUE.
816 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
817 (In the database, Name will be stored as a varchar(200).)
818
819
820 =cut
821
822
823 =head2 Description
824
825 Returns the current value of Description.
826 (In the database, Description is stored as varchar(255).)
827
828
829
830 =head2 SetDescription VALUE
831
832
833 Set Description to VALUE.
834 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
835 (In the database, Description will be stored as a varchar(255).)
836
837
838 =cut
839
840
841 =head2 Type
842
843 Returns the current value of Type.
844 (In the database, Type is stored as varchar(16).)
845
846
847
848 =head2 SetType VALUE
849
850
851 Set Type to VALUE.
852 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
853 (In the database, Type will be stored as a varchar(16).)
854
855
856 =cut
857
858
859 =head2 Language
860
861 Returns the current value of Language.
862 (In the database, Language is stored as varchar(16).)
863
864
865
866 =head2 SetLanguage VALUE
867
868
869 Set Language to VALUE.
870 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
871 (In the database, Language will be stored as a varchar(16).)
872
873
874 =cut
875
876
877 =head2 TranslationOf
878
879 Returns the current value of TranslationOf.
880 (In the database, TranslationOf is stored as int(11).)
881
882
883
884 =head2 SetTranslationOf VALUE
885
886
887 Set TranslationOf to VALUE.
888 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
889 (In the database, TranslationOf will be stored as a int(11).)
890
891
892 =cut
893
894
895 =head2 Content
896
897 Returns the current value of Content.
898 (In the database, Content is stored as text.)
899
900
901
902 =head2 SetContent VALUE
903
904
905 Set Content to VALUE.
906 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
907 (In the database, Content will be stored as a text.)
908
909
910 =cut
911
912
913 =head2 LastUpdated
914
915 Returns the current value of LastUpdated.
916 (In the database, LastUpdated is stored as datetime.)
917
918
919 =cut
920
921
922 =head2 LastUpdatedBy
923
924 Returns the current value of LastUpdatedBy.
925 (In the database, LastUpdatedBy is stored as int(11).)
926
927
928 =cut
929
930
931 =head2 Creator
932
933 Returns the current value of Creator.
934 (In the database, Creator is stored as int(11).)
935
936
937 =cut
938
939
940 =head2 Created
941
942 Returns the current value of Created.
943 (In the database, Created is stored as datetime.)
944
945
946 =cut
947
948
949
950 sub _CoreAccessible {
951     {
952
953         id =>
954                 {read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
955         Queue =>
956                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
957         Name =>
958                 {read => 1, write => 1, sql_type => 12, length => 200,  is_blob => 0,  is_numeric => 0,  type => 'varchar(200)', default => ''},
959         Description =>
960                 {read => 1, write => 1, sql_type => 12, length => 255,  is_blob => 0,  is_numeric => 0,  type => 'varchar(255)', default => ''},
961         Type =>
962                 {read => 1, write => 1, sql_type => 12, length => 16,  is_blob => 0,  is_numeric => 0,  type => 'varchar(16)', default => ''},
963         Language =>
964                 {read => 1, write => 1, sql_type => 12, length => 16,  is_blob => 0,  is_numeric => 0,  type => 'varchar(16)', default => ''},
965         TranslationOf =>
966                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
967         Content =>
968                 {read => 1, write => 1, sql_type => -4, length => 0,  is_blob => 1,  is_numeric => 0,  type => 'text', default => ''},
969         LastUpdated =>
970                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
971         LastUpdatedBy =>
972                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
973         Creator =>
974                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
975         Created =>
976                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
977
978  }
979 };
980
981 RT::Base->_ImportOverlays();
982
983 1;