rt 4.0.23
[freeside.git] / rt / lib / RT / Template.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2015 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 characters, but the contents of all
311 L<MIME::Entity> objects (including the one returned by this function,
312 are bytes in UTF-8.
313
314 =cut
315
316 sub MIMEObj {
317     my $self = shift;
318     return ( $self->{'MIMEObj'} );
319 }
320
321 =head2 Parse
322
323 This routine performs L<Text::Template> parsing on the template and then
324 imports the results into a L<MIME::Entity> so we can really use it. Use
325 L</MIMEObj> method to get the L<MIME::Entity> object.
326
327 Takes a hash containing Argument, TicketObj, and TransactionObj and other
328 arguments that will be available in the template's code. TicketObj and
329 TransactionObj are not mandatory, but highly recommended.
330
331 It returns a tuple of (val, message). If val is false, the message contains
332 an error message.
333
334 =cut
335
336 sub Parse {
337     my $self = shift;
338     my ($rv, $msg);
339
340
341     if (not $self->IsEmpty and $self->Content =~ m{^Content-Type:\s+text/html\b}im) {
342         local $RT::Transaction::PreferredContentType = 'text/html';
343         ($rv, $msg) = $self->_Parse(@_);
344     }
345     else {
346         ($rv, $msg) = $self->_Parse(@_);
347     }
348
349     return ($rv, $msg) unless $rv;
350
351     my $mime_type   = $self->MIMEObj->mime_type;
352     if (defined $mime_type and $mime_type eq 'text/html') {
353         $self->_DowngradeFromHTML(@_);
354     }
355
356     return ($rv, $msg);
357 }
358
359 sub _Parse {
360     my $self = shift;
361
362     # clear prev MIME object
363     $self->{'MIMEObj'} = undef;
364
365     #We're passing in whatever we were passed. it's destined for _ParseContent
366     my ($content, $msg) = $self->_ParseContent(@_);
367     return ( 0, $msg ) unless defined $content && length $content;
368
369     if ( $content =~ /^\S/s && $content !~ /^\S+:/ ) {
370         $RT::Logger->error(
371             "Template #". $self->id ." has leading line that doesn't"
372             ." look like header field, if you don't want to override"
373             ." any headers and don't want to see this error message"
374             ." then leave first line of the template empty"
375         );
376         $content = "\n".$content;
377     }
378
379     my $parser = MIME::Parser->new();
380     $parser->output_to_core(1);
381     $parser->tmp_to_core(1);
382     $parser->use_inner_files(1);
383
384     ### Should we forgive normally-fatal errors?
385     $parser->ignore_errors(1);
386     # Always provide bytes, not characters, to MIME objects
387     $content = Encode::encode( 'UTF-8', $content );
388     $self->{'MIMEObj'} = eval { $parser->parse_data( \$content ) };
389     if ( my $error = $@ || $parser->last_error ) {
390         $RT::Logger->error( "$error" );
391         return ( 0, $error );
392     }
393
394     # Unfold all headers
395     $self->{'MIMEObj'}->head->unfold;
396     $self->{'MIMEObj'}->head->modify(1);
397
398     return ( 1, $self->loc("Template parsed") );
399
400 }
401
402 # Perform Template substitutions on the template
403
404 sub _ParseContent {
405     my $self = shift;
406     my %args = (
407         Argument       => undef,
408         TicketObj      => undef,
409         TransactionObj => undef,
410         @_
411     );
412
413     unless ( $self->CurrentUserCanRead() ) {
414         return (undef, $self->loc("Permission Denied"));
415     }
416
417     if ( $self->IsEmpty ) {
418         return ( undef, $self->loc("Template is empty") );
419     }
420
421     my $content = $self->SUPER::_Value('Content');
422     # We need to untaint the content of the template, since we'll be working
423     # with it
424     $content =~ s/^(.*)$/$1/;
425
426     $args{'Ticket'} = delete $args{'TicketObj'} if $args{'TicketObj'};
427     $args{'Transaction'} = delete $args{'TransactionObj'} if $args{'TransactionObj'};
428     $args{'Requestor'} = eval { $args{'Ticket'}->Requestors->UserMembersObj->First->Name }
429         if $args{'Ticket'};
430     $args{'rtname'}    = RT->Config->Get('rtname');
431     if ( $args{'Ticket'} ) {
432         my $t = $args{'Ticket'}; # avoid memory leak
433         $args{'loc'} = sub { $t->loc(@_) };
434     } else {
435         $args{'loc'} = sub { $self->loc(@_) };
436     }
437
438     if ($self->Type eq 'Perl') {
439         return $self->_ParseContentPerl(
440             Content      => $content,
441             TemplateArgs => \%args,
442         );
443     }
444     else {
445         return $self->_ParseContentSimple(
446             Content      => $content,
447             TemplateArgs => \%args,
448         );
449     }
450 }
451
452 # uses Text::Template for Perl templates
453 sub _ParseContentPerl {
454     my $self = shift;
455     my %args = (
456         Content      => undef,
457         TemplateArgs => {},
458         @_,
459     );
460
461     foreach my $key ( keys %{ $args{TemplateArgs} } ) {
462         my $val = $args{TemplateArgs}{ $key };
463         next unless ref $val;
464         next if ref($val) =~ /^(ARRAY|HASH|SCALAR|CODE)$/;
465         $args{TemplateArgs}{ $key } = \$val;
466     }
467
468     my $template = Text::Template->new(
469         TYPE   => 'STRING',
470         SOURCE => $args{Content},
471     );
472     my ($ok) = $template->compile;
473     unless ($ok) {
474         $RT::Logger->error("Template parsing error in @{[$self->Name]} (#@{[$self->id]}): $Text::Template::ERROR");
475         return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) );
476     }
477
478     my $is_broken = 0;
479     my $retval = $template->fill_in(
480         HASH => $args{TemplateArgs},
481         BROKEN => sub {
482             my (%args) = @_;
483             $RT::Logger->error("Template parsing error: $args{error}")
484                 unless $args{error} =~ /^Died at /; # ignore intentional die()
485             $is_broken++;
486             return undef;
487         },
488     );
489     return ( undef, $self->loc('Template parsing error') ) if $is_broken;
490
491     return ($retval);
492 }
493
494 sub _ParseContentSimple {
495     my $self = shift;
496     my %args = (
497         Content      => undef,
498         TemplateArgs => {},
499         @_,
500     );
501
502     $self->_MassageSimpleTemplateArgs(%args);
503
504     my $template = Text::Template->new(
505         TYPE   => 'STRING',
506         SOURCE => $args{Content},
507     );
508     my ($ok) = $template->compile;
509     return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok;
510
511     # copied from Text::Template::fill_in and refactored to be simple variable
512     # interpolation
513     my $fi_r = '';
514     foreach my $fi_item (@{$template->{SOURCE}}) {
515         my ($fi_type, $fi_text, $fi_lineno) = @$fi_item;
516         if ($fi_type eq 'TEXT') {
517             $fi_r .= $fi_text;
518         } elsif ($fi_type eq 'PROG') {
519             my $fi_res;
520             my $original_fi_text = $fi_text;
521
522             # strip surrounding whitespace for simpler regexes
523             $fi_text =~ s/^\s+//;
524             $fi_text =~ s/\s+$//;
525
526             # if the codeblock is a simple $Variable lookup, use the value from
527             # the TemplateArgs hash...
528             if (my ($var) = $fi_text =~ /^\$(\w+)$/) {
529                 if (exists $args{TemplateArgs}{$var}) {
530                     $fi_res = $args{TemplateArgs}{$var};
531                 }
532             }
533
534             # if there was no substitution then just reinsert the codeblock
535             if (!defined $fi_res) {
536                 $fi_res = "{$original_fi_text}";
537             }
538
539             # If the value of the filled-in text really was undef,
540             # change it to an explicit empty string to avoid undefined
541             # value warnings later.
542             $fi_res = '' unless defined $fi_res;
543
544             $fi_r .= $fi_res;
545         }
546     }
547
548     return $fi_r;
549 }
550
551 sub _MassageSimpleTemplateArgs {
552     my $self = shift;
553     my %args = (
554         TemplateArgs => {},
555         @_,
556     );
557
558     my $template_args = $args{TemplateArgs};
559
560     if (my $ticket = $template_args->{Ticket}) {
561         for my $column (qw/Id Subject Type InitialPriority FinalPriority Priority TimeEstimated TimeWorked Status TimeLeft Told Starts Started Due Resolved RequestorAddresses AdminCcAddresses CcAddresses/) {
562             $template_args->{"Ticket".$column} = $ticket->$column;
563         }
564
565         $template_args->{"TicketQueueId"}   = $ticket->Queue;
566         $template_args->{"TicketQueueName"} = $ticket->QueueObj->Name;
567
568         $template_args->{"TicketOwnerId"}    = $ticket->Owner;
569         $template_args->{"TicketOwnerName"}  = $ticket->OwnerObj->Name;
570         $template_args->{"TicketOwnerEmailAddress"} = $ticket->OwnerObj->EmailAddress;
571
572         my $cfs = $ticket->CustomFields;
573         while (my $cf = $cfs->Next) {
574             $template_args->{"TicketCF" . $cf->Name} = $ticket->CustomFieldValuesAsString($cf->Name);
575         }
576     }
577
578     if (my $txn = $template_args->{Transaction}) {
579         for my $column (qw/Id TimeTaken Type Field OldValue NewValue Data Content Subject Description BriefDescription/) {
580             $template_args->{"Transaction".$column} = $txn->$column;
581         }
582
583         my $cfs = $txn->CustomFields;
584         while (my $cf = $cfs->Next) {
585             $template_args->{"TransactionCF" . $cf->Name} = $txn->CustomFieldValuesAsString($cf->Name);
586         }
587     }
588 }
589
590 sub _DowngradeFromHTML {
591     my $self = shift;
592     my $orig_entity = $self->MIMEObj;
593
594     my $new_entity = $orig_entity->dup; # this will fail badly if we go away from InCore parsing
595     $new_entity->head->mime_attr( "Content-Type" => 'text/plain' );
596     $new_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' );
597
598     $orig_entity->head->mime_attr( "Content-Type" => 'text/html' );
599     $orig_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' );
600     $orig_entity->make_multipart('alternative', Force => 1);
601
602     require HTML::FormatText;
603     require HTML::TreeBuilder;
604     # MIME objects are always bytes, not characters
605     my $tree = HTML::TreeBuilder->new_from_content(
606         Encode::decode( 'UTF-8', $new_entity->bodyhandle->as_string)
607     );
608     my $text = HTML::FormatText->new(
609         leftmargin  => 0,
610         rightmargin => 78,
611     )->format( $tree );
612     $text = Encode::encode( "UTF-8", $text );
613
614     $new_entity->bodyhandle(MIME::Body::InCore->new( \$text ));
615     $tree->delete;
616
617     $orig_entity->add_part($new_entity, 0); # plain comes before html
618     $self->{MIMEObj} = $orig_entity;
619
620     return;
621 }
622
623 =head2 CurrentUserHasQueueRight
624
625 Helper function to call the template's queue's CurrentUserHasQueueRight with the passed in args.
626
627 =cut
628
629 sub CurrentUserHasQueueRight {
630     my $self = shift;
631     return ( $self->QueueObj->CurrentUserHasRight(@_) );
632 }
633
634 =head2 SetType
635
636 If setting Type to Perl, require the ExecuteCode right.
637
638 =cut
639
640 sub SetType {
641     my $self    = shift;
642     my $NewType = shift;
643
644     if ($NewType eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
645         return ( undef, $self->loc('Permission Denied') );
646     }
647
648     return $self->_Set( Field => 'Type', Value => $NewType );
649 }
650
651 =head2 SetContent
652
653 If changing content and the type is Perl, require the ExecuteCode right.
654
655 =cut
656
657 sub SetContent {
658     my $self       = shift;
659     my $NewContent = shift;
660
661     if ($self->Type eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
662         return ( undef, $self->loc('Permission Denied') );
663     }
664
665     return $self->_Set( Field => 'Content', Value => $NewContent );
666 }
667
668 sub _UpdateAttributes {
669     my $self = shift;
670     my %args = (
671         NewValues => {},
672         @_,
673     );
674
675     my $type = $args{NewValues}{Type} || $self->Type;
676
677     # forbid updating content when the (possibly new) value of Type is Perl
678     if ($type eq 'Perl' && exists $args{NewValues}{Content}) {
679         if (!$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) {
680             return $self->loc('Permission Denied');
681         }
682     }
683
684     return $self->SUPER::_UpdateAttributes(%args);
685 }
686
687 =head2 CompileCheck
688
689 If the template's Type is Perl, then compile check all the codeblocks to see if
690 they are syntactically valid. We eval them in a codeblock to avoid actually
691 executing the code.
692
693 Returns an (ok, message) pair.
694
695 =cut
696
697 sub CompileCheck {
698     my $self = shift;
699
700     return (1, $self->loc("Template does not include Perl code"))
701         unless $self->Type eq 'Perl';
702
703     my $content = $self->Content;
704     $content = '' if !defined($content);
705
706     my $template = Text::Template->new(
707         TYPE   => 'STRING',
708         SOURCE => $content,
709     );
710     my ($ok) = $template->compile;
711     return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok;
712
713     # copied from Text::Template::fill_in and refactored to be compile checks
714     foreach my $fi_item (@{$template->{SOURCE}}) {
715         my ($fi_type, $fi_text, $fi_lineno) = @$fi_item;
716         next unless $fi_type eq 'PROG';
717
718         do {
719             no strict 'vars';
720             eval "sub { $fi_text }";
721         };
722         next if !$@;
723
724         my $error = $@;
725
726         # provide a (hopefully) useful line number for the error, but clean up
727         # all the other extraneous garbage
728         $error =~ s/\(eval \d+\) line (\d+).*/"template line " . ($1+$fi_lineno-1)/es;
729
730         return (0, $self->loc("Couldn't compile template codeblock '[_1]': [_2]", $fi_text, $error));
731     }
732
733     return (1, $self->loc("Template compiles"));
734 }
735
736 =head2 CurrentUserCanRead
737
738 =cut
739
740 sub CurrentUserCanRead {
741     my $self =shift;
742
743     if ($self->__Value('Queue')) {
744         my $queue = RT::Queue->new( RT->SystemUser );
745         $queue->Load( $self->__Value('Queue'));
746         return 1 if $self->CurrentUser->HasRight( Right => 'ShowTemplate', Object => $queue );
747     } else {
748         return 1 if $self->CurrentUser->HasRight( Right => 'ShowGlobalTemplates', Object => $RT::System );
749         return 1 if $self->CurrentUser->HasRight( Right => 'ShowTemplate',        Object => $RT::System );
750     }
751
752     return;
753 }
754
755 1;
756
757 use RT::Queue;
758 use base 'RT::Record';
759
760 sub Table {'Templates'}
761
762
763
764
765
766
767 =head2 id
768
769 Returns the current value of id.
770 (In the database, id is stored as int(11).)
771
772
773 =cut
774
775
776 =head2 Queue
777
778 Returns the current value of Queue.
779 (In the database, Queue is stored as int(11).)
780
781
782
783 =head2 SetQueue VALUE
784
785
786 Set Queue to VALUE.
787 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
788 (In the database, Queue will be stored as a int(11).)
789
790
791 =cut
792
793
794 =head2 QueueObj
795
796 Returns the Queue Object which has the id returned by Queue
797
798
799 =cut
800
801 sub QueueObj {
802         my $self = shift;
803         my $Queue =  RT::Queue->new($self->CurrentUser);
804         $Queue->Load($self->__Value('Queue'));
805         return($Queue);
806 }
807
808 =head2 Name
809
810 Returns the current value of Name.
811 (In the database, Name is stored as varchar(200).)
812
813
814
815 =head2 SetName VALUE
816
817
818 Set Name to VALUE.
819 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
820 (In the database, Name will be stored as a varchar(200).)
821
822
823 =cut
824
825
826 =head2 Description
827
828 Returns the current value of Description.
829 (In the database, Description is stored as varchar(255).)
830
831
832
833 =head2 SetDescription VALUE
834
835
836 Set Description to VALUE.
837 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
838 (In the database, Description will be stored as a varchar(255).)
839
840
841 =cut
842
843
844 =head2 Type
845
846 Returns the current value of Type.
847 (In the database, Type is stored as varchar(16).)
848
849
850
851 =head2 SetType VALUE
852
853
854 Set Type to VALUE.
855 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
856 (In the database, Type will be stored as a varchar(16).)
857
858
859 =cut
860
861
862 =head2 Language
863
864 Returns the current value of Language.
865 (In the database, Language is stored as varchar(16).)
866
867
868
869 =head2 SetLanguage VALUE
870
871
872 Set Language to VALUE.
873 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
874 (In the database, Language will be stored as a varchar(16).)
875
876
877 =cut
878
879
880 =head2 TranslationOf
881
882 Returns the current value of TranslationOf.
883 (In the database, TranslationOf is stored as int(11).)
884
885
886
887 =head2 SetTranslationOf VALUE
888
889
890 Set TranslationOf to VALUE.
891 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
892 (In the database, TranslationOf will be stored as a int(11).)
893
894
895 =cut
896
897
898 =head2 Content
899
900 Returns the current value of Content.
901 (In the database, Content is stored as text.)
902
903
904
905 =head2 SetContent VALUE
906
907
908 Set Content to VALUE.
909 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
910 (In the database, Content will be stored as a text.)
911
912
913 =cut
914
915
916 =head2 LastUpdated
917
918 Returns the current value of LastUpdated.
919 (In the database, LastUpdated is stored as datetime.)
920
921
922 =cut
923
924
925 =head2 LastUpdatedBy
926
927 Returns the current value of LastUpdatedBy.
928 (In the database, LastUpdatedBy is stored as int(11).)
929
930
931 =cut
932
933
934 =head2 Creator
935
936 Returns the current value of Creator.
937 (In the database, Creator is stored as int(11).)
938
939
940 =cut
941
942
943 =head2 Created
944
945 Returns the current value of Created.
946 (In the database, Created is stored as datetime.)
947
948
949 =cut
950
951
952
953 sub _CoreAccessible {
954     {
955
956         id =>
957                 {read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
958         Queue =>
959                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
960         Name =>
961                 {read => 1, write => 1, sql_type => 12, length => 200,  is_blob => 0,  is_numeric => 0,  type => 'varchar(200)', default => ''},
962         Description =>
963                 {read => 1, write => 1, sql_type => 12, length => 255,  is_blob => 0,  is_numeric => 0,  type => 'varchar(255)', default => ''},
964         Type =>
965                 {read => 1, write => 1, sql_type => 12, length => 16,  is_blob => 0,  is_numeric => 0,  type => 'varchar(16)', default => ''},
966         Language =>
967                 {read => 1, write => 1, sql_type => 12, length => 16,  is_blob => 0,  is_numeric => 0,  type => 'varchar(16)', default => ''},
968         TranslationOf =>
969                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
970         Content =>
971                 {read => 1, write => 1, sql_type => -4, length => 0,  is_blob => 1,  is_numeric => 0,  type => 'text', default => ''},
972         LastUpdated =>
973                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
974         LastUpdatedBy =>
975                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
976         Creator =>
977                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
978         Created =>
979                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
980
981  }
982 };
983
984 RT::Base->_ImportOverlays();
985
986 1;