1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
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
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.
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.
30 # CONTRIBUTION SUBMISSION POLICY:
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.)
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.
47 # END BPS TAGGED BLOCK }}}
49 package RT::CustomField;
52 no warnings qw(redefine);
54 use RT::CustomFieldValues;
55 use RT::ObjectCustomFieldValues;
60 'Select multiple values', # loc
61 'Select one value', # loc
62 'Select up to [_1] values', # loc
65 'Enter multiple values', # loc
66 'Enter one value', # loc
67 'Enter up to [_1] values', # loc
70 'Fill in multiple text areas', # loc
71 'Fill in one text area', # loc
72 'Fill in up to [_1] text areas',# loc
75 'Fill in multiple wikitext areas', # loc
76 'Fill in one wikitext area', # loc
77 'Fill in up to [_1] wikitext areas',# loc
80 'Upload multiple images', # loc
81 'Upload one image', # loc
82 'Upload up to [_1] images', # loc
85 'Upload multiple files', # loc
86 'Upload one file', # loc
87 'Upload up to [_1] files', # loc
90 'Combobox: Select or enter multiple values', # loc
91 'Combobox: Select or enter one value', # loc
92 'Combobox: Select or enter up to [_1] values', # loc
95 'Enter multiple values with autocompletion', # loc
96 'Enter one value with autocompletion', # loc
97 'Enter up to [_1] values with autocompletion', # loc
102 our %FRIENDLY_OBJECT_TYPES = ();
104 RT::CustomField->_ForObjectType( 'RT::Queue-RT::Ticket' => "Tickets", ); #loc
105 RT::CustomField->_ForObjectType(
106 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", ); #loc
107 RT::CustomField->_ForObjectType( 'RT::User' => "Users", ); #loc
108 RT::CustomField->_ForObjectType( 'RT::Queue' => "Queues", ); #loc
109 RT::CustomField->_ForObjectType( 'RT::Group' => "Groups", ); #loc
112 SeeCustomField => 'See custom fields', # loc_pair
113 AdminCustomField => 'Create, delete and modify custom fields', # loc_pair
114 ModifyCustomField => 'Add, delete and modify custom field values for objects' #loc_pair
117 # Tell RT::ACE that this sort of object can get acls granted
118 $RT::ACE::OBJECT_TYPES{'RT::CustomField'} = 1;
120 foreach my $right ( keys %{$RIGHTS} ) {
121 $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
124 =head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
126 Adds the given rights to the list of possible rights. This method
127 should be called during server startup, not at runtime.
134 $RIGHTS = { %$RIGHTS, %new };
135 %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
136 map { lc($_) => $_ } keys %new);
139 sub AvailableRights {
146 RT::CustomField_Overlay - overlay for RT::CustomField
150 =head1 'CORE' METHODS
152 =head2 Create PARAMHASH
154 Create takes a hash of values and creates a row in the database:
159 varchar(255) 'Pattern'.
160 smallint(6) 'Repeated'.
161 varchar(255) 'Description'.
163 varchar(255) 'LookupType'.
164 smallint(6) 'Disabled'.
166 C<LookupType> is generally the result of either
167 C<RT::Ticket->CustomFieldLookupType> or C<RT::Transaction->CustomFieldLookupType>.
183 IncludeContentForValue => '',
187 unless ( $self->CurrentUser->HasRight(Object => $RT::System, Right => 'AdminCustomField') ) {
188 return (0, $self->loc('Permission Denied'));
191 if ( $args{TypeComposite} ) {
192 @args{'Type', 'MaxValues'} = split(/-/, $args{TypeComposite}, 2);
194 elsif ( $args{Type} =~ s/(?:(Single)|Multiple)$// ) {
195 # old style Type string
196 $args{'MaxValues'} = $1 ? 1 : 0;
198 $args{'MaxValues'} = int $args{'MaxValues'};
200 if ( !exists $args{'Queue'}) {
201 # do nothing -- things below are strictly backward compat
203 elsif ( ! $args{'Queue'} ) {
204 unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AssignCustomFields') ) {
205 return ( 0, $self->loc('Permission Denied') );
207 $args{'LookupType'} = 'RT::Queue-RT::Ticket';
210 my $queue = RT::Queue->new($self->CurrentUser);
211 $queue->Load($args{'Queue'});
212 unless ($queue->Id) {
213 return (0, $self->loc("Queue not found"));
215 unless ( $queue->CurrentUserHasRight('AssignCustomFields') ) {
216 return ( 0, $self->loc('Permission Denied') );
218 $args{'LookupType'} = 'RT::Queue-RT::Ticket';
219 $args{'Queue'} = $queue->Id;
222 my ($ok, $msg) = $self->_IsValidRegex( $args{'Pattern'} );
223 return (0, $self->loc("Invalid pattern: [_1]", $msg)) unless $ok;
225 if ( $args{'MaxValues'} != 1 && $args{'Type'} =~ /(text|combobox)$/i ) {
226 $RT::Logger->warning("Support for 'multiple' Texts or Comboboxes is not implemented");
227 $args{'MaxValues'} = 1;
230 (my $rv, $msg) = $self->SUPER::Create(
231 Name => $args{'Name'},
232 Type => $args{'Type'},
233 MaxValues => $args{'MaxValues'},
234 Pattern => $args{'Pattern'},
235 Description => $args{'Description'},
236 Disabled => $args{'Disabled'},
237 LookupType => $args{'LookupType'},
238 Repeated => $args{'Repeated'},
241 if ( exists $args{'LinkValueTo'}) {
242 $self->SetLinkValueTo($args{'LinkValueTo'});
245 if ( exists $args{'IncludeContentForValue'}) {
246 $self->SetIncludeContentForValue($args{'IncludeContentForValue'});
249 if ( exists $args{'ValuesClass'} ) {
250 $self->SetValuesClass( $args{'ValuesClass'} );
253 if ( exists $args{'BasedOn'} ) {
254 $self->SetBasedOn( $args{'BasedOn'} );
257 return ($rv, $msg) unless exists $args{'Queue'};
259 # Compat code -- create a new ObjectCustomField mapping
260 my $OCF = RT::ObjectCustomField->new( $self->CurrentUser );
262 CustomField => $self->Id,
263 ObjectId => $args{'Queue'},
271 Load a custom field. If the value handed in is an integer, load by custom field ID. Otherwise, Load by name.
277 my $id = shift || '';
279 if ( $id =~ /^\d+$/ ) {
280 return $self->SUPER::Load( $id );
282 return $self->LoadByName( Name => $id );
289 =head2 LoadByName (Queue => QUEUEID, Name => NAME)
291 Loads the Custom field named NAME.
293 Will load a Disabled Custom Field even if there is a non-disabled Custom Field
296 If a Queue parameter is specified, only look for ticket custom fields tied to that Queue.
298 If the Queue parameter is '0', look for global ticket custom fields.
300 If no queue parameter is specified, look for any and all custom fields with this name.
302 BUG/TODO, this won't let you specify that you only want user or group CFs.
306 # Compatibility for API change after 3.0 beta 1
307 *LoadNameAndQueue = \&LoadByName;
308 # Change after 3.4 beta.
309 *LoadByNameAndQueue = \&LoadByName;
319 unless ( defined $args{'Name'} && length $args{'Name'} ) {
320 $RT::Logger->error("Couldn't load Custom Field without Name");
321 return wantarray ? (0, $self->loc("No name provided")) : 0;
324 # if we're looking for a queue by name, make it a number
325 if ( defined $args{'Queue'} && $args{'Queue'} =~ /\D/ ) {
326 my $QueueObj = RT::Queue->new( $self->CurrentUser );
327 $QueueObj->Load( $args{'Queue'} );
328 $args{'Queue'} = $QueueObj->Id;
331 # XXX - really naive implementation. Slow. - not really. still just one query
333 my $CFs = RT::CustomFields->new( $self->CurrentUser );
334 $CFs->SetContextObject( $self->ContextObject );
335 my $field = $args{'Name'} =~ /\D/? 'Name' : 'id';
336 $CFs->Limit( FIELD => $field, VALUE => $args{'Name'}, CASESENSITIVE => 0);
337 # Don't limit to queue if queue is 0. Trying to do so breaks
338 # RT::Group type CFs.
339 if ( defined $args{'Queue'} ) {
340 $CFs->LimitToQueue( $args{'Queue'} );
343 # When loading by name, we _can_ load disabled fields, but prefer
344 # non-disabled fields.
345 $CFs->{'find_disabled_rows'}=1;
347 { FIELD => "Disabled", ORDER => 'ASC' },
350 # We only want one entry.
351 $CFs->RowsPerPage(1);
353 # version before 3.8 just returns 0, so we need to test if wantarray to be
354 # backward compatible.
355 return wantarray ? (0, $self->loc("Not found")) : 0 unless my $first = $CFs->First;
357 return $self->LoadById( $first->id );
362 # {{{ Dealing with custom field values
365 =head2 Custom field values
369 Return a object (collection) of all acceptable values for this Custom Field.
370 Class of the object can vary and depends on the return value
371 of the C<ValuesClass> method.
375 *ValuesObj = \&Values;
380 my $class = $self->ValuesClass || 'RT::CustomFieldValues';
381 eval "require $class" or die "$@";
382 my $cf_values = $class->new( $self->CurrentUser );
383 # if the user has no rights, return an empty object
384 if ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
385 $cf_values->LimitToCustomField( $self->Id );
394 Create a new value for this CustomField. Takes a paramhash containing the elements Name, Description and SortOrder
402 unless ($self->CurrentUserHasRight('AdminCustomField')) {
403 return (0, $self->loc('Permission Denied'));
407 if ( !defined $args{'Name'} || $args{'Name'} eq '' ) {
408 return (0, $self->loc("Can't add a custom field value without a name"));
411 my $newval = RT::CustomFieldValue->new( $self->CurrentUser );
412 return $newval->Create( %args, CustomField => $self->Id );
420 =head3 DeleteValue ID
422 Deletes a value from this custom field by id.
424 Does not remove this value for any article which has had it selected
431 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
432 return (0, $self->loc('Permission Denied'));
435 my $val_to_del = RT::CustomFieldValue->new( $self->CurrentUser );
436 $val_to_del->Load( $id );
437 unless ( $val_to_del->Id ) {
438 return (0, $self->loc("Couldn't find that value"));
440 unless ( $val_to_del->CustomField == $self->Id ) {
441 return (0, $self->loc("That is not a value for this custom field"));
444 my $retval = $val_to_del->Delete;
446 return (0, $self->loc("Custom field value could not be deleted"));
448 return ($retval, $self->loc("Custom field value deleted"));
454 =head2 ValidateQueue Queue
456 Make sure that the queue specified is a valid queue name
464 return undef unless defined $id;
465 # 0 means "Global" null would _not_ be ok.
466 return 1 if $id eq '0';
468 my $q = RT::Queue->new( $RT::SystemUser );
470 return undef unless $q->id;
479 Retuns an array of the types of CustomField that are supported
484 return (keys %FieldTypes);
489 # {{{ IsSelectionType
491 =head2 IsSelectionType
493 Retuns a boolean value indicating whether the C<Values> method makes sense
494 to this Custom Field.
498 sub IsSelectionType {
500 my $type = @_? shift : $self->Type;
501 return undef unless $type;
503 $type =~ /(?:Select|Combobox|Autocomplete)/;
509 =head2 IsExternalValues
513 sub IsExternalValues {
515 my $selectable = $self->IsSelectionType( @_ );
516 return $selectable unless $selectable;
518 my $class = $self->ValuesClass;
519 return 0 if $class eq 'RT::CustomFieldValues';
525 return '' unless $self->IsSelectionType;
527 my $class = $self->FirstAttribute( 'ValuesClass' );
528 $class = $class->Content if $class;
529 return $class || 'RT::CustomFieldValues';
534 my $class = shift || 'RT::CustomFieldValues';
536 if( $class eq 'RT::CustomFieldValues' ) {
537 return $self->DeleteAttribute( 'ValuesClass' );
539 return $self->SetAttribute( Name => 'ValuesClass', Content => $class );
543 =head2 FriendlyType [TYPE, MAX_VALUES]
545 Returns a localized human-readable version of the custom field type.
546 If a custom field type is specified as the parameter, the friendly type for that type will be returned
553 my $type = @_ ? shift : $self->Type;
554 my $max = @_ ? shift : $self->MaxValues;
555 $max = 0 unless $max;
557 if (my $friendly_type = $FieldTypes{$type}[$max>2 ? 2 : $max]) {
558 return ( $self->loc( $friendly_type, $max ) );
561 return ( $self->loc( $type ) );
565 sub FriendlyTypeComposite {
567 my $composite = shift || $self->TypeComposite;
568 return $self->FriendlyType(split(/-/, $composite, 2));
572 =head2 ValidateType TYPE
574 Takes a single string. returns true if that string is a value
584 if ( $type =~ s/(?:Single|Multiple)$// ) {
585 $RT::Logger->warning( "Prefix 'Single' and 'Multiple' to Type deprecated, use MaxValues instead at (". join(":",caller).")");
588 if ( $FieldTypes{$type} ) {
600 if ($type =~ s/(?:(Single)|Multiple)$//) {
601 $RT::Logger->warning("'Single' and 'Multiple' on SetType deprecated, use SetMaxValues instead at (". join(":",caller).")");
602 $self->SetMaxValues($1 ? 1 : 0);
604 $self->SUPER::SetType($type);
607 =head2 SetPattern STRING
609 Takes a single string representing a regular expression. Performs basic
610 validation on that regex, and sets the C<Pattern> field for the CF if it
619 my ($ok, $msg) = $self->_IsValidRegex($regex);
621 return $self->SUPER::SetPattern($regex);
624 return (0, $self->loc("Invalid pattern: [_1]", $msg));
628 =head2 _IsValidRegex(Str $regex) returns (Bool $success, Str $msg)
630 Tests if the string contains an invalid regex.
636 my $regex = shift or return (1, 'valid');
639 local $SIG{__DIE__} = sub { 1 };
640 local $SIG{__WARN__} = sub { 1 };
642 if (eval { qr/$regex/; 1 }) {
647 $err =~ s{[,;].*}{}; # strip debug info from error
656 Returns true if this CustomField only accepts a single value.
657 Returns false if it accepts multiple values
663 if (($self->MaxValues||0) == 1) {
671 sub UnlimitedValues {
673 if (($self->MaxValues||0) == 0) {
683 =head2 CurrentUserHasRight RIGHT
685 Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
689 sub CurrentUserHasRight {
693 return $self->CurrentUser->HasRight(
699 =head2 ACLEquivalenceObjects
701 Returns list of objects via which users can get rights on this custom field. For custom fields
702 these objects can be set using L<ContextObject|/"ContextObject and SetContextObject">.
706 sub ACLEquivalenceObjects {
709 my $ctx = $self->ContextObject
711 return ($ctx, $ctx->ACLEquivalenceObjects);
714 =head2 ContextObject and SetContextObject
716 Set or get a context for this object. It can be ticket, queue or another object
717 this CF applies to. Used for ACL control, for example SeeCustomField can be granted on
718 queue level to allow people to see all fields applied to the queue.
722 sub SetContextObject {
724 return $self->{'context_object'} = shift;
729 return $self->{'context_object'};
737 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
738 return ( 0, $self->loc('Permission Denied') );
740 return $self->SUPER::_Set( @_ );
750 Takes the name of a table column.
751 Returns its value as a string, if the user passes an ACL check
757 return undef unless $self->id;
759 # we need to do the rights check
760 unless ( $self->CurrentUserHasRight('SeeCustomField') ) {
762 "Permission denied. User #". $self->CurrentUser->id
763 ." has no SeeCustomField right on CF #". $self->id
767 return $self->__Value( @_ );
771 # {{{ sub SetDisabled
776 1 will cause this custom field to no longer be avaialble for objects.
777 0 will re-enable this field.
783 =head2 SetTypeComposite
785 Set this custom field's type and maximum values as a composite value
789 sub SetTypeComposite {
791 my $composite = shift;
793 my $old = $self->TypeComposite;
795 my ($type, $max_values) = split(/-/, $composite, 2);
796 if ( $type ne $self->Type ) {
797 my ($status, $msg) = $self->SetType( $type );
798 return ($status, $msg) unless $status;
800 if ( ($max_values || 0) != ($self->MaxValues || 0) ) {
801 my ($status, $msg) = $self->SetMaxValues( $max_values );
802 return ($status, $msg) unless $status;
804 return 1, $self->loc(
805 "Type changed from '[_1]' to '[_2]'",
806 $self->FriendlyTypeComposite( $old ),
807 $self->FriendlyTypeComposite( $composite ),
813 Autrijus: care to doc how LookupTypes work?
820 if ( $lookup ne $self->LookupType ) {
821 # Okay... We need to invalidate our existing relationships
822 my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
823 $ObjectCustomFields->LimitToCustomField($self->Id);
824 $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
826 return $self->SUPER::SetLookupType($lookup);
831 Returns a composite value composed of this object's type and maximum values
838 return join '-', ($self->Type || ''), ($self->MaxValues || 0);
841 =head2 TypeComposites
843 Returns an array of all possible composite values for custom fields.
849 return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
854 Returns an array of LookupTypes available
861 return keys %FRIENDLY_OBJECT_TYPES;
864 my @FriendlyObjectTypes = (
865 "[_1] objects", # loc
866 "[_1]'s [_2] objects", # loc
867 "[_1]'s [_2]'s [_3] objects", # loc
870 =head2 FriendlyTypeLookup
872 Returns a localized description of the type of this custom field
876 sub FriendlyLookupType {
878 my $lookup = shift || $self->LookupType;
880 return ($self->loc( $FRIENDLY_OBJECT_TYPES{$lookup} ))
881 if (defined $FRIENDLY_OBJECT_TYPES{$lookup} );
883 my @types = map { s/^RT::// ? $self->loc($_) : $_ }
884 grep { defined and length }
885 split( /-/, $lookup )
887 return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
891 =head2 AddToObject OBJECT
893 Add this custom field as a custom field for a single object, such as a queue or group.
903 my $id = $object->Id || 0;
905 unless (index($self->LookupType, ref($object)) == 0) {
906 return ( 0, $self->loc('Lookup type mismatch') );
909 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
910 return ( 0, $self->loc('Permission Denied') );
913 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
914 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
915 if ( $ObjectCF->Id ) {
916 return ( 0, $self->loc("That is already the current value") );
919 $ObjectCF->Create( ObjectId => $id, CustomField => $self->Id );
921 return ( $oid, $msg );
925 =head2 RemoveFromObject OBJECT
927 Remove this custom field for a single object, such as a queue or group.
934 sub RemoveFromObject {
937 my $id = $object->Id || 0;
939 unless (index($self->LookupType, ref($object)) == 0) {
940 return ( 0, $self->loc('Object type mismatch') );
943 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
944 return ( 0, $self->loc('Permission Denied') );
947 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
948 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
949 unless ( $ObjectCF->Id ) {
950 return ( 0, $self->loc("This custom field does not apply to that object") );
952 # XXX: Delete doesn't return anything
953 my ( $oid, $msg ) = $ObjectCF->Delete;
955 return ( $oid, $msg );
958 # {{{ AddValueForObject
960 =head2 AddValueForObject HASH
962 Adds a custom field value for a record object of some kind.
963 Takes a param hash of
977 sub AddValueForObject {
982 LargeContent => undef,
983 ContentType => undef,
986 my $obj = $args{'Object'} or return ( 0, $self->loc('Invalid object') );
988 unless ( $self->CurrentUserHasRight('ModifyCustomField') ) {
989 return ( 0, $self->loc('Permission Denied') );
992 unless ( $self->MatchPattern($args{'Content'}) ) {
993 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
996 $RT::Handle->BeginTransaction;
998 if ( $self->MaxValues ) {
999 my $current_values = $self->ValuesForObject($obj);
1000 my $extra_values = ( $current_values->Count + 1 ) - $self->MaxValues;
1002 # (The +1 is for the new value we're adding)
1004 # If we have a set of current values and we've gone over the maximum
1005 # allowed number of values, we'll need to delete some to make room.
1006 # which former values are blown away is not guaranteed
1008 while ($extra_values) {
1009 my $extra_item = $current_values->Next;
1010 unless ( $extra_item->id ) {
1011 $RT::Logger->crit( "We were just asked to delete "
1012 ."a custom field value that doesn't exist!" );
1013 $RT::Handle->Rollback();
1016 $extra_item->Delete;
1020 my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
1021 my $val = $newval->Create(
1022 ObjectType => ref($obj),
1023 ObjectId => $obj->Id,
1024 Content => $args{'Content'},
1025 LargeContent => $args{'LargeContent'},
1026 ContentType => $args{'ContentType'},
1027 CustomField => $self->Id
1031 $RT::Handle->Rollback();
1032 return ($val, $self->loc("Couldn't create record"));
1035 $RT::Handle->Commit();
1044 =head2 MatchPattern STRING
1046 Tests the incoming string against the Pattern of this custom field object
1047 and returns a boolean; returns true if the Pattern is empty.
1053 my $regex = $self->Pattern or return 1;
1055 return (( defined $_[0] ? $_[0] : '') =~ $regex);
1061 # {{{ FriendlyPattern
1063 =head2 FriendlyPattern
1065 Prettify the pattern of this custom field, by taking the text in C<(?#text)>
1070 sub FriendlyPattern {
1072 my $regex = $self->Pattern;
1074 return '' unless length $regex;
1075 if ( $regex =~ /\(\?#([^)]*)\)/ ) {
1076 return '[' . $self->loc($1) . ']';
1086 # {{{ DeleteValueForObject
1088 =head2 DeleteValueForObject HASH
1090 Deletes a custom field value for a ticket. Takes a param hash of Object and Content
1092 Returns a tuple of (STATUS, MESSAGE). If the call succeeded, the STATUS is true. otherwise it's false
1096 sub DeleteValueForObject {
1098 my %args = ( Object => undef,
1104 unless ($self->CurrentUserHasRight('ModifyCustomField')) {
1105 return (0, $self->loc('Permission Denied'));
1108 my $oldval = RT::ObjectCustomFieldValue->new($self->CurrentUser);
1110 if (my $id = $args{'Id'}) {
1113 unless ($oldval->id) {
1114 $oldval->LoadByObjectContentAndCustomField(
1115 Object => $args{'Object'},
1116 Content => $args{'Content'},
1117 CustomField => $self->Id,
1122 # check to make sure we found it
1123 unless ($oldval->Id) {
1124 return(0, $self->loc("Custom field value [_1] could not be found for custom field [_2]", $args{'Content'}, $self->Name));
1127 # for single-value fields, we need to validate that empty string is a valid value for it
1128 if ( $self->SingleValue and not $self->MatchPattern( '' ) ) {
1129 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
1134 my $ret = $oldval->Delete();
1136 return(0, $self->loc("Custom field value could not be found"));
1138 return($oldval->Id, $self->loc("Custom field value deleted"));
1142 =head2 ValuesForObject OBJECT
1144 Return an L<RT::ObjectCustomFieldValues> object containing all of this custom field's values for OBJECT
1148 sub ValuesForObject {
1152 my $values = new RT::ObjectCustomFieldValues($self->CurrentUser);
1153 unless ($self->CurrentUserHasRight('SeeCustomField')) {
1154 # Return an empty object if they have no rights to see
1159 $values->LimitToCustomField($self->Id);
1160 $values->LimitToEnabled();
1161 $values->LimitToObject($object);
1167 =head2 _ForObjectType PATH FRIENDLYNAME
1169 Tell RT that a certain object accepts custom fields
1173 'RT::Queue-RT::Ticket' => "Tickets", # loc
1174 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc
1175 'RT::User' => "Users", # loc
1176 'RT::Group' => "Groups", # loc
1178 This is a class method.
1182 sub _ForObjectType {
1185 my $friendly_name = shift;
1187 $FRIENDLY_OBJECT_TYPES{$path} = $friendly_name;
1192 =head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue)
1194 Gets or sets the C<IncludeContentForValue> for this custom field. RT
1195 uses this field to automatically include content into the user's browser
1196 as they display records with custom fields in RT.
1200 sub SetIncludeContentForValue {
1201 shift->IncludeContentForValue(@_);
1203 sub IncludeContentForValue{
1205 $self->_URLTemplate('IncludeContentForValue', @_);
1210 =head2 LinkValueTo [VALUE] (and SetLinkValueTo)
1212 Gets or sets the C<LinkValueTo> for this custom field. RT
1213 uses this field to make custom field values into hyperlinks in the user's
1214 browser as they display records with custom fields in RT.
1219 sub SetLinkValueTo {
1220 shift->LinkValueTo(@_);
1225 $self->_URLTemplate('LinkValueTo', @_);
1230 =head2 _URLTemplate NAME [VALUE]
1232 With one argument, returns the _URLTemplate named C<NAME>, but only if
1233 the current user has the right to see this custom field.
1235 With two arguments, attemptes to set the relevant template value.
1241 my $template_name = shift;
1245 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
1246 return ( 0, $self->loc('Permission Denied') );
1248 $self->SetAttribute( Name => $template_name, Content => $value );
1249 return ( 1, $self->loc('Updated') );
1251 unless ( $self->id && $self->CurrentUserHasRight('SeeCustomField') ) {
1255 my @attr = $self->Attributes->Named($template_name);
1256 my $attr = shift @attr;
1258 if ($attr) { return $attr->Content }
1267 return $self->DeleteAttribute( "BasedOn" )
1268 unless defined $value and length $value;
1270 my $cf = RT::CustomField->new( $self->CurrentUser );
1271 $cf->Load( ref $value ? $value->Id : $value );
1273 return (0, "Permission denied")
1274 unless $cf->Id && $cf->CurrentUserHasRight('SeeCustomField');
1276 return $self->AddAttribute(
1278 Description => "Custom field whose CF we depend on",
1285 my $obj = RT::CustomField->new( $self->CurrentUser );
1287 my $attribute = $self->FirstAttribute("BasedOn");
1288 $obj->Load($attribute->Content) if defined $attribute;