This commit was generated by cvs2svn to compensate for changes in r3921,
[freeside.git] / rt / lib / RT / CustomField_Overlay.pm
1 # {{{ BEGIN BPS TAGGED BLOCK
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2004 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 use strict;
47 no warnings qw(redefine);
48
49 use vars qw(@TYPES %TYPES);
50
51 use RT::CustomFieldValues;
52 use RT::TicketCustomFieldValues;
53
54 # Enumerate all valid types for this custom field
55 @TYPES = (
56     'SelectSingle',     # loc
57     'SelectMultiple',   # loc
58     'FreeformSingle',   # loc
59     'FreeformMultiple', # loc
60 );
61
62 # Populate a hash of types of easier validation
63 for (@TYPES) { $TYPES{$_} = 1};
64
65
66
67
68 =head1 NAME
69
70   RT::CustomField_Overlay 
71
72 =head1 DESCRIPTION
73
74 =head1 'CORE' METHODS
75
76 =cut
77
78
79
80 =head2 Create PARAMHASH
81
82 Create takes a hash of values and creates a row in the database:
83
84   varchar(200) 'Name'.
85   varchar(200) 'Type'.
86   int(11) 'Queue'.
87   varchar(255) 'Description'.
88   int(11) 'SortOrder'.
89   smallint(6) 'Disabled'.
90
91 =cut
92
93
94
95
96 sub Create {
97     my $self = shift;
98     my %args = ( 
99                 Name => '',
100                 Type => '',
101                 Queue => '0',
102                 Description => '',
103                 SortOrder => '0',
104                 Disabled => '0',
105
106                   @_);
107
108     
109
110     if (  ! $args{'Queue'} ) {
111         unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AdminCustomFields') ) {
112             return ( 0, $self->loc('Permission Denied') );
113         }
114     }
115     else {
116         my $queue = RT::Queue->new($self->CurrentUser);
117         $queue->Load($args{'Queue'});
118         unless ($queue->Id) {
119             return (0, $self->loc("Queue not found"));
120         }
121         unless ( $queue->CurrentUserHasRight('AdminCustomFields') ) {
122             return ( 0, $self->loc('Permission Denied') );
123         }
124     }
125     $self->SUPER::Create(
126                          Name => $args{'Name'},
127                          Type => $args{'Type'},
128                          Queue => $args{'Queue'},
129                          Description => $args{'Description'},
130                          SortOrder => $args{'SortOrder'},
131                          Disabled => $args{'Disabled'},
132 );
133
134 }
135
136
137 # {{{ sub LoadByNameAndQueue
138
139 =head2  LoadByNameAndQueue (Queue => QUEUEID, Name => NAME)
140
141 Loads the Custom field named NAME for Queue QUEUE. If QUEUE is 0,
142 loads a global custom field
143
144 =cut
145
146 # Compatibility for API change after 3.0 beta 1
147 *LoadNameAndQueue = \&LoadByNameAndQueue;
148
149 sub LoadByNameAndQueue {
150     my $self = shift;
151     my %args = (
152         Queue => undef,
153         Name  => undef,
154         @_,
155     );
156
157     if ($args{'Queue'} =~ /\D/) {
158         my $QueueObj = RT::Queue->new($self->CurrentUser);
159         $QueueObj->Load($args{'Queue'});
160         $args{'Queue'} = $QueueObj->Id;
161     }
162
163     return ( $self->LoadByCols( Name => $args{'Name'}, Queue => $args{'Queue'} ) );
164
165 }
166
167 # }}}
168
169 # {{{ Dealing with custom field values 
170
171 =begin testing
172 use_ok(RT::CustomField);
173 ok(my $cf = RT::CustomField->new($RT::SystemUser));
174 ok(my ($id, $msg)=  $cf->Create( Name => 'TestingCF',
175                                  Queue => '0',
176                                  SortOrder => '1',
177                                  Description => 'A Testing custom field',
178                                  Type=> 'SelectSingle'), 'Created a global CustomField');
179 ok($id != 0, 'Global custom field correctly created');
180 ok ($cf->SingleValue);
181 ok($cf->Type eq 'SelectSingle');
182
183 ok($cf->SetType('SelectMultiple'));
184 ok($cf->Type eq 'SelectMultiple');
185 ok(!$cf->SingleValue );
186 ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type");
187 ok($bogus_val == 0, "Unable to set a custom field's type to a bogus type");
188
189 ok(my $bad_cf = RT::CustomField->new($RT::SystemUser));
190 ok(my ($bad_id, $bad_msg)=  $cf->Create( Name => 'TestingCF-bad',
191                                  Queue => '0',
192                                  SortOrder => '1',
193                                  Description => 'A Testing custom field with a bogus Type',
194                                  Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type');
195 ok($bad_id == 0, 'Global custom field correctly decided to not create a cf with a bogus type ');
196
197 =end testing
198
199 =cut
200
201 # {{{ AddValue
202
203 =head2 AddValue HASH
204
205 Create a new value for this CustomField.  Takes a paramhash containing the elements Name, Description and SortOrder
206
207 =begin testing
208
209 ok(my $cf = RT::CustomField->new($RT::SystemUser));
210 $cf->Load(1);
211 ok($cf->Id == 1);
212 ok(my ($val,$msg)  = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'));
213 ok($val != 0);
214 ok (my ($delval, $delmsg) = $cf->DeleteValue($val));
215 ok ($delval != 0);
216
217 =end testing
218
219 =cut
220
221 sub AddValue {
222         my $self = shift;
223         my %args = ( Name => undef,
224                      Description => undef,
225                      SortOrder => undef,
226                      @_ );
227
228     unless ($self->CurrentUserHasRight('AdminCustomFields')) {
229         return (0, $self->loc('Permission Denied'));
230     }
231
232     unless ($args{'Name'}) {
233         return(0, $self->loc("Can't add a custom field value without a name"));
234     }
235         my $newval = RT::CustomFieldValue->new($self->CurrentUser);
236         return($newval->Create(
237                      CustomField => $self->Id,
238              Name =>$args{'Name'},
239              Description => ($args{'Description'} || ''),
240              SortOrder => ($args{'SortOrder'} || '0')
241         ));    
242 }
243
244
245 # }}}
246
247 # {{{ DeleteValue
248
249 =head2 DeleteValue ID
250
251 Deletes a value from this custom field by id. 
252
253 Does not remove this value for any article which has had it selected    
254
255 =cut
256
257 sub DeleteValue {
258         my $self = shift;
259     my $id = shift;
260     unless ($self->CurrentUserHasRight('AdminCustomFields')) {
261         return (0, $self->loc('Permission Denied'));
262     }
263
264         my $val_to_del = RT::CustomFieldValue->new($self->CurrentUser);
265         $val_to_del->Load($id);
266         unless ($val_to_del->Id) {
267                 return (0, $self->loc("Couldn't find that value"));
268         }
269         unless ($val_to_del->CustomField == $self->Id) {
270                 return (0, $self->loc("That is not a value for this custom field"));
271         }
272
273         my $retval = $val_to_del->Delete();
274     if ($retval) {
275         return ($retval, $self->loc("Custom field value deleted"));
276     } else {
277         return(0, $self->loc("Custom field value could not be deleted"));
278     }
279 }
280
281 # }}}
282
283 # {{{ Values
284
285 =head2 Values FIELD
286
287 Return a CustomFieldeValues object of all acceptable values for this Custom Field.
288
289
290 =cut
291
292 sub Values {
293     my $self = shift;
294
295     my $cf_values = RT::CustomFieldValues->new($self->CurrentUser);
296     if ( $self->__Value('Queue') == 0 || $self->CurrentUserHasRight( 'SeeQueue') ) {
297         $cf_values->LimitToCustomField($self->Id);
298     }
299     return ($cf_values);
300 }
301
302 # }}}
303
304 # }}}
305
306 # {{{ Ticket related routines
307
308 # {{{ ValuesForTicket
309
310 =head2 ValuesForTicket TICKET
311
312 Returns a RT::TicketCustomFieldValues object of this Field's values for TICKET.
313 TICKET is a ticket id.
314
315
316 =cut
317
318 sub ValuesForTicket {
319         my $self = shift;
320     my $ticket_id = shift;
321
322         my $values = new RT::TicketCustomFieldValues($self->CurrentUser);
323         $values->LimitToCustomField($self->Id);
324     $values->LimitToTicket($ticket_id);
325
326         return ($values);
327 }
328
329 # }}}
330
331 # {{{ AddValueForTicket
332
333 =head2 AddValueForTicket HASH
334
335 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
336
337 =cut
338
339 sub AddValueForTicket {
340         my $self = shift;
341         my %args = ( Ticket => undef,
342                  Content => undef,
343                      @_ );
344
345         my $newval = RT::TicketCustomFieldValue->new($self->CurrentUser);
346         my $val = $newval->Create(Ticket => $args{'Ticket'},
347                             Content => $args{'Content'},
348                             CustomField => $self->Id);
349
350     return($val);
351
352 }
353
354
355 # }}}
356
357 # {{{ DeleteValueForTicket
358
359 =head2 DeleteValueForTicket HASH
360
361 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
362
363 =cut
364
365 sub DeleteValueForTicket {
366         my $self = shift;
367         my %args = ( Ticket => undef,
368                  Content => undef,
369                      @_ );
370
371         my $oldval = RT::TicketCustomFieldValue->new($self->CurrentUser);
372     $oldval->LoadByTicketContentAndCustomField (Ticket => $args{'Ticket'}, 
373                                                 Content =>  $args{'Content'}, 
374                                                 CustomField => $self->Id );
375     # check ot make sure we found it
376     unless ($oldval->Id) {
377         return(0, $self->loc("Custom field value [_1] could not be found for custom field [_2]", $args{'Content'}, $self->Name));
378     }
379     # delete it
380
381     my $ret = $oldval->Delete();
382     unless ($ret) {
383         return(0, $self->loc("Custom field value could not be found"));
384     }
385     return(1, $self->loc("Custom field value deleted"));
386 }
387
388
389 # }}}
390 # }}}
391
392
393 =head2 ValidateQueue Queue
394
395 Make sure that the queue specified is a valid queue name
396
397 =cut
398
399 sub ValidateQueue {
400     my $self = shift;
401     my $id = shift;
402
403     if ($id eq '0') { # 0 means "Global" null would _not_ be ok.
404         return (1); 
405     }
406
407     my $q = RT::Queue->new($RT::SystemUser);
408     $q->Load($id);
409     unless ($q->id) {
410         return undef;
411     }
412     return (1);
413
414
415 }
416
417
418 # {{{ Types
419
420 =head2 Types 
421
422 Retuns an array of the types of CustomField that are supported
423
424 =cut
425
426 sub Types {
427         return (@TYPES);
428 }
429
430 # }}}
431
432
433 =head2 FriendlyType [TYPE]
434
435 Returns a localized human-readable version of the custom field type.
436 If a custom field type is specified as the parameter, the friendly type for that type will be returned
437
438 =cut
439
440 sub FriendlyType {
441     my $self = shift;
442
443     my $type = shift || $self->Type;
444
445     if ( $type eq 'SelectSingle' ) {
446         return ( $self->loc('Select one value') );
447     }
448     elsif ( $type eq 'SelectMultiple' ) {
449         return ( $self->loc('Select multiple values') );
450     }
451     elsif ( $type eq 'FreeformSingle' ) {
452         return ( $self->loc('Enter one value') );
453     }
454     elsif ( $type eq 'FreeformMultiple' ) {
455         return ( $self->loc('Enter multiple values') );
456     }
457     else {
458         return ( $self->loc( $self->Type ) );
459     }
460 }
461
462
463 =head2 ValidateType TYPE
464
465 Takes a single string. returns true if that string is a value
466 type of custom field
467
468 =begin testing
469
470 ok(my $cf = RT::CustomField->new($RT::SystemUser));
471 ok($cf->ValidateType('SelectSingle'));
472 ok($cf->ValidateType('SelectMultiple'));
473 ok(!$cf->ValidateType('SelectFooMultiple'));
474
475 =end testing
476
477 =cut
478
479 sub ValidateType {
480     my $self = shift;
481     my $type = shift;
482
483     if( $TYPES{$type}) {
484         return(1);
485     }
486     else {
487         return undef;
488     }
489 }
490
491 # {{{ SingleValue
492
493 =head2 SingleValue
494
495 Returns true if this CustomField only accepts a single value. 
496 Returns false if it accepts multiple values
497
498 =cut
499
500 sub SingleValue {
501     my $self = shift;
502     if ($self->Type =~  /Single$/) {
503         return 1;
504     } 
505     else {
506         return undef;
507     }
508 }
509
510 # }}}
511
512 # {{{ sub CurrentUserHasRight
513
514 =head2 CurrentUserHasRight RIGHT
515
516 Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
517
518 =cut
519
520 sub CurrentUserHasRight {
521     my $self = shift;
522     my $right = shift;
523     # if there's no queue, we want to know about a global right
524     if ( ( !defined $self->__Value('Queue') ) || ( $self->__Value('Queue') == 0 ) ) {
525          return $self->CurrentUser->HasRight( Object => $RT::System, Right => $right); 
526     } else {
527         return ( $self->QueueObj->CurrentUserHasRight($right) );
528     }
529 }
530
531 # }}}
532
533 # {{{ sub _Set
534
535 sub _Set {
536     my $self = shift;
537
538     unless ( $self->CurrentUserHasRight('AdminCustomFields') ) {
539         return ( 0, $self->loc('Permission Denied') );
540     }
541     return ( $self->SUPER::_Set(@_) );
542
543 }
544
545 # }}}
546
547 # {{{ sub _Value 
548
549 =head2 _Value
550
551 Takes the name of a table column.
552 Returns its value as a string, if the user passes an ACL check
553
554 =cut
555
556 sub _Value {
557
558     my $self  = shift;
559     my $field = shift;
560
561     # We need to expose the queue so that we can do things like ACL checks
562     if ( $field eq 'Queue') {
563           return ( $self->SUPER::_Value($field) );
564      }
565
566
567     #Anybody can see global custom fields, otherwise we need to do the rights check
568         unless ( $self->__Value('Queue') == 0 || $self->CurrentUserHasRight( 'SeeQueue') ) {
569             return (undef);
570         }
571     return ( $self->__Value($field) );
572
573 }
574
575 # }}}
576 # {{{ sub SetDisabled
577
578 =head2 SetDisabled
579
580 Takes a boolean.
581 1 will cause this custom field to no longer be avaialble for tickets.
582 0 will re-enable this queue
583
584 =cut
585
586 # }}}
587
588 1;