1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2007 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/copyleft/gpl.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 }}}
48 package RT::CustomField;
51 no warnings qw(redefine);
53 use vars qw(%FieldTypes $RIGHTS %FRIENDLY_OBJECT_TYPES);
55 use RT::CustomFieldValues;
56 use RT::ObjectCustomFieldValues;
61 'Select multiple values', # loc
62 'Select one value', # loc
63 'Select up to [_1] values', # loc
66 'Enter multiple values', # loc
67 'Enter one value', # loc
68 'Enter up to [_1] values', # loc
71 'Fill in multiple text areas', # loc
72 'Fill in one text area', # loc
73 'Fill in up to [_1] text areas',# loc
76 'Fill in multiple wikitext areas', # loc
77 'Fill in one wikitext area', # loc
78 'Fill in up to [_1] wikitext areas',# loc
81 'Upload multiple images', # loc
82 'Upload one image', # loc
83 'Upload up to [_1] images', # loc
86 'Upload multiple files', # loc
87 'Upload one file', # loc
88 'Upload up to [_1] files', # loc
91 'Combobox: Select or enter multiple values', # loc
92 'Combobox: Select or enter one value', # loc
93 'Combobox: Select or enter up to [_1] values', # loc
98 %FRIENDLY_OBJECT_TYPES = ();
100 RT::CustomField->_ForObjectType( 'RT::Queue-RT::Ticket' => "Tickets", ); #loc
101 RT::CustomField->_ForObjectType(
102 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", ); #loc
103 RT::CustomField->_ForObjectType( 'RT::User' => "Users", ); #loc
104 RT::CustomField->_ForObjectType( 'RT::Group' => "Groups", ); #loc
107 SeeCustomField => 'See custom fields', # loc_pair
108 AdminCustomField => 'Create, delete and modify custom fields', # loc_pair
109 ModifyCustomField => 'Add, delete and modify custom field values for objects' #loc_pair
113 # Tell RT::ACE that this sort of object can get acls granted
114 $RT::ACE::OBJECT_TYPES{'RT::CustomField'} = 1;
116 foreach my $right ( keys %{$RIGHTS} ) {
117 $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
120 sub AvailableRights {
127 RT::CustomField_Overlay
131 =head1 'CORE' METHODS
137 =head2 Create PARAMHASH
139 Create takes a hash of values and creates a row in the database:
144 varchar(255) 'Pattern'.
145 smallint(6) 'Repeated'.
146 varchar(255) 'Description'.
148 varchar(255) 'LookupType'.
149 smallint(6) 'Disabled'.
151 'LookupType' is generally the result of either
152 RT::Ticket->CustomFieldLookupType or RT::Transaction->CustomFieldLookupType
173 unless ($self->CurrentUser->HasRight(Object => $RT::System, Right => 'AdminCustomField')) {
174 return (0, $self->loc('Permission Denied'));
178 if ($args{TypeComposite}) {
179 @args{'Type', 'MaxValues'} = split(/-/, $args{TypeComposite}, 2);
181 elsif ($args{Type} =~ s/(?:(Single)|Multiple)$//) {
182 # old style Type string
183 $args{'MaxValues'} = $1 ? 1 : 0;
186 if ( !exists $args{'Queue'}) {
187 # do nothing -- things below are strictly backward compat
189 elsif ( ! $args{'Queue'} ) {
190 unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AssignCustomFields') ) {
191 return ( 0, $self->loc('Permission Denied') );
193 $args{'LookupType'} = 'RT::Queue-RT::Ticket';
196 my $queue = RT::Queue->new($self->CurrentUser);
197 $queue->Load($args{'Queue'});
198 unless ($queue->Id) {
199 return (0, $self->loc("Queue not found"));
201 unless ( $queue->CurrentUserHasRight('AssignCustomFields') ) {
202 return ( 0, $self->loc('Permission Denied') );
204 $args{'LookupType'} = 'RT::Queue-RT::Ticket';
205 $args{'Queue'} = $queue->Id;
208 my ($ok, $msg) = $self->_IsValidRegex($args{'Pattern'});
210 return (0, $self->loc("Invalid pattern: [_1]", $msg));
213 my $rv = $self->SUPER::Create(
214 Name => $args{'Name'},
215 Type => $args{'Type'},
216 MaxValues => $args{'MaxValues'},
217 Pattern => $args{'Pattern'},
218 Description => $args{'Description'},
219 Disabled => $args{'Disabled'},
220 LookupType => $args{'LookupType'},
221 Repeated => $args{'Repeated'},
224 return $rv unless exists $args{'Queue'};
226 # Compat code -- create a new ObjectCustomField mapping
227 my $OCF = RT::ObjectCustomField->new($self->CurrentUser);
229 CustomField => $self->Id,
230 ObjectId => $args{'Queue'},
238 Load a custom field. If the value handed in is an integer, load by custom field ID. Otherwise, Load by name.
247 if ($id =~ /^\d+$/) {
248 return ($self->SUPER::Load($id));
250 return($self->LoadByName(Name => $id));
257 =head2 LoadByName (Queue => QUEUEID, Name => NAME)
259 Loads the Custom field named NAME.
261 If a Queue parameter is specified, only look for ticket custom fields tied to that Queue.
263 If the Queue parameter is '0', look for global ticket custom fields.
265 If no queue parameter is specified, look for any and all custom fields with this name.
267 BUG/TODO, this won't let you specify that you only want user or group CFs.
271 # Compatibility for API change after 3.0 beta 1
272 *LoadNameAndQueue = \&LoadByName;
273 # Change after 3.4 beta.
274 *LoadByNameAndQueue = \&LoadByName;
284 # if we're looking for a queue by name, make it a number
285 if (defined $args{'Queue'} && $args{'Queue'} !~ /^\d+$/) {
286 my $QueueObj = RT::Queue->new($self->CurrentUser);
287 $QueueObj->Load($args{'Queue'});
288 $args{'Queue'} = $QueueObj->Id;
291 # XXX - really naive implementation. Slow. - not really. still just one query
293 my $CFs = RT::CustomFields->new($self->CurrentUser);
295 $CFs->Limit( FIELD => 'Name', VALUE => $args{'Name'} );
296 # Don't limit to queue if queue is 0. Trying to do so breaks
297 # RT::Group type CFs.
298 if (defined $args{'Queue'}) {
299 $CFs->LimitToQueue( $args{'Queue'} );
302 # When loading by name, it's ok if they're disabled. That's not a big deal.
303 $CFs->{'find_disabled_rows'}=1;
305 # We only want one entry.
306 $CFs->RowsPerPage(1);
307 unless ($CFs->First) {
310 return($self->Load($CFs->First->id));
316 # {{{ Dealing with custom field values
320 use_ok(RT::CustomField);
321 ok(my $cf = RT::CustomField->new($RT::SystemUser));
322 ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF',
325 Description => 'A Testing custom field',
326 Type=> 'SelectSingle'), 'Created a global CustomField');
327 ok($id != 0, 'Global custom field correctly created');
328 ok ($cf->SingleValue);
329 is($cf->Type, 'Select');
330 is($cf->MaxValues, 1);
332 my ($val, $msg) = $cf->SetMaxValues('0');
334 is($cf->Type, 'Select');
335 is($cf->MaxValues, 0);
336 ok(!$cf->SingleValue );
337 ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type");
338 ok($bogus_val == 0, "Unable to set a custom field's type to a bogus type");
340 ok(my $bad_cf = RT::CustomField->new($RT::SystemUser));
341 ok(my ($bad_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad',
344 Description => 'A Testing custom field with a bogus Type',
345 Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type');
346 ok($bad_id == 0, 'Global custom field correctly decided to not create a cf with a bogus type ');
356 Create a new value for this CustomField. Takes a paramhash containing the elements Name, Description and SortOrder
360 ok(my $cf = RT::CustomField->new($RT::SystemUser));
363 ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'));
365 ok (my ($delval, $delmsg) = $cf->DeleteValue($val));
366 ok ($delval,"Deleting a cf value: $delmsg");
376 unless ($self->CurrentUserHasRight('AdminCustomField')) {
377 return (0, $self->loc('Permission Denied'));
381 if ( !defined $args{'Name'} || $args{'Name'} eq '' ) {
382 return(0, $self->loc("Can't add a custom field value without a name"));
385 my $newval = RT::CustomFieldValue->new($self->CurrentUser);
386 return($newval->Create(%args, CustomField => $self->Id));
394 =head2 DeleteValue ID
396 Deletes a value from this custom field by id.
398 Does not remove this value for any article which has had it selected
405 unless ($self->CurrentUserHasRight('AdminCustomField')) {
406 return (0, $self->loc('Permission Denied'));
409 my $val_to_del = RT::CustomFieldValue->new($self->CurrentUser);
410 $val_to_del->Load($id);
411 unless ($val_to_del->Id) {
412 return (0, $self->loc("Couldn't find that value"));
414 unless ($val_to_del->CustomField == $self->Id) {
415 return (0, $self->loc("That is not a value for this custom field"));
418 my $retval = $val_to_del->Delete();
420 return ($retval, $self->loc("Custom field value deleted"));
422 return(0, $self->loc("Custom field value could not be deleted"));
432 Return a CustomFieldeValues object of all acceptable values for this Custom Field.
437 *ValuesObj = \&Values;
442 my $cf_values = RT::CustomFieldValues->new($self->CurrentUser);
443 # if the user has no rights, return an empty object
444 if ($self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
445 $cf_values->LimitToCustomField($self->Id);
454 # {{{ Ticket related routines
456 # {{{ ValuesForTicket
458 =head2 ValuesForTicket TICKET
460 Returns a RT::ObjectCustomFieldValues object of this Field's values for TICKET.
461 TICKET is a ticket id.
463 This is deprecated -- use ValuesForObject instead.
468 sub ValuesForTicket {
470 my $ticket_id = shift;
472 $RT::Logger->debug( ref($self) . " -> ValuesForTicket deprecated in favor of ValuesForObject at (". join(":",caller).")");
473 my $ticket = RT::Ticket->new($self->CurrentUser);
474 $ticket->Load($ticket_id);
476 return $self->ValuesForObject($ticket);
481 # {{{ AddValueForTicket
483 =head2 AddValueForTicket HASH
485 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
487 This is deprecated -- use AddValueForObject instead.
491 sub AddValueForTicket {
493 my %args = ( Ticket => undef,
496 $RT::Logger->debug( ref($self) . " -> AddValueForTicket deprecated in favor of AddValueForObject at (". join(":",caller).")");
499 my $ticket = RT::Ticket->new($self->CurrentUser);
500 $ticket->Load($args{'Ticket'});
501 return($self->AddValueForObject(Content => $args{'Content'}, Object => $ticket,@_));
508 # {{{ DeleteValueForTicket
510 =head2 DeleteValueForTicket HASH
512 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
514 This is deprecated -- use DeleteValueForObject instead.
518 sub DeleteValueForTicket {
520 my %args = ( Ticket => undef,
524 $RT::Logger->debug( ref($self) . " -> DeleteValueForTicket deprecated in favor of DeleteValueForObject at (". join(":",caller).")");
527 my $ticket = RT::Ticket->new($self->CurrentUser);
528 $ticket->load($args{'Ticket'});
529 return ($self->DeleteValueForObject(Object => $ticket, Content => $args{'Content'}, @_));
537 =head2 ValidateQueue Queue
539 Make sure that the queue specified is a valid queue name
547 if ($id eq '0') { # 0 means "Global" null would _not_ be ok.
551 my $q = RT::Queue->new($RT::SystemUser);
566 Retuns an array of the types of CustomField that are supported
571 return (keys %FieldTypes);
576 # {{{ IsSelectionType
578 =head2 IsSelectionType
580 Retuns a boolean value indicating whether the C<Values> method makes sense
581 to this Custom Field.
585 sub IsSelectionType {
587 $self->Type =~ /(?:Select|Combobox)/;
593 =head2 FriendlyType [TYPE, MAX_VALUES]
595 Returns a localized human-readable version of the custom field type.
596 If a custom field type is specified as the parameter, the friendly type for that type will be returned
603 my $type = @_ ? shift : $self->Type;
604 my $max = @_ ? shift : $self->MaxValues;
606 if (my $friendly_type = $FieldTypes{$type}[$max>2 ? 2 : $max]) {
607 return ( $self->loc( $friendly_type, $max ) );
610 return ( $self->loc( $type ) );
614 sub FriendlyTypeComposite {
616 my $composite = shift || $self->TypeComposite;
617 return $self->FriendlyType(split(/-/, $composite, 2));
621 =head2 ValidateType TYPE
623 Takes a single string. returns true if that string is a value
628 ok(my $cf = RT::CustomField->new($RT::SystemUser));
629 ok($cf->ValidateType('SelectSingle'));
630 ok($cf->ValidateType('SelectMultiple'));
631 ok(!$cf->ValidateType('SelectFooMultiple'));
641 if ($type =~ s/(?:Single|Multiple)$//) {
642 $RT::Logger->warning( "Prefix 'Single' and 'Multiple' to Type deprecated, use MaxValues instead at (". join(":",caller).")");
645 if( $FieldTypes{$type}) {
657 if ($type =~ s/(?:(Single)|Multiple)$//) {
658 $RT::Logger->warning("'Single' and 'Multiple' on SetType deprecated, use SetMaxValues instead at (". join(":",caller).")");
659 $self->SetMaxValues($1 ? 1 : 0);
661 $self->SUPER::SetType($type);
664 =head2 SetPattern STRING
666 Takes a single string representing a regular expression. Performs basic
667 validation on that regex, and sets the C<Pattern> field for the CF if it
676 my ($ok, $msg) = $self->_IsValidRegex($regex);
678 return $self->SUPER::SetPattern($regex);
681 return (0, $self->loc("Invalid pattern: [_1]", $msg));
685 =head2 _IsValidRegex(Str $regex) returns (Bool $success, Str $msg)
687 Tests if the string contains an invalid regex.
693 my $regex = shift or return (1, 'valid');
696 $SIG{__DIE__} = sub { 1 };
697 $SIG{__WARN__} = sub { 1 };
699 if (eval { qr/$regex/; 1 }) {
704 $err =~ s{[,;].*}{}; # strip debug info from error
713 Returns true if this CustomField only accepts a single value.
714 Returns false if it accepts multiple values
720 if ($self->MaxValues == 1) {
728 sub UnlimitedValues {
730 if ($self->MaxValues == 0) {
740 # {{{ sub CurrentUserHasRight
742 =head2 CurrentUserHasRight RIGHT
744 Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
748 sub CurrentUserHasRight {
752 return $self->CurrentUser->HasRight(
765 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
766 return ( 0, $self->loc('Permission Denied') );
768 return ( $self->SUPER::_Set(@_) );
778 Takes the name of a table column.
779 Returns its value as a string, if the user passes an ACL check
788 # we need to do the rights check
789 unless ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
792 return ( $self->__Value($field) );
797 # {{{ sub SetDisabled
802 1 will cause this custom field to no longer be avaialble for tickets.
803 0 will re-enable this queue
810 $RT::Logger->debug( ref($_[0]) . " -> Queue deprecated at (". join(":",caller).")");
816 $RT::Logger->debug( ref($_[0]) . " -> SetQueue deprecated at (". join(":",caller).")");
822 $RT::Logger->debug( ref($_[0]) . " -> QueueObj deprecated at (". join(":",caller).")");
827 =head2 SetTypeComposite
829 Set this custom field's type and maximum values as a composite value
834 sub SetTypeComposite {
836 my $composite = shift;
837 my ($type, $max_values) = split(/-/, $composite, 2);
838 $self->SetType($type);
839 $self->SetMaxValues($max_values);
844 Autrijus: care to doc how LookupTypes work?
851 if ($lookup ne $self->LookupType) {
852 # Okay... We need to invalidate our existing relationships
853 my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
854 $ObjectCustomFields->LimitToCustomField($self->Id);
855 $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
857 $self->SUPER::SetLookupType($lookup);
862 Returns a composite value composed of this object's type and maximum values
869 join('-', $self->Type, $self->MaxValues);
872 =head2 TypeComposites
874 Returns an array of all possible composite values for custom fields.
880 return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
885 Returns an array of LookupTypes available
892 return keys %FRIENDLY_OBJECT_TYPES;
895 my @FriendlyObjectTypes = (
896 "[_1] objects", # loc
897 "[_1]'s [_2] objects", # loc
898 "[_1]'s [_2]'s [_3] objects", # loc
901 =head2 FriendlyTypeLookup
905 sub FriendlyLookupType {
907 my $lookup = shift || $self->LookupType;
909 return ($self->loc( $FRIENDLY_OBJECT_TYPES{$lookup} ))
910 if (defined $FRIENDLY_OBJECT_TYPES{$lookup} );
912 my @types = map { s/^RT::// ? $self->loc($_) : $_ }
913 grep { defined and length }
914 split( /-/, $lookup )
916 return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
920 =head2 AddToObject OBJECT
922 Add this custom field as a custom field for a single object, such as a queue or group.
932 my $id = $object->Id || 0;
934 unless (index($self->LookupType, ref($object)) == 0) {
935 return ( 0, $self->loc('Lookup type mismatch') );
938 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
939 return ( 0, $self->loc('Permission Denied') );
942 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
944 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
945 if ( $ObjectCF->Id ) {
946 return ( 0, $self->loc("That is already the current value") );
949 $ObjectCF->Create( ObjectId => $id, CustomField => $self->Id );
951 return ( $oid, $msg );
955 =head2 RemoveFromObject OBJECT
957 Remove this custom field for a single object, such as a queue or group.
964 sub RemoveFromObject {
967 my $id = $object->Id || 0;
969 unless (index($self->LookupType, ref($object)) == 0) {
970 return ( 0, $self->loc('Object type mismatch') );
973 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
974 return ( 0, $self->loc('Permission Denied') );
977 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
979 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
980 unless ( $ObjectCF->Id ) {
981 return ( 0, $self->loc("This custom field does not apply to that object") );
983 # XXX: Delete doesn't return anything
984 my ( $oid, $msg ) = $ObjectCF->Delete;
986 return ( $oid, $msg );
989 # {{{ AddValueForObject
991 =head2 AddValueForObject HASH
993 Adds a custom field value for a record object of some kind.
994 Takes a param hash of
1008 sub AddValueForObject {
1013 LargeContent => undef,
1014 ContentType => undef,
1017 my $obj = $args{'Object'} or return;
1019 unless ( $self->CurrentUserHasRight('ModifyCustomField') ) {
1020 return ( 0, $self->loc('Permission Denied') );
1023 unless ( $self->MatchPattern($args{Content}) ) {
1024 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
1027 $RT::Handle->BeginTransaction;
1029 my $current_values = $self->ValuesForObject($obj);
1031 if ( $self->MaxValues ) {
1032 my $extra_values = ( $current_values->Count + 1 ) - $self->MaxValues;
1034 # (The +1 is for the new value we're adding)
1036 # If we have a set of current values and we've gone over the maximum
1037 # allowed number of values, we'll need to delete some to make room.
1038 # which former values are blown away is not guaranteed
1040 while ($extra_values) {
1041 my $extra_item = $current_values->Next;
1043 unless ( $extra_item->id ) {
1045 "We were just asked to delete a custom fieldvalue that doesn't exist!"
1047 $RT::Handle->Rollback();
1050 $extra_item->Delete;
1055 my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
1056 my $val = $newval->Create(
1057 ObjectType => ref($obj),
1058 ObjectId => $obj->Id,
1059 Content => $args{'Content'},
1060 LargeContent => $args{'LargeContent'},
1061 ContentType => $args{'ContentType'},
1062 CustomField => $self->Id
1066 $RT::Handle->Rollback();
1070 $RT::Handle->Commit();
1079 =head2 MatchPattern STRING
1081 Tests the incoming string against the Pattern of this custom field object
1082 and returns a boolean; returns true if the Pattern is empty.
1088 my $regex = $self->Pattern;
1090 return 1 if !length($regex);
1091 return ($_[0] =~ $regex);
1097 # {{{ FriendlyPattern
1099 =head2 FriendlyPattern
1101 Prettify the pattern of this custom field, by taking the text in C<(?#text)>
1106 sub FriendlyPattern {
1108 my $regex = $self->Pattern;
1110 return '' if !length($regex);
1111 if ($regex =~ /\(\?#([^)]*)\)/) {
1112 return '[' . $self->loc($1) . ']';
1122 # {{{ DeleteValueForObject
1124 =head2 DeleteValueForObject HASH
1126 Deletes a custom field value for a ticket. Takes a param hash of Object and Content
1128 Returns a tuple of (STATUS, MESSAGE). If the call succeeded, the STATUS is true. otherwise it's false
1132 sub DeleteValueForObject {
1134 my %args = ( Object => undef,
1140 unless ($self->CurrentUserHasRight('ModifyCustomField')) {
1141 return (0, $self->loc('Permission Denied'));
1144 my $oldval = RT::ObjectCustomFieldValue->new($self->CurrentUser);
1146 if (my $id = $args{'Id'}) {
1149 unless ($oldval->id) {
1150 $oldval->LoadByObjectContentAndCustomField(
1151 Object => $args{'Object'},
1152 Content => $args{'Content'},
1153 CustomField => $self->Id,
1158 # check to make sure we found it
1159 unless ($oldval->Id) {
1160 return(0, $self->loc("Custom field value [_1] could not be found for custom field [_2]", $args{'Content'}, $self->Name));
1163 # for single-value fields, we need to validate that empty string is a valid value for it
1164 if ( $self->SingleValue and not $self->MatchPattern( '' ) ) {
1165 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
1170 my $ret = $oldval->Delete();
1172 return(0, $self->loc("Custom field value could not be found"));
1174 return($oldval->Id, $self->loc("Custom field value deleted"));
1178 =head2 ValuesForObject OBJECT
1180 Return an RT::ObjectCustomFieldValues object containing all of this custom field's values for OBJECT
1184 sub ValuesForObject {
1188 my $values = new RT::ObjectCustomFieldValues($self->CurrentUser);
1189 unless ($self->CurrentUserHasRight('SeeCustomField')) {
1190 # Return an empty object if they have no rights to see
1195 $values->LimitToCustomField($self->Id);
1196 $values->LimitToEnabled();
1197 $values->LimitToObject($object);
1203 =head2 _ForObjectType PATH FRIENDLYNAME
1205 Tell RT that a certain object accepts custom fields
1209 'RT::Queue-RT::Ticket' => "Tickets", # loc
1210 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc
1211 'RT::User' => "Users", # loc
1212 'RT::Group' => "Groups", # loc
1214 This is a class method.
1218 sub _ForObjectType {
1221 my $friendly_name = shift;
1223 $FRIENDLY_OBJECT_TYPES{$path} = $friendly_name;
1228 =head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue)
1230 Gets or sets the C<IncludeContentForValue> for this custom field. RT
1231 uses this field to automatically include content into the user's browser
1232 as they display records with custom fields in RT.
1236 sub SetIncludeContentForValue {
1237 shift->IncludeContentForValue(@_);
1239 sub IncludeContentForValue{
1241 $self->_URLTemplate('IncludeContentForValue', @_);
1246 =head2 LinkValueTo [VALUE] (and SetLinkValueTo)
1248 Gets or sets the C<LinkValueTo> for this custom field. RT
1249 uses this field to make custom field values into hyperlinks in the user's
1250 browser as they display records with custom fields in RT.
1255 sub SetLinkValueTo {
1256 shift->LinkValueTo(@_);
1261 $self->_URLTemplate('LinkValueTo', @_);
1266 =head2 _URLTemplate NAME [VALUE]
1268 With one argument, returns the _URLTemplate named C<NAME>, but only if
1269 the current user has the right to see this custom field.
1271 With two arguments, attemptes to set the relevant template value.
1279 my $template_name = shift;
1283 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
1284 return ( 0, $self->loc('Permission Denied') );
1286 $self->SetAttribute( Name => $template_name, Content => $value );
1287 return ( 1, $self->loc('Updated') );
1289 unless ( $self->id && $self->CurrentUserHasRight('SeeCustomField') ) {
1293 my @attr = $self->Attributes->Named($template_name);
1294 my $attr = shift @attr;
1296 if ($attr) { return $attr->Content }