import rt 3.4.6
[freeside.git] / rt / lib / RT / Template_Overlay.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
6 #                                          <jesse@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., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27
28 # CONTRIBUTION SUBMISSION POLICY:
29
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
35
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
44
45 # END BPS TAGGED BLOCK }}}
46 # Portions Copyright 2000 Tobias Brox <tobix@cpan.org> 
47
48 =head1 NAME
49
50   RT::Template - RT's template object
51
52 =head1 SYNOPSIS
53
54   use RT::Template;
55
56 =head1 DESCRIPTION
57
58
59 =head1 METHODS
60
61 =begin testing
62
63 ok(require RT::Template);
64
65 =end testing
66
67 =cut
68
69
70 package RT::Template;
71
72 use strict;
73 no warnings qw(redefine);
74
75 use Text::Template;
76 use MIME::Entity;
77 use MIME::Parser;
78 use File::Temp qw /tempdir/;
79
80
81 # {{{ sub _Accessible 
82
83 sub _Accessible {
84     my $self = shift;
85     my %Cols = (
86         id            => 'read',
87         Name          => 'read/write',
88         Description   => 'read/write',
89         Type          => 'read/write',    #Type is one of Action or Message
90         Content       => 'read/write',
91         Queue         => 'read/write',
92         Creator       => 'read/auto',
93         Created       => 'read/auto',
94         LastUpdatedBy => 'read/auto',
95         LastUpdated   => 'read/auto'
96     );
97     return $self->SUPER::_Accessible( @_, %Cols );
98 }
99
100 # }}}
101
102 # {{{ sub _Set
103
104 sub _Set {
105     my $self = shift;
106     
107     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
108         return ( 0, $self->loc('Permission Denied') );
109     }
110     return $self->SUPER::_Set( @_ );
111 }
112
113 # }}}
114
115 # {{{ sub _Value 
116
117 =head2 _Value
118
119 Takes the name of a table column.
120 Returns its value as a string, if the user passes an ACL check
121
122
123 =begin testing
124
125 my $t = RT::Template->new($RT::SystemUser);
126 $t->Create(Name => "Foo", Queue => 1);
127 my $t2 = RT::Template->new($RT::Nobody);
128 $t2->Load($t->Id);
129 ok($t2->QueueObj->id, "Got the template's queue objet");
130
131 =end testing
132
133
134
135 =cut
136
137 sub _Value {
138     my $self  = shift;
139
140     unless ( $self->CurrentUserHasQueueRight('ShowTemplate') ) {
141         return undef;
142     }
143     return $self->__Value( @_ );
144
145 }
146
147 # }}}
148
149 # {{{ sub Load
150
151 =head2 Load <identifer>
152
153 Load a template, either by number or by name
154
155 =cut
156
157 sub Load {
158     my $self       = shift;
159     my $identifier = shift;
160     return undef unless $identifier;
161
162     if ( $identifier =~ /\D/ ) {
163         return $self->LoadByCol( 'Name', $identifier );
164     }
165     return $self->LoadById( $identifier );
166 }
167
168 # }}}
169
170 # {{{ sub LoadGlobalTemplate
171
172 =head2 LoadGlobalTemplate NAME
173
174 Load the global tempalte with the name NAME
175
176 =cut
177
178 sub LoadGlobalTemplate {
179     my $self = shift;
180     my $id   = shift;
181
182     return ( $self->LoadQueueTemplate( Queue => 0, Name => $id ) );
183 }
184
185 # }}}
186
187 # {{{ sub LoadQueueTemplate
188
189 =head2  LoadQueueTemplate (Queue => QUEUEID, Name => NAME)
190
191 Loads the Queue template named NAME for Queue QUEUE.
192
193 =cut
194
195 sub LoadQueueTemplate {
196     my $self = shift;
197     my %args = (
198         Queue => undef,
199         Name  => undef,
200         @_
201     );
202
203     return ( $self->LoadByCols( Name => $args{'Name'}, Queue => $args{'Queue'} ) );
204
205 }
206
207 # }}}
208
209 # {{{ sub Create
210
211 =head2 Create
212
213 Takes a paramhash of Content, Queue, Name and Description.
214 Name should be a unique string identifying this Template.
215 Description and Content should be the template's title and content.
216 Queue should be 0 for a global template and the queue # for a queue-specific 
217 template.
218
219 Returns the Template's id # if the create was successful. Returns undef for
220 unknown database failure.
221
222
223 =cut
224
225 sub Create {
226     my $self = shift;
227     my %args = (
228         Content     => undef,
229         Queue       => 0,
230         Description => '[no description]',
231         Type        => 'Action', #By default, template are 'Action' templates
232         Name        => undef,
233         @_
234     );
235
236     unless ( $args{'Queue'} ) {
237         unless ( $self->CurrentUser->HasRight(Right =>'ModifyTemplate', Object => $RT::System) ) {
238             return ( undef, $self->loc('Permission denied') );
239         }
240         $args{'Queue'} = 0;
241     }
242     else {
243         my $QueueObj = new RT::Queue( $self->CurrentUser );
244         $QueueObj->Load( $args{'Queue'} ) || return ( undef, $self->loc('Invalid queue') );
245     
246         unless ( $QueueObj->CurrentUserHasRight('ModifyTemplate') ) {
247             return ( undef, $self->loc('Permission denied') );
248         }
249         $args{'Queue'} = $QueueObj->Id;
250     }
251
252     my $result = $self->SUPER::Create(
253         Content     => $args{'Content'},
254         Queue       => $args{'Queue'},
255         Description => $args{'Description'},
256         Name        => $args{'Name'},
257     );
258
259     return ($result);
260
261 }
262
263 # }}}
264
265 # {{{ sub Delete
266
267 =head2 Delete
268
269 Delete this template.
270
271 =cut
272
273 sub Delete {
274     my $self = shift;
275
276     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
277         return ( 0, $self->loc('Permission Denied') );
278     }
279
280     return ( $self->SUPER::Delete(@_) );
281 }
282
283 # }}}
284
285 # {{{ sub MIMEObj
286 sub MIMEObj {
287     my $self = shift;
288     return ( $self->{'MIMEObj'} );
289 }
290
291 # }}}
292
293 # {{{ sub Parse 
294
295 =head2 Parse
296
297  This routine performs Text::Template parsing on the template and then
298  imports the results into a MIME::Entity so we can really use it
299
300  Takes a hash containing Argument, TicketObj, and TransactionObj.
301
302  It returns a tuple of (val, message)
303  If val is 0, the message contains an error message
304
305 =cut
306
307 sub Parse {
308     my $self = shift;
309
310     #We're passing in whatever we were passed. it's destined for _ParseContent
311     my ($content, $msg) = $self->_ParseContent(@_);
312     return ( 0, $msg ) unless defined $content;
313
314     #Lets build our mime Entity
315
316     my $parser = MIME::Parser->new();
317
318     # On some situations TMPDIR is non-writable. sad but true.
319     $parser->output_to_core(1);
320     $parser->tmp_to_core(1);
321
322     #If someone includes a message, don't extract it
323     $parser->extract_nested_messages(1);
324
325     # Set up the prefix for files with auto-generated names:
326     $parser->output_prefix("part");
327
328     # If content length is <= 50000 bytes, store each msg as in-core scalar;
329     # Else, write to a disk file (the default action):
330     $parser->output_to_core(50000);
331
332     ### Should we forgive normally-fatal errors?
333     $parser->ignore_errors(1);
334     $self->{'MIMEObj'} = eval { $parser->parse_data($content) };
335     if ( my $error = $@ || $parser->last_error ) {
336         $RT::Logger->error( "$error" );
337         return ( 0, $error );
338     }
339
340     # Unfold all headers
341     $self->{'MIMEObj'}->head->unfold;
342
343     return ( 1, $self->loc("Template parsed") );
344
345 }
346
347 # }}}
348
349 # {{{ sub _ParseContent
350
351 # Perform Template substitutions on the template
352
353 sub _ParseContent {
354     my $self = shift;
355     my %args = (
356         Argument       => undef,
357         TicketObj      => undef,
358         TransactionObj => undef,
359         @_
360     );
361
362     no warnings 'redefine';
363     $T::Ticket      = $args{'TicketObj'};
364     $T::Transaction = $args{'TransactionObj'};
365     $T::Argument    = $args{'Argument'};
366     $T::Requestor   = eval { $T::Ticket->Requestors->UserMembersObj->First->Name };
367     $T::rtname      = $RT::rtname;
368     *T::loc         = sub { $T::Ticket->loc(@_) };
369
370     my $content = $self->Content;
371     unless ( defined $content ) {
372         return ( undef, $self->loc("Permissions denied") );
373     }
374
375     # We need to untaint the content of the template, since we'll be working
376     # with it
377     $content =~ s/^(.*)$/$1/;
378     my $template = Text::Template->new(
379         TYPE   => 'STRING',
380         SOURCE => $content
381     );
382
383     my $is_broken = 0;
384     my $retval = $template->fill_in( PACKAGE => 'T', BROKEN => sub {
385         my (%args) = @_;
386         $RT::Logger->error("Template parsing error: $args{error}")
387             unless $args{error} =~ /^Died at /; # ignore intentional die()
388         $is_broken++;
389         return undef;
390     } );
391     return ( undef, $self->loc('Template parsing error') ) if $is_broken;
392
393     # MIME::Parser has problems dealing with high-bit utf8 data.
394     Encode::_utf8_off($retval);
395     return ($retval);
396 }
397
398 # }}}
399
400 # {{{ sub CurrentUserHasQueueRight
401
402 =head2 CurrentUserHasQueueRight
403
404 Helper function to call the template's queue's CurrentUserHasQueueRight with the passed in args.
405
406 =cut
407
408 sub CurrentUserHasQueueRight {
409     my $self = shift;
410     return ( $self->QueueObj->CurrentUserHasRight(@_) );
411 }
412
413 # }}}
414
415 1;