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 }}}
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 Will load a Disabled Custom Field even if there is a non-disabled Custom Field
264 If a Queue parameter is specified, only look for ticket custom fields tied to that Queue.
266 If the Queue parameter is '0', look for global ticket custom fields.
268 If no queue parameter is specified, look for any and all custom fields with this name.
270 BUG/TODO, this won't let you specify that you only want user or group CFs.
274 # Compatibility for API change after 3.0 beta 1
275 *LoadNameAndQueue = \&LoadByName;
276 # Change after 3.4 beta.
277 *LoadByNameAndQueue = \&LoadByName;
287 # if we're looking for a queue by name, make it a number
288 if (defined $args{'Queue'} && $args{'Queue'} !~ /^\d+$/) {
289 my $QueueObj = RT::Queue->new($self->CurrentUser);
290 $QueueObj->Load($args{'Queue'});
291 $args{'Queue'} = $QueueObj->Id;
294 # XXX - really naive implementation. Slow. - not really. still just one query
296 my $CFs = RT::CustomFields->new($self->CurrentUser);
298 $CFs->Limit( FIELD => 'Name', VALUE => $args{'Name'}, CASESENSITIVE => 0);
299 # Don't limit to queue if queue is 0. Trying to do so breaks
300 # RT::Group type CFs.
301 if (defined $args{'Queue'}) {
302 $CFs->LimitToQueue( $args{'Queue'} );
305 # When loading by name, it's ok if they're disabled. That's not a big deal.
306 $CFs->{'find_disabled_rows'}=1;
308 # We only want one entry.
309 $CFs->RowsPerPage(1);
310 unless ($CFs->First) {
313 return($self->Load($CFs->First->id));
319 # {{{ Dealing with custom field values
323 use_ok(RT::CustomField);
324 ok(my $cf = RT::CustomField->new($RT::SystemUser));
325 ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF',
328 Description => 'A Testing custom field',
329 Type=> 'SelectSingle'), 'Created a global CustomField');
330 ok($id != 0, 'Global custom field correctly created');
331 ok ($cf->SingleValue);
332 is($cf->Type, 'Select');
333 is($cf->MaxValues, 1);
335 my ($val, $msg) = $cf->SetMaxValues('0');
337 is($cf->Type, 'Select');
338 is($cf->MaxValues, 0);
339 ok(!$cf->SingleValue );
340 ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type");
341 ok($bogus_val == 0, "Unable to set a custom field's type to a bogus type");
343 ok(my $bad_cf = RT::CustomField->new($RT::SystemUser));
344 ok(my ($bad_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad',
347 Description => 'A Testing custom field with a bogus Type',
348 Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type');
349 ok($bad_id == 0, 'Global custom field correctly decided to not create a cf with a bogus type ');
359 Create a new value for this CustomField. Takes a paramhash containing the elements Name, Description and SortOrder
363 ok(my $cf = RT::CustomField->new($RT::SystemUser));
366 ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'));
368 ok (my ($delval, $delmsg) = $cf->DeleteValue($val));
369 ok ($delval,"Deleting a cf value: $delmsg");
379 unless ($self->CurrentUserHasRight('AdminCustomField')) {
380 return (0, $self->loc('Permission Denied'));
384 if ( !defined $args{'Name'} || $args{'Name'} eq '' ) {
385 return(0, $self->loc("Can't add a custom field value without a name"));
388 my $newval = RT::CustomFieldValue->new($self->CurrentUser);
389 return($newval->Create(%args, CustomField => $self->Id));
397 =head2 DeleteValue ID
399 Deletes a value from this custom field by id.
401 Does not remove this value for any article which has had it selected
408 unless ($self->CurrentUserHasRight('AdminCustomField')) {
409 return (0, $self->loc('Permission Denied'));
412 my $val_to_del = RT::CustomFieldValue->new($self->CurrentUser);
413 $val_to_del->Load($id);
414 unless ($val_to_del->Id) {
415 return (0, $self->loc("Couldn't find that value"));
417 unless ($val_to_del->CustomField == $self->Id) {
418 return (0, $self->loc("That is not a value for this custom field"));
421 my $retval = $val_to_del->Delete();
423 return ($retval, $self->loc("Custom field value deleted"));
425 return(0, $self->loc("Custom field value could not be deleted"));
435 Return a CustomFieldeValues object of all acceptable values for this Custom Field.
440 *ValuesObj = \&Values;
445 my $cf_values = RT::CustomFieldValues->new($self->CurrentUser);
446 # if the user has no rights, return an empty object
447 if ($self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
448 $cf_values->LimitToCustomField($self->Id);
457 # {{{ Ticket related routines
459 # {{{ ValuesForTicket
461 =head2 ValuesForTicket TICKET
463 Returns a RT::ObjectCustomFieldValues object of this Field's values for TICKET.
464 TICKET is a ticket id.
466 This is deprecated -- use ValuesForObject instead.
471 sub ValuesForTicket {
473 my $ticket_id = shift;
475 $RT::Logger->debug( ref($self) . " -> ValuesForTicket deprecated in favor of ValuesForObject at (". join(":",caller).")");
476 my $ticket = RT::Ticket->new($self->CurrentUser);
477 $ticket->Load($ticket_id);
479 return $self->ValuesForObject($ticket);
484 # {{{ AddValueForTicket
486 =head2 AddValueForTicket HASH
488 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
490 This is deprecated -- use AddValueForObject instead.
494 sub AddValueForTicket {
496 my %args = ( Ticket => undef,
499 $RT::Logger->debug( ref($self) . " -> AddValueForTicket deprecated in favor of AddValueForObject at (". join(":",caller).")");
502 my $ticket = RT::Ticket->new($self->CurrentUser);
503 $ticket->Load($args{'Ticket'});
504 return($self->AddValueForObject(Content => $args{'Content'}, Object => $ticket,@_));
511 # {{{ DeleteValueForTicket
513 =head2 DeleteValueForTicket HASH
515 Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
517 This is deprecated -- use DeleteValueForObject instead.
521 sub DeleteValueForTicket {
523 my %args = ( Ticket => undef,
527 $RT::Logger->debug( ref($self) . " -> DeleteValueForTicket deprecated in favor of DeleteValueForObject at (". join(":",caller).")");
530 my $ticket = RT::Ticket->new($self->CurrentUser);
531 $ticket->load($args{'Ticket'});
532 return ($self->DeleteValueForObject(Object => $ticket, Content => $args{'Content'}, @_));
540 =head2 ValidateQueue Queue
542 Make sure that the queue specified is a valid queue name
550 if ($id eq '0') { # 0 means "Global" null would _not_ be ok.
554 my $q = RT::Queue->new($RT::SystemUser);
569 Retuns an array of the types of CustomField that are supported
574 return (keys %FieldTypes);
579 # {{{ IsSelectionType
581 =head2 IsSelectionType
583 Retuns a boolean value indicating whether the C<Values> method makes sense
584 to this Custom Field.
588 sub IsSelectionType {
590 $self->Type =~ /(?:Select|Combobox)/;
596 =head2 FriendlyType [TYPE, MAX_VALUES]
598 Returns a localized human-readable version of the custom field type.
599 If a custom field type is specified as the parameter, the friendly type for that type will be returned
606 my $type = @_ ? shift : $self->Type;
607 my $max = @_ ? shift : $self->MaxValues;
609 if (my $friendly_type = $FieldTypes{$type}[$max>2 ? 2 : $max]) {
610 return ( $self->loc( $friendly_type, $max ) );
613 return ( $self->loc( $type ) );
617 sub FriendlyTypeComposite {
619 my $composite = shift || $self->TypeComposite;
620 return $self->FriendlyType(split(/-/, $composite, 2));
624 =head2 ValidateType TYPE
626 Takes a single string. returns true if that string is a value
631 ok(my $cf = RT::CustomField->new($RT::SystemUser));
632 ok($cf->ValidateType('SelectSingle'));
633 ok($cf->ValidateType('SelectMultiple'));
634 ok(!$cf->ValidateType('SelectFooMultiple'));
644 if ($type =~ s/(?:Single|Multiple)$//) {
645 $RT::Logger->warning( "Prefix 'Single' and 'Multiple' to Type deprecated, use MaxValues instead at (". join(":",caller).")");
648 if( $FieldTypes{$type}) {
660 if ($type =~ s/(?:(Single)|Multiple)$//) {
661 $RT::Logger->warning("'Single' and 'Multiple' on SetType deprecated, use SetMaxValues instead at (". join(":",caller).")");
662 $self->SetMaxValues($1 ? 1 : 0);
664 $self->SUPER::SetType($type);
667 =head2 SetPattern STRING
669 Takes a single string representing a regular expression. Performs basic
670 validation on that regex, and sets the C<Pattern> field for the CF if it
679 my ($ok, $msg) = $self->_IsValidRegex($regex);
681 return $self->SUPER::SetPattern($regex);
684 return (0, $self->loc("Invalid pattern: [_1]", $msg));
688 =head2 _IsValidRegex(Str $regex) returns (Bool $success, Str $msg)
690 Tests if the string contains an invalid regex.
696 my $regex = shift or return (1, 'valid');
699 $SIG{__DIE__} = sub { 1 };
700 $SIG{__WARN__} = sub { 1 };
702 if (eval { qr/$regex/; 1 }) {
707 $err =~ s{[,;].*}{}; # strip debug info from error
716 Returns true if this CustomField only accepts a single value.
717 Returns false if it accepts multiple values
723 if ($self->MaxValues == 1) {
731 sub UnlimitedValues {
733 if ($self->MaxValues == 0) {
743 # {{{ sub CurrentUserHasRight
745 =head2 CurrentUserHasRight RIGHT
747 Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
751 sub CurrentUserHasRight {
755 return $self->CurrentUser->HasRight(
768 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
769 return ( 0, $self->loc('Permission Denied') );
771 return ( $self->SUPER::_Set(@_) );
781 Takes the name of a table column.
782 Returns its value as a string, if the user passes an ACL check
791 # we need to do the rights check
792 unless ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
795 return ( $self->__Value($field) );
800 # {{{ sub SetDisabled
805 1 will cause this custom field to no longer be avaialble for tickets.
806 0 will re-enable this queue
813 $RT::Logger->debug( ref($_[0]) . " -> Queue deprecated at (". join(":",caller).")");
819 $RT::Logger->debug( ref($_[0]) . " -> SetQueue deprecated at (". join(":",caller).")");
825 $RT::Logger->debug( ref($_[0]) . " -> QueueObj deprecated at (". join(":",caller).")");
830 =head2 SetTypeComposite
832 Set this custom field's type and maximum values as a composite value
837 sub SetTypeComposite {
839 my $composite = shift;
840 my ($type, $max_values) = split(/-/, $composite, 2);
841 $self->SetType($type);
842 $self->SetMaxValues($max_values);
847 Autrijus: care to doc how LookupTypes work?
854 if ($lookup ne $self->LookupType) {
855 # Okay... We need to invalidate our existing relationships
856 my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
857 $ObjectCustomFields->LimitToCustomField($self->Id);
858 $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
860 $self->SUPER::SetLookupType($lookup);
865 Returns a composite value composed of this object's type and maximum values
872 join('-', $self->Type, $self->MaxValues);
875 =head2 TypeComposites
877 Returns an array of all possible composite values for custom fields.
883 return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
888 Returns an array of LookupTypes available
895 return keys %FRIENDLY_OBJECT_TYPES;
898 my @FriendlyObjectTypes = (
899 "[_1] objects", # loc
900 "[_1]'s [_2] objects", # loc
901 "[_1]'s [_2]'s [_3] objects", # loc
904 =head2 FriendlyTypeLookup
908 sub FriendlyLookupType {
910 my $lookup = shift || $self->LookupType;
912 return ($self->loc( $FRIENDLY_OBJECT_TYPES{$lookup} ))
913 if (defined $FRIENDLY_OBJECT_TYPES{$lookup} );
915 my @types = map { s/^RT::// ? $self->loc($_) : $_ }
916 grep { defined and length }
917 split( /-/, $lookup )
919 return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
923 =head2 AddToObject OBJECT
925 Add this custom field as a custom field for a single object, such as a queue or group.
935 my $id = $object->Id || 0;
937 unless (index($self->LookupType, ref($object)) == 0) {
938 return ( 0, $self->loc('Lookup type mismatch') );
941 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
942 return ( 0, $self->loc('Permission Denied') );
945 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
947 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
948 if ( $ObjectCF->Id ) {
949 return ( 0, $self->loc("That is already the current value") );
952 $ObjectCF->Create( ObjectId => $id, CustomField => $self->Id );
954 return ( $oid, $msg );
958 =head2 RemoveFromObject OBJECT
960 Remove this custom field for a single object, such as a queue or group.
967 sub RemoveFromObject {
970 my $id = $object->Id || 0;
972 unless (index($self->LookupType, ref($object)) == 0) {
973 return ( 0, $self->loc('Object type mismatch') );
976 unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
977 return ( 0, $self->loc('Permission Denied') );
980 my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
982 $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
983 unless ( $ObjectCF->Id ) {
984 return ( 0, $self->loc("This custom field does not apply to that object") );
986 # XXX: Delete doesn't return anything
987 my ( $oid, $msg ) = $ObjectCF->Delete;
989 return ( $oid, $msg );
992 # {{{ AddValueForObject
994 =head2 AddValueForObject HASH
996 Adds a custom field value for a record object of some kind.
997 Takes a param hash of
1011 sub AddValueForObject {
1016 LargeContent => undef,
1017 ContentType => undef,
1020 my $obj = $args{'Object'} or return;
1022 unless ( $self->CurrentUserHasRight('ModifyCustomField') ) {
1023 return ( 0, $self->loc('Permission Denied') );
1026 unless ( $self->MatchPattern($args{Content}) ) {
1027 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
1030 $RT::Handle->BeginTransaction;
1032 my $current_values = $self->ValuesForObject($obj);
1034 if ( $self->MaxValues ) {
1035 my $extra_values = ( $current_values->Count + 1 ) - $self->MaxValues;
1037 # (The +1 is for the new value we're adding)
1039 # If we have a set of current values and we've gone over the maximum
1040 # allowed number of values, we'll need to delete some to make room.
1041 # which former values are blown away is not guaranteed
1043 while ($extra_values) {
1044 my $extra_item = $current_values->Next;
1046 unless ( $extra_item->id ) {
1048 "We were just asked to delete a custom fieldvalue that doesn't exist!"
1050 $RT::Handle->Rollback();
1053 $extra_item->Delete;
1058 my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
1059 my $val = $newval->Create(
1060 ObjectType => ref($obj),
1061 ObjectId => $obj->Id,
1062 Content => $args{'Content'},
1063 LargeContent => $args{'LargeContent'},
1064 ContentType => $args{'ContentType'},
1065 CustomField => $self->Id
1069 $RT::Handle->Rollback();
1073 $RT::Handle->Commit();
1082 =head2 MatchPattern STRING
1084 Tests the incoming string against the Pattern of this custom field object
1085 and returns a boolean; returns true if the Pattern is empty.
1091 my $regex = $self->Pattern;
1093 return 1 if !length($regex);
1094 return ($_[0] =~ $regex);
1100 # {{{ FriendlyPattern
1102 =head2 FriendlyPattern
1104 Prettify the pattern of this custom field, by taking the text in C<(?#text)>
1109 sub FriendlyPattern {
1111 my $regex = $self->Pattern;
1113 return '' if !length($regex);
1114 if ($regex =~ /\(\?#([^)]*)\)/) {
1115 return '[' . $self->loc($1) . ']';
1125 # {{{ DeleteValueForObject
1127 =head2 DeleteValueForObject HASH
1129 Deletes a custom field value for a ticket. Takes a param hash of Object and Content
1131 Returns a tuple of (STATUS, MESSAGE). If the call succeeded, the STATUS is true. otherwise it's false
1135 sub DeleteValueForObject {
1137 my %args = ( Object => undef,
1143 unless ($self->CurrentUserHasRight('ModifyCustomField')) {
1144 return (0, $self->loc('Permission Denied'));
1147 my $oldval = RT::ObjectCustomFieldValue->new($self->CurrentUser);
1149 if (my $id = $args{'Id'}) {
1152 unless ($oldval->id) {
1153 $oldval->LoadByObjectContentAndCustomField(
1154 Object => $args{'Object'},
1155 Content => $args{'Content'},
1156 CustomField => $self->Id,
1161 # check to make sure we found it
1162 unless ($oldval->Id) {
1163 return(0, $self->loc("Custom field value [_1] could not be found for custom field [_2]", $args{'Content'}, $self->Name));
1166 # for single-value fields, we need to validate that empty string is a valid value for it
1167 if ( $self->SingleValue and not $self->MatchPattern( '' ) ) {
1168 return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
1173 my $ret = $oldval->Delete();
1175 return(0, $self->loc("Custom field value could not be found"));
1177 return($oldval->Id, $self->loc("Custom field value deleted"));
1181 =head2 ValuesForObject OBJECT
1183 Return an RT::ObjectCustomFieldValues object containing all of this custom field's values for OBJECT
1187 sub ValuesForObject {
1191 my $values = new RT::ObjectCustomFieldValues($self->CurrentUser);
1192 unless ($self->CurrentUserHasRight('SeeCustomField')) {
1193 # Return an empty object if they have no rights to see
1198 $values->LimitToCustomField($self->Id);
1199 $values->LimitToEnabled();
1200 $values->LimitToObject($object);
1206 =head2 _ForObjectType PATH FRIENDLYNAME
1208 Tell RT that a certain object accepts custom fields
1212 'RT::Queue-RT::Ticket' => "Tickets", # loc
1213 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc
1214 'RT::User' => "Users", # loc
1215 'RT::Group' => "Groups", # loc
1217 This is a class method.
1221 sub _ForObjectType {
1224 my $friendly_name = shift;
1226 $FRIENDLY_OBJECT_TYPES{$path} = $friendly_name;
1231 =head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue)
1233 Gets or sets the C<IncludeContentForValue> for this custom field. RT
1234 uses this field to automatically include content into the user's browser
1235 as they display records with custom fields in RT.
1239 sub SetIncludeContentForValue {
1240 shift->IncludeContentForValue(@_);
1242 sub IncludeContentForValue{
1244 $self->_URLTemplate('IncludeContentForValue', @_);
1249 =head2 LinkValueTo [VALUE] (and SetLinkValueTo)
1251 Gets or sets the C<LinkValueTo> for this custom field. RT
1252 uses this field to make custom field values into hyperlinks in the user's
1253 browser as they display records with custom fields in RT.
1258 sub SetLinkValueTo {
1259 shift->LinkValueTo(@_);
1264 $self->_URLTemplate('LinkValueTo', @_);
1269 =head2 _URLTemplate NAME [VALUE]
1271 With one argument, returns the _URLTemplate named C<NAME>, but only if
1272 the current user has the right to see this custom field.
1274 With two arguments, attemptes to set the relevant template value.
1282 my $template_name = shift;
1286 unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
1287 return ( 0, $self->loc('Permission Denied') );
1289 $self->SetAttribute( Name => $template_name, Content => $value );
1290 return ( 1, $self->loc('Updated') );
1292 unless ( $self->id && $self->CurrentUserHasRight('SeeCustomField') ) {
1296 my @attr = $self->Attributes->Named($template_name);
1297 my $attr = shift @attr;
1299 if ($attr) { return $attr->Content }