import rt 3.4.4
[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     # use super::value or we get acl blocked
108     if ( ( defined $self->SUPER::_Value('Queue') )
109         && ( $self->SUPER::_Value('Queue') == 0 ) )
110     {
111         unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ModifyTemplate') ) {
112             return ( 0, $self->loc('Permission Denied') );
113         }
114     }
115     else {
116
117         unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
118             return ( 0, $self->loc('Permission Denied') );
119         }
120     }
121     return ( $self->SUPER::_Set(@_) );
122
123 }
124
125 # }}}
126
127 # {{{ sub _Value 
128
129 =head2 _Value
130
131 Takes the name of a table column.
132 Returns its value as a string, if the user passes an ACL check
133
134
135 =begin testing
136
137 my $t = RT::Template->new($RT::SystemUser);
138 $t->Create(Name => "Foo", Queue => 1);
139 my $t2 = RT::Template->new($RT::Nobody);
140 $t2->Load($t->Id);
141 ok($t2->QueueObj->id, "Got the template's queue objet");
142
143 =end testing
144
145
146
147 =cut
148
149 sub _Value {
150
151     my $self  = shift;
152     my $field = shift;
153
154    
155     #If the current user doesn't have ACLs, don't let em at it.  
156     #use super::value or we get acl blocked
157     if ( ( !defined $self->__Value('Queue') )
158         || ( $self->__Value('Queue') == 0 ) )
159     {
160         unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ShowTemplate') ) {
161             return (undef);
162         }
163     }
164     else {
165         unless ( $self->CurrentUserHasQueueRight('ShowTemplate') ) {
166             return (undef);
167         }
168     }
169     return ( $self->__Value($field) );
170
171 }
172
173 # }}}
174
175 # {{{ sub Load
176
177 =head2 Load <identifer>
178
179 Load a template, either by number or by name
180
181 =cut
182
183 sub Load {
184     my $self       = shift;
185     my $identifier = shift;
186
187     if ( !$identifier ) {
188         return (undef);
189     }
190
191     if ( $identifier !~ /\D/ ) {
192         $self->SUPER::LoadById($identifier);
193     }
194     else {
195         $self->LoadByCol( 'Name', $identifier );
196
197     }
198 }
199
200 # }}}
201
202 # {{{ sub LoadGlobalTemplate
203
204 =head2 LoadGlobalTemplate NAME
205
206 Load the global tempalte with the name NAME
207
208 =cut
209
210 sub LoadGlobalTemplate {
211     my $self = shift;
212     my $id   = shift;
213
214     return ( $self->LoadQueueTemplate( Queue => 0, Name => $id ) );
215 }
216
217 # }}}
218
219 # {{{ sub LoadQueueTemplate
220
221 =head2  LoadQueueTemplate (Queue => QUEUEID, Name => NAME)
222
223 Loads the Queue template named NAME for Queue QUEUE.
224
225 =cut
226
227 sub LoadQueueTemplate {
228     my $self = shift;
229     my %args = (
230         Queue => undef,
231         Name  => undef,
232         @_
233     );
234
235     return ( $self->LoadByCols( Name => $args{'Name'}, Queue => $args{'Queue'} ) );
236
237 }
238
239 # }}}
240
241 # {{{ sub Create
242
243 =head2 Create
244
245 Takes a paramhash of Content, Queue, Name and Description.
246 Name should be a unique string identifying this Template.
247 Description and Content should be the template's title and content.
248 Queue should be 0 for a global template and the queue # for a queue-specific 
249 template.
250
251 Returns the Template's id # if the create was successful. Returns undef for
252 unknown database failure.
253
254
255 =cut
256
257 sub Create {
258     my $self = shift;
259     my %args = (
260         Content     => undef,
261         Queue       => 0,
262         Description => '[no description]',
263         Type => 'Action',    #By default, template are 'Action' templates
264         Name => undef,
265         @_
266     );
267
268     if ( !$args{'Queue'}  ) {
269         unless ( $self->CurrentUser->HasRight(Right =>'ModifyTemplate', Object => $RT::System) ) {
270             return (undef);
271         }
272         $args{'Queue'} = 0;
273     }
274     else {
275         my $QueueObj = new RT::Queue( $self->CurrentUser );
276         $QueueObj->Load( $args{'Queue'} ) || return ( 0, $self->loc('Invalid queue') );
277     
278         unless ( $QueueObj->CurrentUserHasRight('ModifyTemplate') ) {
279             return (undef);
280         }
281         $args{'Queue'} = $QueueObj->Id;
282     }
283
284     my $result = $self->SUPER::Create(
285         Content => $args{'Content'},
286         Queue   =>  $args{'Queue'},
287         Description => $args{'Description'},
288         Name        => $args{'Name'}
289     );
290
291     return ($result);
292
293 }
294
295 # }}}
296
297 # {{{ sub Delete
298
299 =head2 Delete
300
301 Delete this template.
302
303 =cut
304
305 sub Delete {
306     my $self = shift;
307
308     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
309         return ( 0, $self->loc('Permission Denied') );
310     }
311
312     return ( $self->SUPER::Delete(@_) );
313 }
314
315 # }}}
316
317 # {{{ sub MIMEObj
318 sub MIMEObj {
319     my $self = shift;
320     return ( $self->{'MIMEObj'} );
321 }
322
323 # }}}
324
325 # {{{ sub Parse 
326
327 =head2 Parse
328
329  This routine performs Text::Template parsing on the template and then
330  imports the results into a MIME::Entity so we can really use it
331
332  Takes a hash containing Argument, TicketObj, and TransactionObj.
333
334  It returns a tuple of (val, message)
335  If val is 0, the message contains an error message
336
337 =cut
338
339 sub Parse {
340     my $self = shift;
341
342     #We're passing in whatever we were passed. it's destined for _ParseContent
343     my $content = $self->_ParseContent(@_);
344
345     #Lets build our mime Entity
346
347     my $parser = MIME::Parser->new();
348
349         # On some situations TMPDIR is non-writable. sad but true.
350         $parser->output_to_core(1);
351         $parser->tmp_to_core(1);
352
353     #If someone includes a message, don't extract it
354     $parser->extract_nested_messages(1);
355
356     # Set up the prefix for files with auto-generated names:
357     $parser->output_prefix("part");
358
359     # If content length is <= 50000 bytes, store each msg as in-core scalar;
360     # Else, write to a disk file (the default action):
361     $parser->output_to_core(50000);
362
363     ### Should we forgive normally-fatal errors?
364     $parser->ignore_errors(1);
365     $self->{'MIMEObj'} = eval { $parser->parse_data($content) };
366     my $error = ( $@ || $parser->last_error );
367
368     if ($error) {
369         $RT::Logger->error("$error");
370         return ( 0, $error );
371     }
372
373     # Unfold all headers
374     $self->{'MIMEObj'}->head->unfold();
375
376     return ( 1, $self->loc("Template parsed") );
377
378 }
379
380 # }}}
381
382 # {{{ sub _ParseContent
383
384 # Perform Template substitutions on the template
385
386 sub _ParseContent {
387     my $self = shift;
388     my %args = (
389         Argument       => undef,
390         TicketObj      => undef,
391         TransactionObj => undef,
392         @_
393     );
394
395     no warnings 'redefine';
396     $T::Ticket      = $args{'TicketObj'};
397     $T::Transaction = $args{'TransactionObj'};
398     $T::Argument    = $args{'Argument'};
399     $T::Requestor   = eval { $T::Ticket->Requestors->UserMembersObj->First->Name };
400     $T::rtname      = $RT::rtname;
401     *T::loc         = sub { $T::Ticket->loc(@_) };
402
403     # We need to untaint the content of the template, since we'll be working
404     # with it
405     my $content = $self->Content();
406     $content =~ s/^(.*)$/$1/;
407     my $template = Text::Template->new(
408         TYPE   => 'STRING',
409         SOURCE => $content
410     );
411
412     my $is_broken = 0;
413     my $retval = $template->fill_in( PACKAGE => 'T', BROKEN => sub {
414         my (%args) = @_;
415         $RT::Logger->error("Template parsing error: $args{error}")
416             unless $args{error} =~ /^Died at /; # ignore intentional die()
417         $is_broken++;
418         return undef;
419     } );
420     return undef if $is_broken;
421
422     # MIME::Parser has problems dealing with high-bit utf8 data.
423     Encode::_utf8_off($retval);
424     return ($retval);
425 }
426
427 # }}}
428
429 # {{{ sub CurrentUserHasQueueRight
430
431 =head2 CurrentUserHasQueueRight
432
433 Helper function to call the template's queue's CurrentUserHasQueueRight with the passed in args.
434
435 =cut
436
437 sub CurrentUserHasQueueRight {
438     my $self = shift;
439     return ( $self->QueueObj->CurrentUserHasRight(@_) );
440 }
441
442 # }}}
443
444 1;