# BEGIN BPS TAGGED BLOCK {{{
-#
+#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@bestpractical.com>
+#
# (Except where explicitly superseded by other copyright notices)
-#
-#
+#
+#
# LICENSE:
-#
+#
# This work is made available to you under the terms of Version 2 of
# the GNU General Public License. A copy of that license should have
# been provided with this software, but in any event can be snarfed
# from www.gnu.org.
-#
+#
# This work is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
+#
+#
# CONTRIBUTION SUBMISSION POLICY:
-#
+#
# (The following paragraph is not intended to limit the rights granted
# to you to modify and distribute this software under the terms of
# the GNU General Public License and is only of importance to you if
# you choose to contribute your changes and enhancements to the
# community by submitting them to Best Practical Solutions, LLC.)
-#
+#
# By intentionally submitting any modifications, corrections or
# derivatives to this work, or any other work intended for use with
# Request Tracker, to Best Practical Solutions, LLC, you confirm that
# royalty-free, perpetual, license to use, copy, create derivative
# works based on those contributions, and sublicense and distribute
# those contributions and any derivatives thereof.
-#
+#
# END BPS TAGGED BLOCK }}}
package RT::CustomField;
no warnings qw(redefine);
use RT::CustomFieldValues;
+use RT::ObjectCustomFields;
use RT::ObjectCustomFieldValues;
'Enter one value with autocompletion', # loc
'Enter up to [_1] values with autocompletion', # loc
],
+ Date => [
+ 'Select multiple dates', # loc
+ 'Select date', # loc
+ 'Select up to [_1] dates', # loc
+ ],
+ TimeValue => [
+ 'Enter multiple time values (UNSUPPORTED)',
+ 'Enter a time value',
+ 'Enter [_1] time values (UNSUPPORTED)',
+ ],
);
our $RIGHTS = {
SeeCustomField => 'See custom fields', # loc_pair
AdminCustomField => 'Create, delete and modify custom fields', # loc_pair
+ AdminCustomFieldValues => 'Create, delete and modify custom fields values', # loc_pair
ModifyCustomField => 'Add, delete and modify custom field values for objects' #loc_pair
};
$self->SetBasedOn( $args{'BasedOn'} );
}
+ if ( exists $args{'UILocation'} ) {
+ $self->SetUILocation( $args{'UILocation'} );
+ }
+
return ($rv, $msg) unless exists $args{'Queue'};
# Compat code -- create a new ObjectCustomField mapping
# When loading by name, we _can_ load disabled fields, but prefer
# non-disabled fields.
- $CFs->{'find_disabled_rows'}=1;
+ $CFs->FindAllRows;
$CFs->OrderByCols(
{ FIELD => "Disabled", ORDER => 'ASC' },
);
my $self = shift;
my %args = @_;
- unless ($self->CurrentUserHasRight('AdminCustomField')) {
+ unless ($self->CurrentUserHasRight('AdminCustomField') || $self->CurrentUserHasRight('AdminCustomFieldValues')) {
return (0, $self->loc('Permission Denied'));
}
sub DeleteValue {
my $self = shift;
my $id = shift;
- unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
+ unless ( $self->CurrentUserHasRight('AdminCustomField') || $self->CurrentUserHasRight('AdminCustomFieldValues') ) {
return (0, $self->loc('Permission Denied'));
}
);
}
-=head2 SetLookupType
-
-Autrijus: care to doc how LookupTypes work?
-
-=cut
-
-sub SetLookupType {
- my $self = shift;
- my $lookup = shift;
- if ( $lookup ne $self->LookupType ) {
- # Okay... We need to invalidate our existing relationships
- my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
- $ObjectCustomFields->LimitToCustomField($self->Id);
- $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
- }
- return $self->SUPER::SetLookupType($lookup);
-}
-
=head2 TypeComposite
Returns a composite value composed of this object's type and maximum values
sub TypeComposites {
my $self = shift;
- return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
+ return grep !/(?:[Tt]ext|Combobox|Date|TimeValue)-0/, map { ("$_-1", "$_-0") } $self->Types;
+}
+
+=head2 SetLookupType
+
+Autrijus: care to doc how LookupTypes work?
+
+=cut
+
+sub SetLookupType {
+ my $self = shift;
+ my $lookup = shift;
+ if ( $lookup ne $self->LookupType ) {
+ # Okay... We need to invalidate our existing relationships
+ my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
+ $ObjectCustomFields->LimitToCustomField($self->Id);
+ $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
+ }
+ return $self->SUPER::SetLookupType($lookup);
}
=head2 LookupTypes
"[_1]'s [_2]'s [_3] objects", # loc
);
-=head2 FriendlyTypeLookup
+=head2 FriendlyLookupType
Returns a localized description of the type of this custom field
return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
}
+sub RecordClassFromLookupType {
+ my $self = shift;
+ my ($class) = ($self->LookupType =~ /^([^-]+)/);
+ unless ( $class ) {
+ $RT::Logger->error(
+ "Custom Field #". $self->id
+ ." has incorrect LookupType '". $self->LookupType ."'"
+ );
+ return undef;
+ }
+ return $class;
+}
+
+sub CollectionClassFromLookupType {
+ my $self = shift;
+
+ my $record_class = $self->RecordClassFromLookupType;
+ return undef unless $record_class;
+
+ my $collection_class;
+ if ( UNIVERSAL::can($record_class.'Collection', 'new') ) {
+ $collection_class = $record_class.'Collection';
+ } elsif ( UNIVERSAL::can($record_class.'es', 'new') ) {
+ $collection_class = $record_class.'es';
+ } elsif ( UNIVERSAL::can($record_class.'s', 'new') ) {
+ $collection_class = $record_class.'s';
+ } else {
+ $RT::Logger->error("Can not find a collection class for record class '$record_class'");
+ return undef;
+ }
+ return $collection_class;
+}
+
+=head1 AppliedTo
+
+Returns collection with objects this custom field is applied to.
+Class of the collection depends on L</LookupType>.
+See all L</NotAppliedTo> .
+
+Doesn't takes into account if object is applied globally.
+
+=cut
+
+sub AppliedTo {
+ my $self = shift;
+
+ my ($res, $ocfs_alias) = $self->_AppliedTo;
+ return $res unless $res;
+
+ $res->Limit(
+ ALIAS => $ocfs_alias,
+ FIELD => 'id',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+
+ return $res;
+}
+
+=head1 NotAppliedTo
+
+Returns collection with objects this custom field is not applied to.
+Class of the collection depends on L</LookupType>.
+See all L</AppliedTo> .
+
+Doesn't takes into account if object is applied globally.
+
+=cut
+
+sub NotAppliedTo {
+ my $self = shift;
+
+ my ($res, $ocfs_alias) = $self->_AppliedTo;
+ return $res unless $res;
+
+ $res->Limit(
+ ALIAS => $ocfs_alias,
+ FIELD => 'id',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+
+ return $res;
+}
+
+sub _AppliedTo {
+ my $self = shift;
+
+ my ($class) = $self->CollectionClassFromLookupType;
+ return undef unless $class;
+
+ my $res = $class->new( $self->CurrentUser );
+
+ # If CF is a Group CF, only display user-defined groups
+ if ( $class eq 'RT::Groups' ) {
+ $res->LimitToUserDefinedGroups;
+ }
+
+ $res->OrderBy( FIELD => 'Name' );
+ my $ocfs_alias = $res->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFields',
+ FIELD2 => 'ObjectId',
+ );
+ $res->Limit(
+ LEFTJOIN => $ocfs_alias,
+ ALIAS => $ocfs_alias,
+ FIELD => 'CustomField',
+ VALUE => $self->id,
+ );
+ return ($res, $ocfs_alias);
+}
+
+=head2 IsApplied
+
+Takes object id and returns corresponding L<RT::ObjectCustomField>
+record if this custom field is applied to the object. Use 0 to check
+if custom field is applied globally.
+
+=cut
+
+sub IsApplied {
+ my $self = shift;
+ my $id = shift;
+ my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
+ $ocf->LoadByCols( CustomField => $self->id, ObjectId => $id || 0 );
+ return undef unless $ocf->id;
+ return $ocf;
+}
=head2 AddToObject OBJECT
return ( 0, $self->loc('Permission Denied') );
}
- my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
- $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
- if ( $ObjectCF->Id ) {
- return ( 0, $self->loc("That is already the current value") );
+ if ( $self->IsApplied( $id ) ) {
+ return ( 0, $self->loc("Custom field is already applied to the object") );
}
- my ( $oid, $msg ) =
- $ObjectCF->Create( ObjectId => $id, CustomField => $self->Id );
+ if ( $id ) {
+ # applying locally
+ return (0, $self->loc("Couldn't apply custom field to an object as it's global already") )
+ if $self->IsApplied( 0 );
+ }
+ else {
+ my $applied = RT::ObjectCustomFields->new( $self->CurrentUser );
+ $applied->LimitToCustomField( $self->id );
+ while ( my $record = $applied->Next ) {
+ $record->Delete;
+ }
+ }
+
+ my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
+ my ( $oid, $msg ) = $ocf->Create(
+ ObjectId => $id, CustomField => $self->id,
+ );
return ( $oid, $msg );
}
=cut
-
sub RemoveFromObject {
my $self = shift;
my $object = shift;
return ( 0, $self->loc('Permission Denied') );
}
- my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
- $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
- unless ( $ObjectCF->Id ) {
+ my $ocf = $self->IsApplied( $id );
+ unless ( $ocf ) {
return ( 0, $self->loc("This custom field does not apply to that object") );
}
- # XXX: Delete doesn't return anything
- my ( $oid, $msg ) = $ObjectCF->Delete;
+ # XXX: Delete doesn't return anything
+ my ( $oid, $msg ) = $ocf->Delete;
return ( $oid, $msg );
}
$extra_values--;
}
}
+ # For date, we need to store Content as ISO date
+ if ($self->Type eq 'Date') {
+ my $DateObj = new RT::Date( $self->CurrentUser );
+ $DateObj->Set(
+ Format => 'unknown',
+ Value => $args{'Content'},
+ );
+ $args{'Content'} = $DateObj->ISO;
+ }
my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
my $val = $newval->Create(
ObjectType => ref($obj),
return $obj;
}
+sub UILocation {
+ my $self = shift;
+ my $tag = $self->FirstAttribute( 'UILocation' );
+ return $tag ? $tag->Content : '';
+}
+
+sub SetUILocation {
+ my $self = shift;
+ my $tag = shift;
+ if ( $tag ) {
+ return $self->SetAttribute( Name => 'UILocation', Content => $tag );
+ }
+ else {
+ return $self->DeleteAttribute('UILocation');
+ }
+}
+
1;