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