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