-# BEGIN LICENSE BLOCK
+# {{{ BEGIN BPS TAGGED BLOCK
#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
#
-# (Except where explictly superceded by other copyright notices)
+# (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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# Unless otherwise specified, all modifications, corrections or
-# extensions to this work which alter its source code become the
-# property of Best Practical Solutions, LLC when submitted for
-# inclusion in the work.
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# 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.)
#
-# END LICENSE BLOCK
+# 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
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# 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
# Major Changes:
# - Decimated ProcessRestrictions and broke it into multiple
use strict;
no warnings qw(redefine);
use vars qw(@SORTFIELDS);
+use RT::CustomFields;
# Configuration Tables:
RefersTo => ['LINK' => To => 'RefersTo',],
HasMember => ['LINK' => From => 'MemberOf',],
DependentOn => ['LINK' => From => 'DependsOn',],
- ReferredTo => ['LINK' => From => 'RefersTo',],
+ DependedOnBy => ['LINK' => From => 'DependsOn',],
+ ReferredToBy => ['LINK' => From => 'RefersTo',],
# HasDepender => ['LINK',],
# RelatedTo => ['LINK',],
Told => ['DATE' => 'Told',],
LastUpdated => ['DATE' => 'LastUpdated',],
Created => ['DATE' => 'Created',],
Subject => ['STRING',],
- Type => ['STRING',],
Content => ['TRANSFIELD',],
ContentType => ['TRANSFIELD',],
Filename => ['TRANSFIELD',],
TransactionDate => ['TRANSDATE',],
Requestor => ['WATCHERFIELD' => 'Requestor',],
- CC => ['WATCHERFIELD' => 'Cc',],
- AdminCC => ['WATCHERFIELD' => 'AdminCC',],
+ Requestors => ['WATCHERFIELD' => 'Requestor',],
+ Cc => ['WATCHERFIELD' => 'Cc',],
+ AdminCc => ['WATCHERFIELD' => 'AdminCC',],
Watcher => ['WATCHERFIELD'],
LinkedTo => ['LINKFIELD',],
CustomFieldValue =>['CUSTOMFIELD',],
LINKFIELD => \&_LinkFieldLimit,
CUSTOMFIELD => \&_CustomFieldLimit,
);
+my %can_bundle =
+ ( WATCHERFIELD => "yeps",
+ );
# Default EntryAggregator per type
+# if you specify OP, you must specify all valid OPs
my %DefaultEA = (
INT => 'AND',
ENUM => { '=' => 'OR',
'!='=> 'AND'
},
- DATE => 'AND',
+ DATE => { '=' => 'OR',
+ '>='=> 'AND',
+ '<='=> 'AND',
+ '>' => 'AND',
+ '<' => 'AND'
+ },
STRING => { '=' => 'OR',
'!='=> 'AND',
'LIKE'=> 'AND',
},
TRANSFIELD => 'AND',
TRANSDATE => 'AND',
+ LINK => 'OR',
LINKFIELD => 'AND',
TARGET => 'AND',
BASE => 'AND',
# into Tickets_Overlay_SQL.
sub FIELDS { return \%FIELDS }
sub dispatch { return \%dispatch }
+sub can_bundle { return \%can_bundle }
# Bring in the clowns.
require RT::Tickets_Overlay_SQL;
Meta Data:
1: Direction (From,To)
- 2: Relationship Type (MemberOf, DependsOn,RefersTo)
+ 2: Link Type (MemberOf, DependsOn,RefersTo)
=cut
die "Incorrect Meta Data for $field"
unless (defined $meta->[1] and defined $meta->[2]);
- my $LinkAlias = $sb->NewAlias ('Links');
+ $sb->{_sql_linkalias} = $sb->NewAlias ('Links')
+ unless defined $sb->{_sql_linkalias};
$sb->_OpenParen();
$sb->_SQLLimit(
- ALIAS => $LinkAlias,
+ ALIAS => $sb->{_sql_linkalias},
FIELD => 'Type',
OPERATOR => '=',
VALUE => $meta->[2],
my $matchfield = ( $value =~ /^(\d+)$/ ? "LocalTarget" : "Target" );
$sb->_SQLLimit(
- ALIAS => $LinkAlias,
+ ALIAS => $sb->{_sql_linkalias},
ENTRYAGGREGATOR => 'AND',
FIELD => $matchfield,
OPERATOR => '=',
);
#If we're searching on target, join the base to ticket.id
- $sb->Join( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'},
- ALIAS2 => $LinkAlias, FIELD2 => 'LocalBase');
+ $sb->_SQLJoin( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'},
+ ALIAS2 => $sb->{_sql_linkalias}, FIELD2 => 'LocalBase');
} elsif ( $meta->[1] eq "From" ) {
my $matchfield = ( $value =~ /^(\d+)$/ ? "LocalBase" : "Base" );
$sb->_SQLLimit(
- ALIAS => $LinkAlias,
+ ALIAS => $sb->{_sql_linkalias},
ENTRYAGGREGATOR => 'AND',
FIELD => $matchfield,
OPERATOR => '=',
);
#If we're searching on base, join the target to ticket.id
- $sb->Join( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'},
- ALIAS2 => $LinkAlias, FIELD2 => 'LocalTarget');
+ $sb->_SQLJoin( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'},
+ ALIAS2 => $sb->{_sql_linkalias}, FIELD2 => 'LocalTarget');
} else {
die "Invalid link direction '$meta->[1]' for $field\n";
Handle date fields. (Created, LastTold..)
Meta Data:
- 1: type of relationship. (Probably not necessary.)
+ 1: type of link. (Probably not necessary.)
=cut
my ($sb,$field,$op,$value,@rest) = @_;
die "Invalid Date Op: $op"
- unless $op =~ /^(=|!=|>|<|>=|<=)$/;
+ unless $op =~ /^(=|>|<|>=|<=)$/;
my $meta = $FIELDS{$field};
die "Incorrect Meta Data for $field"
require Time::ParseDate;
use POSIX 'strftime';
+ # FIXME: Replace me with RT::Date( Type => 'unknown' ...)
my $time = Time::ParseDate::parsedate( $value,
UK => $RT::DateDayBeforeMonth,
PREFER_PAST => $RT::AmbiguousDayInPast,
- PREFER_FUTURE => !($RT::AmbiguousDayInPast));
- $value = strftime("%Y-%m-%d %H:%M",localtime($time));
+ PREFER_FUTURE => !($RT::AmbiguousDayInPast),
+ FUZZY => 1
+ );
- $sb->_SQLLimit(
- FIELD => $meta->[1],
- OPERATOR => $op,
- VALUE => $value,
- @rest,
- );
+ if ($op eq "=") {
+ # if we're specifying =, that means we want everything on a
+ # particular single day. in the database, we need to check for >
+ # and < the edges of that day.
+
+ my $daystart = strftime("%Y-%m-%d %H:%M",
+ gmtime($time - ( $time % 86400 )));
+ my $dayend = strftime("%Y-%m-%d %H:%M",
+ gmtime($time + ( 86399 - $time % 86400 )));
+
+ $sb-> _OpenParen;
+
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => ">=",
+ VALUE => $daystart,
+ @rest,
+ );
+
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => "<=",
+ VALUE => $dayend,
+ @rest,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ $sb-> _CloseParen;
+
+ } else {
+ $value = strftime("%Y-%m-%d %H:%M", gmtime($time));
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => $op,
+ VALUE => $value,
+ @rest,
+ );
+ }
}
=head2 _StringLimit
$sb->_OpenParen;
# Join Transactions To Attachments
- $sb->Join( ALIAS1 => $sb->{_sql_trattachalias}, FIELD1 => 'TransactionId',
+ $sb->_SQLJoin( ALIAS1 => $sb->{_sql_trattachalias}, FIELD1 => 'TransactionId',
ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'id');
# Join Transactions to Tickets
- $sb->Join( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'}, # UGH!
+ $sb->_SQLJoin( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'}, # UGH!
ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'Ticket');
my $d = new RT::Date( $sb->CurrentUser );
- $d->Set($value);
- $value = $d->ISO;
+ $d->Set( Format => 'ISO', Value => $value);
+ $value = $d->ISO;
#Search for the right field
$sb->_SQLLimit(ALIAS => $sb->{_sql_trattachalias},
$sb->_OpenParen;
- # Join Transactions To Attachments
- $sb->Join( ALIAS1 => $sb->{_sql_trattachalias}, FIELD1 => 'TransactionId',
- ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'id');
-
- # Join Transactions to Tickets
- $sb->Join( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'}, # UGH!
- ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'Ticket');
-
#Search for the right field
$sb->_SQLLimit(ALIAS => $sb->{_sql_trattachalias},
FIELD => $field,
@rest
);
+ # Join Transactions To Attachments
+ $sb->_SQLJoin( ALIAS1 => $sb->{_sql_trattachalias}, FIELD1 => 'TransactionId',
+ ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'id');
+
+ # Join Transactions to Tickets
+ $sb->_SQLJoin( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'}, # UGH!
+ ALIAS2 => $sb->{_sql_transalias}, FIELD2 => 'Ticket');
+
$sb->_CloseParen;
}
Meta Data:
1: Field to query on
-=cut
-sub _WatcherLimit {
- my ($self,$field,$op,$value,@rest) = @_;
- my %rest = @rest;
+=begin testing
- $self->_OpenParen;
+# Test to make sure that you can search for tickets by requestor address and
+# by requestor name.
- my $groups = $self->NewAlias('Groups');
- my $group_princs = $self->NewAlias('Principals');
- my $groupmembers = $self->NewAlias('CachedGroupMembers');
- my $member_princs = $self->NewAlias('Principals');
- my $users = $self->NewAlias('Users');
+my ($id,$msg);
+my $u1 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u1->Create( Name => 'RequestorTestOne', EmailAddress => 'rqtest1@example.com');
+ok ($id,$msg);
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create( Name => 'RequestorTestTwo', EmailAddress => 'rqtest2@example.com');
+ok ($id,$msg);
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($trans);
+($id,$trans,$msg) =$t1->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u1->EmailAddress]);
+ok ($id, $msg);
- #Find user watchers
-# my $subclause = undef;
-# my $aggregator = 'OR';
-# if ($restriction->{'OPERATOR'} =~ /!|NOT/i ){
-# $subclause = 'AndEmailIsNot';
-# $aggregator = 'AND';
-# }
+my $t2 = RT::Ticket->new($RT::SystemUser);
+($id,$trans,$msg) =$t2->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress]);
+ok ($id, $msg);
- $self->_SQLLimit(ALIAS => $users,
- FIELD => $rest{SUBKEY} || 'EmailAddress',
- VALUE => $value,
- OPERATOR => $op,
- CASESENSITIVE => 0,
- @rest,
- );
+my $t3 = RT::Ticket->new($RT::SystemUser);
+($id,$trans,$msg) =$t3->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress, $u1->EmailAddress]);
+ok ($id, $msg);
- # {{{ Tie to groups for tickets we care about
- $self->_SQLLimit(ALIAS => $groups,
- FIELD => 'Domain',
- VALUE => 'RT::Ticket-Role',
- ENTRYAGGREGATOR => 'AND');
- $self->Join(ALIAS1 => $groups, FIELD1 => 'Instance',
- ALIAS2 => 'main', FIELD2 => 'id');
- # }}}
+my $tix1 = RT::Tickets->new($RT::SystemUser);
+$tix1->FromSQL('Requestor.EmailAddress LIKE "rqtest1" OR Requestor.EmailAddress LIKE "rqtest2"');
- # If we care about which sort of watcher
- my $meta = $FIELDS{$field};
- my $type = ( defined $meta->[1] ? $meta->[1] : undef );
+is ($tix1->Count, 3);
- if ( $type ) {
- $self->_SQLLimit(ALIAS => $groups,
- FIELD => 'Type',
- VALUE => $type,
- ENTRYAGGREGATOR => 'AND');
- }
+my $tix2 = RT::Tickets->new($RT::SystemUser);
+$tix2->FromSQL('Requestor.Name LIKE "TestOne" OR Requestor.Name LIKE "TestTwo"');
+
+is ($tix2->Count, 3);
+
+
+my $tix3 = RT::Tickets->new($RT::SystemUser);
+$tix3->FromSQL('Requestor.EmailAddress LIKE "rqtest1"');
+
+is ($tix3->Count, 2);
+
+my $tix4 = RT::Tickets->new($RT::SystemUser);
+$tix4->FromSQL('Requestor.Name LIKE "TestOne" ');
- $self->Join (ALIAS1 => $groups, FIELD1 => 'id',
- ALIAS2 => $group_princs, FIELD2 => 'ObjectId');
- $self->_SQLLimit(ALIAS => $group_princs,
- FIELD => 'PrincipalType',
- VALUE => 'Group',
- ENTRYAGGREGATOR => 'AND');
- $self->Join( ALIAS1 => $group_princs, FIELD1 => 'id',
- ALIAS2 => $groupmembers, FIELD2 => 'GroupId');
+is ($tix4->Count, 2);
- $self->Join( ALIAS1 => $groupmembers, FIELD1 => 'MemberId',
- ALIAS2 => $member_princs, FIELD2 => 'id');
- $self->Join (ALIAS1 => $member_princs, FIELD1 => 'ObjectId',
- ALIAS2 => $users, FIELD2 => 'id');
+# Searching for tickets that have two requestors isn't supported
+# There's no way to differentiate "one requestor name that matches foo and bar"
+# and "two requestors, one matching foo and one matching bar"
- $self->_CloseParen;
+# my $tix5 = RT::Tickets->new($RT::SystemUser);
+# $tix5->FromSQL('Requestor.Name LIKE "TestOne" AND Requestor.Name LIKE "TestTwo"');
+#
+# is ($tix5->Count, 1);
+#
+# my $tix6 = RT::Tickets->new($RT::SystemUser);
+# $tix6->FromSQL('Requestor.EmailAddress LIKE "rqtest1" AND Requestor.EmailAddress LIKE "rqtest2"');
+#
+# is ($tix6->Count, 1);
+
+
+=end testing
+
+=cut
+
+sub _WatcherLimit {
+ my $self = shift;
+ my $field = shift;
+ my $op = shift;
+ my $value = shift;
+ my %rest = (@_);
+
+ $self->_OpenParen;
+
+ my $groups = $self->NewAlias('Groups');
+ my $groupmembers = $self->NewAlias('CachedGroupMembers');
+ my $users = $self->NewAlias('Users');
+
+ # If we're looking for multiple watchers of a given type,
+ # TicketSQL will be handing it to us as an array of cluases in
+ # $field
+ if ( ref $field ) { # gross hack
+ $self->_OpenParen;
+ for my $chunk (@$field) {
+ ( $field, $op, $value, %rest ) = @$chunk;
+ $self->_SQLLimit(
+ ALIAS => $users,
+ FIELD => $rest{SUBKEY} || 'EmailAddress',
+ VALUE => $value,
+ OPERATOR => $op,
+ CASESENSITIVE => 0,
+ %rest
+ );
+ }
+ $self->_CloseParen;
+ }
+ else {
+ $self->_SQLLimit(
+ ALIAS => $users,
+ FIELD => $rest{SUBKEY} || 'EmailAddress',
+ VALUE => $value,
+ OPERATOR => $op,
+ CASESENSITIVE => 0,
+ %rest,
+ );
+ }
+
+ # {{{ Tie to groups for tickets we care about
+ $self->_SQLLimit(
+ ALIAS => $groups,
+ FIELD => 'Domain',
+ VALUE => 'RT::Ticket-Role',
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ $self->_SQLJoin(
+ ALIAS1 => $groups,
+ FIELD1 => 'Instance',
+ ALIAS2 => 'main',
+ FIELD2 => 'id'
+ );
+
+ # }}}
+
+ # If we care about which sort of watcher
+ my $meta = $FIELDS{$field};
+ my $type = ( defined $meta->[1] ? $meta->[1] : undef );
+
+ if ($type) {
+ $self->_SQLLimit(
+ ALIAS => $groups,
+ FIELD => 'Type',
+ VALUE => $type,
+ ENTRYAGGREGATOR => 'AND'
+ );
+ }
+
+ $self->_SQLJoin(
+ ALIAS1 => $groups,
+ FIELD1 => 'id',
+ ALIAS2 => $groupmembers,
+ FIELD2 => 'GroupId'
+ );
+
+ $self->_SQLJoin(
+ ALIAS1 => $groupmembers,
+ FIELD1 => 'MemberId',
+ ALIAS2 => $users,
+ FIELD2 => 'id'
+ );
+
+ $self->_CloseParen;
}
OPERATOR => '=',
VALUE => $restriction->{'TARGET'} );
#If we're searching on target, join the base to ticket.id
- $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'},
+ $self->_SQLJoin( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'},
ALIAS2 => $LinkAlias,
FIELD2 => 'LocalBase');
}
OPERATOR => '=',
VALUE => $restriction->{'BASE'} );
#If we're searching on base, join the target to ticket.id
- $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'},
+ $self->_SQLJoin( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'},
ALIAS2 => $LinkAlias,
FIELD2 => 'LocalTarget')
}
my $field = $rest{SUBKEY} || die "No field specified";
# For our sanity, we can only limit on one queue at a time
- my $queue = undef;
- # Ugh. This will not do well for things with underscores in them
+ my $queue = 0;
- use RT::CustomFields;
- my $CF = RT::CustomFields->new( $self->CurrentUser );
- #$CF->Load( $cfid} );
- my $q;
if ($field =~ /^(.+?)\.{(.+)}$/) {
- my $q = RT::Queue->new($self->CurrentUser);
- $q->Load($1);
+ $queue = $1;
$field = $2;
- $CF->LimitToQueue( $q->Id );
- $queue = $q->Id;
- } else {
- $CF->LimitToGlobal;
- }
- $CF->FindAllRows;
+ }
+ $field = $1 if $field =~ /^{(.+)}$/; # trim { }
- my $cfid = 0;
+ my $q = RT::Queue->new($self->CurrentUser);
+ $q->Load($queue) if ($queue);
- while ( my $CustomField = $CF->Next ) {
- if ($CustomField->Name eq $field) {
- $cfid = $CustomField->Id;
- last;
+ my $cf;
+ if ($q->id) {
+ $cf = $q->CustomField($field);
}
- }
- die "No custom field named $field found\n"
- unless $cfid;
+ else {
+ $cf = RT::CustomField->new($self->CurrentUser);
+ $cf->LoadByNameAndQueue(Queue => '0', Name => $field);
+ }
+
+
+
+
+
+ my $cfid = $cf->id;
+
+ die "No custom field named $field found\n" unless $cfid;
-# use RT::CustomFields;
-# my $CF = RT::CustomField->new( $self->CurrentUser );
-# $CF->Load( $cfid );
my $null_columns_ok;
- my $TicketCFs = $self->Join( TYPE => 'left',
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'TicketCustomFieldValues',
- FIELD2 => 'Ticket' );
+
+ my $TicketCFs;
+ # Perform one Join per CustomField
+ if ($self->{_sql_keywordalias}{$cfid}) {
+ $TicketCFs = $self->{_sql_keywordalias}{$cfid};
+ } else {
+ $TicketCFs = $self->{_sql_keywordalias}{$cfid} =
+ $self->_SQLJoin( TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'TicketCustomFieldValues',
+ FIELD2 => 'Ticket' );
+ }
$self->_OpenParen;
QUOTEVALUE => 1,
@rest );
- if ( $op =~ /^IS$/i
- or ( $op eq '!=' ) ) {
+
+ # If we're trying to find custom fields that don't match something, we want tickets
+ # where the custom field has no value at all
+
+ if ( ($op =~ /^IS$/i) || ($op =~ /^NOT LIKE$/i) || ( $op eq '!=' ) ) {
$null_columns_ok = 1;
}
+
- #If we're trying to find tickets where the keyword isn't somethng,
- #also check ones where it _IS_ null
-
- if ( $op eq '!=' ) {
+ if ( $null_columns_ok && $op !~ /IS/i && uc $value ne "NULL") {
$self->_SQLLimit( ALIAS => $TicketCFs,
FIELD => 'Content',
OPERATOR => 'IS',
Returns a frozen string suitable for handing back to ThawLimits.
=cut
+
+sub _FreezeThawKeys {
+ 'TicketRestrictions',
+ 'restriction_index',
+ 'looking_at_effective_id',
+ 'looking_at_type'
+}
+
# {{{ sub FreezeLimits
sub FreezeLimits {
my $self = shift;
require FreezeThaw;
- return (FreezeThaw::freeze($self->{'TicketRestrictions'},
- $self->{'restriction_index'}
- ));
+ return (FreezeThaw::freeze(@{$self}{$self->_FreezeThawKeys}));
}
# }}}
#We don't need to die if the thaw fails.
eval {
- ($self->{'TicketRestrictions'},
- $self->{'restriction_index'}
- ) = FreezeThaw::thaw($in);
- }
+ @{$self}{$self->_FreezeThawKeys} = FreezeThaw::thaw($in);
+ };
+ $RT::Logger->error( $@ ) if $@;
}
=head2 LimitLinkedTo
LimitLinkedTo takes a paramhash with two fields: TYPE and TARGET
-TYPE limits the sort of relationship we want to search on
+TYPE limits the sort of link we want to search on
TYPE = { RefersTo, MemberOf, DependsOn }
=head2 LimitLinkedFrom
LimitLinkedFrom takes a paramhash with two fields: TYPE and BASE
-TYPE limits the sort of relationship we want to search on
+TYPE limits the sort of link we want to search on
BASE is the id or URI of the BASE of the link
TYPE => undef,
@_);
+ # translate RT2 From/To naming to RT3 TicketSQL naming
+ my %fromToMap = qw(DependsOn DependentOn
+ MemberOf HasMember
+ RefersTo ReferredToBy);
+
+ my $type = $args{'TYPE'};
+ $type = $fromToMap{$type} if exists($fromToMap{$type});
$self->Limit( FIELD => 'LinkedTo',
TARGET => undef,
BASE => ($args{'BASE'} || $args{'TICKET'}),
- TYPE => $args{'TYPE'},
+ TYPE => $type,
DESCRIPTION => $self->loc(
"Tickets [_1] [_2]", $self->loc($args{'TYPE'}), ($args{'BASE'} || $args{'TICKET'})
),
=over 4
-=item KEYWORDSELECT - KeywordSelect id
+=item CUSTOMFIELD - CustomField name or id. If a name is passed, an additional
+parameter QUEUE may also be passed to distinguish the custom field.
-=item OPERATOR - (for KEYWORD only - KEYWORDSELECT operator is always `=')
+=item OPERATOR - The usual Limit operators
-=item KEYWORD - Keyword id
+=item VALUE - The value to compare against
=back
QUOTEVALUE => 1,
@_ );
- use RT::CustomFields;
my $CF = RT::CustomField->new( $self->CurrentUser );
- $CF->Load( $args{CUSTOMFIELD} );
+ if ( $args{CUSTOMFIELD} =~ /^\d+$/) {
+ $CF->Load( $args{CUSTOMFIELD} );
+ }
+ else {
+ $CF->LoadByNameAndQueue( Name => $args{CUSTOMFIELD}, Queue => $args{QUEUE} );
+ $args{CUSTOMFIELD} = $CF->Id;
+ }
#If we are looking to compare with a null value.
if ( $args{'OPERATOR'} =~ /^is$/i ) {
$args{'DESCRIPTION'} ||= $self->loc("Custom field [_1] [_2] [_3]", $CF->Name , $args{OPERATOR} , $args{VALUE});
}
-# my $index = $self->_NextIndex;
-# %{ $self->{'TicketRestrictions'}{$index} } = %args;
-
-
my $q = "";
if ($CF->Queue) {
my $qo = new RT::Queue( $self->CurrentUser );
$q = $qo->Name;
}
+ my @rest;
+ @rest = ( ENTRYAGGREGATOR => 'AND' )
+ if ($CF->Type eq 'SelectMultiple');
+
$self->Limit( VALUE => $args{VALUE},
FIELD => "CF.".( $q
? $q . ".{" . $CF->Name . "}"
),
OPERATOR => $args{OPERATOR},
CUSTOMFIELD => 1,
+ @rest,
);
$self->{'RecalcTicketLimits'} = 1;
- # return ($index);
}
# }}}
$self->{'primary_key'} = "id";
delete $self->{'items_array'};
delete $self->{'item_map'};
+ delete $self->{'columns_to_display'};
$self->SUPER::_Init(@_);
$self->_InitSQL;
push ( @{ $self->{'items_array'} }, $item );
}
$self->GotoItem($placeholder);
+ $self->{'items_array'} = $self->ItemsOrderBy($self->{'items_array'});
}
return ( $self->{'items_array'} );
}
# defined $restriction->{'TARGET'} ?
# $restriction->{TARGET} )
- my $ea = $DefaultEA{$type};
+ my $ea = $restriction->{ENTRYAGGREGATOR} || $DefaultEA{$type} || "AND";
if ( ref $ea ) {
die "Invalid operator $op for $field ($type)"
unless exists $ea->{$op};
$ea = $ea->{$op};
}
+
+ # Each CustomField should be put into a different Clause so they
+ # are ANDed together.
+ if ($restriction->{CUSTOMFIELD}) {
+ $realfield = $field;
+ }
+
exists $clause{$realfield} or $clause{$realfield} = [];
# Escape Quotes
$field =~ s!(['"])!\\$1!g;
#a new search
delete $self->{'TicketAliases'};
delete $self->{'items_array'};
+ delete $self->{'item_map'};
+ delete $self->{'raw_rows'};
+ delete $self->{'rows'};
+ delete $self->{'count_all'};
+
my $sql = $self->{_sql_query}; # Violating the _SQL namespace
if (!$sql||$self->{'RecalcTicketLimits'}) {
# "Restrictions to Clauses Branch\n";
delete $self->{'item_map'};
if ($items->[0]) {
- $self->{'item_map'}->{'first'} = $items->[0]->Id;
- while (my $item = shift @$items ) {
- my $id = $item->Id;
- $self->{'item_map'}->{$id}->{'defined'} = 1;
- $self->{'item_map'}->{$id}->{prev} = $prev;
- $self->{'item_map'}->{$id}->{next} = $items->[0]->Id if ($items->[0]);
- $prev = $id;
- }
- $self->{'item_map'}->{'last'} = $prev;
+ $self->{'item_map'}->{'first'} = $items->[0]->EffectiveId;
+ while (my $item = shift @$items ) {
+ my $id = $item->EffectiveId;
+ $self->{'item_map'}->{$id}->{'defined'} = 1;
+ $self->{'item_map'}->{$id}->{prev} = $prev;
+ $self->{'item_map'}->{$id}->{next} = $items->[0]->EffectiveId if ($items->[0]);
+ $prev = $id;
+ }
+ $self->{'item_map'}->{'last'} = $prev;
}
}
$ItemMap->{'first'} = first ticketid found
$ItemMap->{'last'} = last ticketid found
-$ItemMap->{$id}->{prev} = the tikcet id found before $id
-$ItemMap->{$id}->{next} = the tikcet id found after $id
+$ItemMap->{$id}->{prev} = the ticket id found before $id
+$ItemMap->{$id}->{next} = the ticket id found after $id
=cut
sub ItemMap {
my $self = shift;
- $self->_BuildItemMap() unless ($self->{'item_map'});
+ $self->_BuildItemMap() unless ($self->{'items_array'} and $self->{'item_map'});
return ($self->{'item_map'});
}