X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FTickets.pm;h=2220a077f7b31e3d3228962e7d75a863e0320235;hb=b8988e1d3ac75af63c85e8563e57701030315a9e;hp=06b17e263187645457c4b5bdcd485df77059f694;hpb=7f029e082712dceafb9152820746da79a50f2275;p=freeside.git diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm index 06b17e263..2220a077f 100755 --- a/rt/lib/RT/Tickets.pm +++ b/rt/lib/RT/Tickets.pm @@ -2,7 +2,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -142,9 +142,9 @@ our %FIELD_METADATA = ( QueueCc => [ 'WATCHERFIELD' => 'Cc' => 'Queue', ], #loc_left_pair QueueAdminCc => [ 'WATCHERFIELD' => 'AdminCc' => 'Queue', ], #loc_left_pair QueueWatcher => [ 'WATCHERFIELD' => undef => 'Queue', ], #loc_left_pair - CustomFieldValue => [ 'CUSTOMFIELD', ], #loc_left_pair - CustomField => [ 'CUSTOMFIELD', ], #loc_left_pair - CF => [ 'CUSTOMFIELD', ], #loc_left_pair + CustomFieldValue => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair + CustomField => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair + CF => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair Updated => [ 'TRANSDATE', ], #loc_left_pair RequestorGroup => [ 'MEMBERSHIPFIELD' => 'Requestor', ], #loc_left_pair CCGroup => [ 'MEMBERSHIPFIELD' => 'Cc', ], #loc_left_pair @@ -443,10 +443,6 @@ sub _LinkLimit { my $is_null = 0; $is_null = 1 if !$value || $value =~ /^null$/io; - unless ($is_null) { - $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value ); - } - my $direction = $meta->[1] || ''; my ($matchfield, $linkfield) = ('', ''); if ( $direction eq 'To' ) { @@ -473,6 +469,7 @@ sub _LinkLimit { $op = ($op =~ /^(=|IS)$/i)? 'IS': 'IS NOT'; } elsif ( $value =~ /\D/ ) { + $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value ); $is_local = 0; } $matchfield = "Local$matchfield" if $is_local; @@ -977,13 +974,18 @@ sub _WatcherLimit { } $rest{SUBKEY} ||= 'EmailAddress'; - my $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type ); + my ($groups, $group_members, $users); + if ( $rest{'BUNDLE'} ) { + ($groups, $group_members, $users) = @{ $rest{'BUNDLE'} }; + } else { + $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type ); + } $self->_OpenParen; if ( $op =~ /^IS(?: NOT)?$/i ) { # is [not] empty case - my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups ); + $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups ); # to avoid joining the table Users into the query, we just join GM # and make sure we don't match records where group is member of itself $self->SUPER::Limit( @@ -1021,7 +1023,7 @@ sub _WatcherLimit { $users_obj->RowsPerPage(2); my @users = @{ $users_obj->ItemsArrayRef }; - my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups ); + $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups ); if ( @users <= 1 ) { my $uid = 0; $uid = $users[0]->id if @users; @@ -1046,7 +1048,7 @@ sub _WatcherLimit { VALUE => "$group_members.MemberId", QUOTEVALUE => 0, ); - my $users = $self->Join( + $users ||= $self->Join( TYPE => 'LEFT', ALIAS1 => $group_members, FIELD1 => 'MemberId', @@ -1072,10 +1074,10 @@ sub _WatcherLimit { } else { # positive condition case - my $group_members = $self->_GroupMembersJoin( + $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups, New => 1, Left => 0 ); - my $users = $self->Join( + $users ||= $self->Join( TYPE => 'LEFT', ALIAS1 => $group_members, FIELD1 => 'MemberId', @@ -1092,6 +1094,7 @@ sub _WatcherLimit { ); } $self->_CloseParen; + return ($groups, $group_members, $users); } sub _RoleGroupsJoin { @@ -1342,33 +1345,44 @@ sub _WatcherMembershipLimit { Try and turn a CF descriptor into (cfid, cfname) object pair. +Takes an optional second parameter of the CF LookupType, defaults to Ticket CFs. + =cut sub _CustomFieldDecipher { - my ($self, $string) = @_; + my ($self, $string, $lookuptype) = @_; + $lookuptype ||= $self->_SingularClass->CustomFieldLookupType; - my ($queue, $field, $column) = ($string =~ /^(?:(.+?)\.)?{(.+)}(?:\.(Content|LargeContent))?$/); + my ($object, $field, $column) = ($string =~ /^(?:(.+?)\.)?\{(.+)\}(?:\.(Content|LargeContent))?$/); $field ||= ($string =~ /^{(.*?)}$/)[0] || $string; - my $cf; - if ( $queue ) { - my $q = RT::Queue->new( $self->CurrentUser ); - $q->Load( $queue ); + my ($cf, $applied_to); + + if ( $object ) { + my $record_class = RT::CustomField->RecordClassFromLookupType($lookuptype); + $applied_to = $record_class->new( $self->CurrentUser ); + $applied_to->Load( $object ); - if ( $q->id ) { - # $queue = $q->Name; # should we normalize the queue? - $cf = $q->CustomField( $field ); + if ( $applied_to->id ) { + RT->Logger->debug("Limiting to CFs identified by '$field' applied to $record_class #@{[$applied_to->id]} (loaded via '$object')"); } else { - $RT::Logger->warning("Queue '$queue' doesn't exist, parsed from '$string'"); - $queue = 0; + RT->Logger->warning("$record_class '$object' doesn't exist, parsed from '$string'"); + $object = 0; + undef $applied_to; } } - elsif ( $field =~ /\D/ ) { - $queue = ''; + + if ( $field =~ /\D/ ) { + $object ||= ''; my $cfs = RT::CustomFields->new( $self->CurrentUser ); - $cfs->Limit( FIELD => 'Name', VALUE => $field ); - $cfs->LimitToLookupType('RT::Queue-RT::Ticket'); + $cfs->Limit( FIELD => 'Name', VALUE => $field, ($applied_to ? (CASESENSITIVE => 0) : ()) ); + $cfs->LimitToLookupType($lookuptype); + + if ($applied_to) { + $cfs->SetContextObject($applied_to); + $cfs->LimitToObjectId($applied_to->id); + } # if there is more then one field the current user can # see with the same name then we shouldn't return cf object @@ -1381,9 +1395,11 @@ sub _CustomFieldDecipher { else { $cf = RT::CustomField->new( $self->CurrentUser ); $cf->Load( $field ); + $cf->SetContextObject($applied_to) + if $cf->id and $applied_to; } - return ($queue, $field, $cf, $column); + return ($object, $field, $cf, $column); } =head2 _CustomFieldJoin @@ -1392,8 +1408,14 @@ Factor out the Join of custom fields so we can use it for sorting too =cut +our %JOIN_ALIAS_FOR_LOOKUP_TYPE = ( + RT::Ticket->CustomFieldLookupType => sub { "main" }, +); + sub _CustomFieldJoin { - my ($self, $cfkey, $cfid, $field) = @_; + my ($self, $cfkey, $cfid, $field, $type) = @_; + $type ||= RT::Ticket->CustomFieldLookupType; + # Perform one Join per CustomField if ( $self->{_sql_object_cfv_alias}{$cfkey} || $self->{_sql_cf_alias}{$cfkey} ) @@ -1402,17 +1424,21 @@ sub _CustomFieldJoin { $self->{_sql_cf_alias}{$cfkey} ); } - my ($TicketCFs, $CFs); + my $ObjectAlias = $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type} + ? $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}->($self) + : die "We don't know how to join on $type"; + + my ($ObjectCFs, $CFs); if ( $cfid ) { - $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( + $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( TYPE => 'LEFT', - ALIAS1 => 'main', + ALIAS1 => $ObjectAlias, FIELD1 => 'id', TABLE2 => 'ObjectCustomFieldValues', FIELD2 => 'ObjectId', ); $self->SUPER::Limit( - LEFTJOIN => $TicketCFs, + LEFTJOIN => $ObjectCFs, FIELD => 'CustomField', VALUE => $cfid, ENTRYAGGREGATOR => 'AND' @@ -1444,7 +1470,7 @@ sub _CustomFieldJoin { LEFTJOIN => $CFs, ENTRYAGGREGATOR => 'AND', FIELD => 'LookupType', - VALUE => 'RT::Queue-RT::Ticket', + VALUE => $type, ); $self->SUPER::Limit( LEFTJOIN => $CFs, @@ -1453,7 +1479,7 @@ sub _CustomFieldJoin { VALUE => $field, ); - $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( + $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( TYPE => 'LEFT', ALIAS1 => $CFs, FIELD1 => 'id', @@ -1461,28 +1487,29 @@ sub _CustomFieldJoin { FIELD2 => 'CustomField', ); $self->SUPER::Limit( - LEFTJOIN => $TicketCFs, + LEFTJOIN => $ObjectCFs, FIELD => 'ObjectId', - VALUE => 'main.id', + VALUE => "$ObjectAlias.id", QUOTEVALUE => 0, ENTRYAGGREGATOR => 'AND', ); } + $self->SUPER::Limit( - LEFTJOIN => $TicketCFs, + LEFTJOIN => $ObjectCFs, FIELD => 'ObjectType', - VALUE => 'RT::Ticket', + VALUE => RT::CustomField->ObjectTypeFromLookupType($type), ENTRYAGGREGATOR => 'AND' ); $self->SUPER::Limit( - LEFTJOIN => $TicketCFs, + LEFTJOIN => $ObjectCFs, FIELD => 'Disabled', OPERATOR => '=', VALUE => '0', ENTRYAGGREGATOR => 'AND' ); - return ($TicketCFs, $CFs); + return ($ObjectCFs, $CFs); } =head2 _CustomFieldLimit @@ -1501,12 +1528,16 @@ use Regexp::Common::net::CIDR; sub _CustomFieldLimit { my ( $self, $_field, $op, $value, %rest ) = @_; + my $meta = $FIELD_METADATA{ $_field }; + my $class = $meta->[1] || 'Ticket'; + my $type = "RT::$class"->CustomFieldLookupType; + my $field = $rest{'SUBKEY'} || die "No field specified"; # For our sanity, we can only limit on one queue at a time - my ($queue, $cfid, $cf, $column); - ($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $field ); + my ($object, $cfid, $cf, $column); + ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field, $type ); $cfid = $cf ? $cf->id : 0 ; # If we're trying to find custom fields that don't match something, we @@ -1602,16 +1633,16 @@ sub _CustomFieldLimit { my $single_value = !$cf || !$cfid || $cf->SingleValue; - my $cfkey = $cfid ? $cfid : "$queue.$field"; + my $cfkey = $cfid ? $cfid : "$type-$object.$field"; if ( $null_op && !$column ) { # IS[ NOT] NULL without column is the same as has[ no] any CF value, # we can reuse our default joins for this operation # with column specified we have different situation - my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field ); + my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); $self->_OpenParen; $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'id', OPERATOR => $op, VALUE => $value, @@ -1634,11 +1665,11 @@ sub _CustomFieldLimit { $self->_OpenParen; if ( $op !~ /NOT|!=|<>/i ) { # positive equation $self->_CustomFieldLimit( - 'CF', '<=', $end_ip, %rest, + $_field, '<=', $end_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.Content', ); $self->_CustomFieldLimit( - 'CF', '>=', $start_ip, %rest, + $_field, '>=', $start_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.LargeContent', ENTRYAGGREGATOR => 'AND', ); @@ -1646,20 +1677,20 @@ sub _CustomFieldLimit { # estimations and scan less rows # have to disable this tweak because of ipv6 # $self->_CustomFieldLimit( -# $field, '>=', '000.000.000.000', %rest, +# $_field, '>=', '000.000.000.000', %rest, # SUBKEY => $rest{'SUBKEY'}. '.Content', # ENTRYAGGREGATOR => 'AND', # ); # $self->_CustomFieldLimit( -# $field, '<=', '255.255.255.255', %rest, +# $_field, '<=', '255.255.255.255', %rest, # SUBKEY => $rest{'SUBKEY'}. '.LargeContent', # ENTRYAGGREGATOR => 'AND', # ); } else { # negative equation - $self->_CustomFieldLimit($field, '>', $end_ip, %rest); + $self->_CustomFieldLimit($_field, '>', $end_ip, %rest); $self->_CustomFieldLimit( - $field, '<', $start_ip, %rest, + $_field, '<', $start_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.LargeContent', ENTRYAGGREGATOR => 'OR', ); @@ -1671,7 +1702,7 @@ sub _CustomFieldLimit { } elsif ( !$negative_op || $single_value ) { $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op; - my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field ); + my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); $self->_OpenParen; @@ -1682,7 +1713,7 @@ sub _CustomFieldLimit { # otherwise search in Content and in LargeContent if ( $column ) { $self->_SQLLimit( $fix_op->( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => $column, OPERATOR => $op, VALUE => $value, @@ -1708,7 +1739,7 @@ sub _CustomFieldLimit { $self->_OpenParen; $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => ">=", VALUE => $daystart, @@ -1716,7 +1747,7 @@ sub _CustomFieldLimit { ); $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => "<", VALUE => $dayend, @@ -1729,7 +1760,7 @@ sub _CustomFieldLimit { elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) { if ( length( Encode::encode_utf8($value) ) < 256 ) { $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, @@ -1740,14 +1771,14 @@ sub _CustomFieldLimit { else { $self->_OpenParen; $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => '=', VALUE => '', ENTRYAGGREGATOR => 'OR' ); $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => 'IS', VALUE => 'NULL', @@ -1755,7 +1786,7 @@ sub _CustomFieldLimit { ); $self->_CloseParen; $self->_SQLLimit( $fix_op->( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'LargeContent', OPERATOR => $op, VALUE => $value, @@ -1766,7 +1797,7 @@ sub _CustomFieldLimit { } else { $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, @@ -1777,14 +1808,14 @@ sub _CustomFieldLimit { $self->_OpenParen; $self->_OpenParen; $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => '=', VALUE => '', ENTRYAGGREGATOR => 'OR' ); $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => 'IS', VALUE => 'NULL', @@ -1792,7 +1823,7 @@ sub _CustomFieldLimit { ); $self->_CloseParen; $self->_SQLLimit( $fix_op->( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'LargeContent', OPERATOR => $op, VALUE => $value, @@ -1826,7 +1857,7 @@ sub _CustomFieldLimit { if ($negative_op) { $self->_SQLLimit( - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => $column || 'Content', OPERATOR => 'IS', VALUE => 'NULL', @@ -1840,7 +1871,7 @@ sub _CustomFieldLimit { } else { $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++; - my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field ); + my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); # reverse operation $op =~ s/!|NOT\s+//i; @@ -1849,8 +1880,8 @@ sub _CustomFieldLimit { # otherwise search in Content and in LargeContent if ( $column ) { $self->SUPER::Limit( $fix_op->( - LEFTJOIN => $TicketCFs, - ALIAS => $TicketCFs, + LEFTJOIN => $ObjectCFs, + ALIAS => $ObjectCFs, FIELD => $column, OPERATOR => $op, VALUE => $value, @@ -1859,8 +1890,8 @@ sub _CustomFieldLimit { } else { $self->SUPER::Limit( - LEFTJOIN => $TicketCFs, - ALIAS => $TicketCFs, + LEFTJOIN => $ObjectCFs, + ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, @@ -1869,7 +1900,7 @@ sub _CustomFieldLimit { } $self->_SQLLimit( %rest, - ALIAS => $TicketCFs, + ALIAS => $ObjectCFs, FIELD => 'id', OPERATOR => 'IS', VALUE => 'NULL', @@ -1979,10 +2010,10 @@ sub OrderByCols { } push @res, { %$row, ALIAS => $users, FIELD => $subkey }; } elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) { - my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey ); - my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field"; + my ($object, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey ); + my $cfkey = $cf_obj ? $cf_obj->id : "$object.$field"; $cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1; - my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field ); + my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field ); # this is described in _CustomFieldLimit $self->_SQLLimit( ALIAS => $CFs, @@ -2004,7 +2035,7 @@ sub OrderByCols { } my $CFvs = $self->Join( TYPE => 'LEFT', - ALIAS1 => $TicketCFs, + ALIAS1 => $ObjectCFs, FIELD1 => 'CustomField', TABLE2 => 'CustomFieldValues', FIELD2 => 'CustomField', @@ -2013,12 +2044,12 @@ sub OrderByCols { LEFTJOIN => $CFvs, FIELD => 'Name', QUOTEVALUE => 0, - VALUE => $TicketCFs . ".Content", + VALUE => $ObjectCFs . ".Content", ENTRYAGGREGATOR => 'AND' ); push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' }; - push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' }; + push @res, { %$row, ALIAS => $ObjectCFs, FIELD => 'Content' }; } elsif ( $field eq "Custom" && $subkey eq "Ownership") { # PAW logic is "reversed" my $order = "ASC"; @@ -3144,7 +3175,7 @@ sub LimitCustomField { $self->Limit( VALUE => $args{VALUE}, FIELD => "CF" - .(defined $args{'QUEUE'}? ".{$args{'QUEUE'}}" : '' ) + .(defined $args{'QUEUE'}? ".$args{'QUEUE'}" : '' ) .".{" . $CF->Name . "}", OPERATOR => $args{OPERATOR}, CUSTOMFIELD => 1,