Commit a227d629 authored by Jesse Vincent's avatar Jesse Vincent
Browse files

Custom Field searching. Principals change

parent 3e609f26
......@@ -11,7 +11,7 @@ GETPARAM = $(PERL) -e'require "$(CONFIG_FILE)"; print $${$$RT::{$$ARGV[0]}};'
RT_VERSION_MAJOR = 2
RT_VERSION_MINOR = 1
RT_VERSION_PATCH = 19
RT_VERSION_PATCH = 20
RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
......
find all tickets where:
CF Foo
Has values (talk or read) AND
Has values (bar and baz) AND
doesn't have values (bing or bong)
LimitCustomFieldValues {
my %args = ( CustomField => undef,
ClauseId => 'CustomFields',
OPERATOR => undef,
ENTRYAGGREGATOR => undef,
VALUES => undef,
@_) ;
unless ( $self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} ) {
$self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} = $self->NewAlias('CustomFields');
$self->Join(TABLE1 =>$self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField' },
FIELD1 => 'QueueId',
TABLE2 => 'main', FIELD2 => 'QueueId');
if ($args{'OPERATOR'} =~ /!=|IS/i) {
}
else {
}
}
# {{{ if it's a keyword
elsif ( $TYPES{ $restriction->{'FIELD'} } eq 'CUSTOMFIELD' ) {
my $null_columns_ok;
my $TicketCFs = $self->Join( TYPE => 'left',
ALIAS1 => 'main',
FIELD1 => 'id',
TABLE2 => 'TicketCustomFieldValues',
FIELD2 => 'Ticket' );
foreach my $value ( @{ $restriction->{'VALUES'} } ) {
$self->SUPER::Limit( ALIAS => $TicketCFs,
FIELD => 'Content',
OPERATOR => $restriction->{'OPERATOR'},
VALUE => $value,
QUOTEVALUE => $restriction->{'QUOTEVALUE'},
ENTRYAGGREGATOR => 'AND', );
}
if ( ( $restriction->{'OPERATOR'} =~ /^IS$/i ) or ( $restriction->{'OPERATOR'} 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 ( $restriction->{'OPERATOR'} eq '!=' ) {
$self->SUPER::Limit( ALIAS => $TicketCFs,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'OR', );
}
$self->SUPER::Limit( LEFTJOIN => $TicketCFs,
FIELD => 'CustomField',
VALUE => $restriction->{'CUSTOMFIELD'},
ENTRYAGGREGATOR => 'OR' );
}
# }}}
}
......@@ -3,10 +3,7 @@ Current planned 2.2 feature list. subject to change.
Core
Should Make "Owner" a watcher type, rather than a special ticket attribute,
under the hood. This wins for ACL and code consistency reasons.
This loses for performance reasons and the fact that it makes
it easier to lose and possibly have no owner or two owners.
Web UI
......@@ -69,7 +66,6 @@ nice Scrips could apply to a list of queues, rather than just one queue or
Custom fields
Should String and multi-string custom fields.
Nice Date custom fields
Nice Some way to order and group custom fields.
Nice Default values
......@@ -80,8 +76,6 @@ Nice Make custom fields apply to an enumerated list of queues,
Web infrastructure
Must Full fastcgi support.
Installation
......
% if ($CustomField->Type =~ /Select/i) {
% my $values = $CustomField->Values;
<select name="<%$Name%>">
<option value="" SELECTED>-</option>
<option value="null"><&|/l&>(no value)</&></option>
% while (my $value = $values->Next) {
<option value="<%$value->Name%>"><%$value->Name%></option>
% }
</select>
% }
% else {
<input name="<%$Name%>" size="20">
% }
<%args>
$Name => undef
$CustomField =>undef
</%args>
......@@ -56,6 +56,20 @@
&>
<& /Elements/SelectStatus, Name => "ValueOfStatus" &>
% while ( my $CustomField = $CustomFields->Next ) {
<li><% $CustomField->Name %>
<& /Elements/SelectBoolean, Name => "CustomFieldOp". $CustomField->id,
True => loc("is"),
False => loc("isn't"),
TrueVal=> '=', FalseVal => '!=' &>
<& /Elements/SelectCustomFieldValue, Name => "CustomField".$CustomField->id,
CustomField => $CustomField,
&>
% }
</UL>
<& /Elements/TitleBoxEnd &>
......@@ -91,4 +105,11 @@
<%INIT>
my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
foreach ( $session{'tickets'}->RestrictionValues('Queue') ) {
$CustomFields->LimitToQueue($_);
}
$CustomFields->LimitToGlobal();
</%INIT>
......@@ -8,7 +8,7 @@
<& /Ticket/Elements/ShowBasics, Ticket => $Ticket &>
<& /Elements/TitleBoxEnd &>
<& /Elements/TitleBoxStart, title => loc('Custom Fields'),
title_href =>"$RT::WebPath/Ticket/CustomFields.html?id=".$Ticket->Id,
# title_href =>"$RT::WebPath/Ticket/CustomFields.html?id=".$Ticket->Id, #
title_class=> 'inverse',
color => "#993333" &>
<& /Ticket/Elements/ShowCustomFields, Ticket => $Ticket &>
......
......@@ -1055,6 +1055,8 @@ sub _CanonicalizePrincipal {
$princ_obj->Load( $princ_id );
unless ($princ_obj->Id) {
use Carp;
$RT::Logger->crit(Carp::cluck);
$RT::Logger->crit("Can't load a principal for id $princ_id");
return($princ_obj, undef);
}
......
......@@ -370,27 +370,29 @@ sub _Create {
);
$RT::Handle->BeginTransaction();
# Groups deal with principal ids, rather than user ids.
# When creating this user, set up a principal Id for it.
my $principal = RT::Principal->new( $self->CurrentUser );
my $principal_id = $principal->Create(
PrincipalType => 'Group',
ObjectId => '0'
);
$principal->__Set(Field => 'ObjectId', Value => $principal_id);
my $id = $self->SUPER::Create(
$self->SUPER::Create(
Id => $principal_id,
Name => $args{'Name'},
Description => $args{'Description'},
Type => $args{'Type'},
Domain => $args{'Domain'},
Instance => $args{'Instance'}
);
my $id = $self->Id;
unless ($id) {
return ( 0, $self->loc('Could not create group') );
}
# Groups deal with principal ids, rather than user ids.
# When creating this user, set up a principal Id for it.
my $principal = RT::Principal->new( $self->CurrentUser );
my $principal_id = $principal->Create(
PrincipalType => 'Group',
ObjectId => $id
);
# If we couldn't create a principal Id, get the fuck out.
unless ($principal_id) {
$RT::Handle->Rollback();
......@@ -1071,7 +1073,7 @@ Returns this user's PrincipalId
sub PrincipalId {
my $self = shift;
return $self->PrincipalObj->Id;
return $self->Id;
}
# }}}
......
......@@ -147,11 +147,11 @@ sub loc_match {
my $regex = quotemeta($entry);
$regex =~ s/\\\[_\d\\\]/(.*?)/;
if (my @matched = ($msg =~ /^$regex$/)) {
return $self->loc($entry, @matched);
return loc($entry, @matched);
}
}
return $self->loc($msg);
return loc($msg);
}
# }}}
......@@ -384,7 +384,7 @@ sub MakeMIMEEntity {
Data => [ $args{'Body'} ]
);
my $cgi_object = $r->cgi_object;
my $cgi_object = $m->cgi_object;
if (my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
......@@ -590,6 +590,42 @@ sub ProcessSearchQuery {
}
# }}}
# {{{ Limit CustomFields
foreach my $arg ( keys %{ $args{ARGS} } ) {
my $id;
if ( $arg =~ /^CustomField(\d+)$/ ) {
$id = $1;
}
else {
next;
}
next unless ( $args{ARGS}->{$arg} );
my $form = $args{ARGS}->{$arg};
my $oper = $args{ARGS}->{ "CustomFieldOp" . $id };
foreach my $value ( ref($form) ? @{$form} : ($form) ) {
my $quote = 1;
if ( $KeywordId =~ /^null$/i ) {
#Don't quote the string 'null'
$quote = 0;
# Convert the operator to something apropriate for nulls
$oper = 'IS' if ( $oper eq '=' );
$oper = 'IS NOT' if ( $oper eq '!=' );
}
$session{'tickets'}->LimitCustomField( CUSTOMFIELD => $id,
OPERATOR => $oper,
QUOTEVALUE => $quote,
VALUE => $value );
}
}
# }}}
}
# }}}
......@@ -711,7 +747,7 @@ sub UpdateRecordObject {
my $method = "Set$attribute";
my ( $code, $msg ) = $object->$method( $ARGSRef->{"$attribute"} );
push @results, $self->loc($attribute) . ': '. $self->loc_match(
push @results, loc($attribute) . ': '. loc_match(
$msg,
"[_1] could not be set to [_2].", # loc
"That is already the current value", # loc
......
......@@ -26,7 +26,8 @@ ok(require RT::Template);
no warnings qw(redefine);
use Safe;
use Text::Template;
use MIME::Entity;
use MIME::Parser;
......@@ -312,8 +313,6 @@ sub _ParseContent {
@_
);
# Might be subject to change
use Text::Template;
$T::Ticket = $args{'TicketObj'};
$T::Transaction = $args{'TransactionObj'};
......@@ -329,7 +328,8 @@ sub _ParseContent {
SOURCE => $content
);
my $retval = $template->fill_in( PACKAGE => T );
my $safe = Safe->new();
my $retval = $template->fill_in( SAFE => $safe, PACKAGE => T );
return ($retval);
}
......
......@@ -62,6 +62,7 @@ use vars qw(%TYPES @SORTFIELDS);
TransactionDate => 'TRANSDATE',
LinkedTo => 'LINKFIELD',
Watcher => 'WATCHERFIELD',
CustomFieldValue => 'CUSTOMFIELD'
);
......@@ -540,6 +541,13 @@ sub LimitOwner {
VALUE is a value to match the ticket\'s watcher email addresses against
TYPE is the sort of watchers you want to match against. Leave it undef if you want to search all of them
=begin testing
my $t1 = RT::Ticket->new($RT::SystemUser);
$t1->Create(Subject => "LimitWatchers test", Requestors => \['requestor1@example.com']);
=end testing
=cut
sub LimitWatcher {
......@@ -569,53 +577,18 @@ sub LimitWatcher {
);
}
# }}}
# {{{ sub LimitRequestor
=head2 LimitRequestor
It\'s like LimitWatcher, but it presets TYPE to Requestor
=cut
sub LimitRequestor {
my $self = shift;
$self->LimitWatcher(TYPE=> 'Requestor', @_);
}
# }}}
# {{{ sub LimitCc
=head2 LimitCC
It\'s like LimitWatcher, but it presets TYPE to Cc
=cut
my %args = (@_);
my ($package, $filename, $line) = caller;
$RT::Logger->error("Tickets->LimitRequestor is deprecated. please rewrite call at $package - $filename: $line");
$self->LimitWatcher(TYPE => 'Requestor', @_);
sub LimitCc {
my $self = shift;
$self->LimitWatcher(TYPE=> 'Cc', @_);
}
# }}}
# {{{ sub LimitAdminCc
=head2 LimitAdminCc
It\'s like LimitWatcher, but it presets TYPE to AdminCc
=cut
sub LimitAdminCc {
my $self = shift;
$self->LimitWatcher(TYPE=> 'AdminCc', @_);
}
# }}}
# }}}
......@@ -874,6 +847,63 @@ sub LimitTransactionDate {
# }}}
# {{{ Limit based on custom fields
# {{{ sub LimitCustomField
=head2 LimitCustomField
Takes a paramhash of key/value pairs with the following keys:
=over 4
=item KEYWORDSELECT - KeywordSelect id
=item OPERATOR - (for KEYWORD only - KEYWORDSELECT operator is always `=')
=item KEYWORD - Keyword id
=back
=cut
sub LimitCustomField {
my $self = shift;
my %args = ( VALUE => undef,
CUSTOMFIELD => undef,
OPERATOR => '=',
DESCRIPTION => undef,
FIELD => 'CustomFieldValue',
QUOTEVALUE => 1,
@_ );
use RT::CustomFields;
my $CF = RT::CustomField->new( $self->CurrentUser );
$CF->Load( $args{CUSTOMFIELD} );
#If we are looking to compare with a null value.
if ( $args{'OPERATOR'} =~ /^is$/i ) {
$args{'DESCRIPTION'} ||= $self->loc("Custom field [_1] has no value.", $CF->Name);
}
elsif ( $args{'OPERATOR'} =~ /^is not$/i ) {
$args{'DESCRIPTION'} ||= $self->loc("Custom field [_1] has a value.", $CF->Name);
}
# if we're not looking to compare with a null value
else {
$args{'DESCRIPTION'} ||= $self->loc("Custom field [_1] [_2] [_3]", $CF->Name , $args{OPERATOR} , $args{VALUE});
}
my $index = $self->_NextIndex;
%{ $self->{'TicketRestrictions'}{$index} } = %args;
$self->{'RecalcTicketLimits'} = 1;
return ($index);
}
# }}}
# }}}
# {{{ sub _NextIndex
......@@ -906,14 +936,6 @@ sub _Init {
}
# }}}
# {{{ sub NewItem
sub NewItem {
my $self = shift;
return(RT::Ticket->new($self->CurrentUser));
}
# }}}
# {{{ sub Count
sub Count {
my $self = shift;
......@@ -1320,9 +1342,103 @@ sub _ProcessRestrictions {
# }}}
# {{{ if it's a watcher that we're hunting for
elsif ($TYPES{$restriction->{'FIELD'}} eq 'WATCHERFIELD') {
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');
# {{{ Tie to groups for tickets we care about
$self->SUPER::Limit(ALIAS => $groups,
FIELD => 'Domain',
VALUE => 'TicketRole');
$self->Join(ALIAS1 => $groups, FIELD1 => 'Instance',
ALIAS2 => 'main', FIELD2 => 'id');
# }}}
# If we care about which sort of watcher
if ($restriction->{'TYPE'} ) {
$self->SUPER::Limit(ALIAS => $groups,
FIELD => 'Type',
VALUE => $restriction->{'TYPE'});
}
$self->Join (ALIAS1 => $groups, FIELD1 => 'id',
ALIAS2 => $group_princs, FIELD2 => 'ObjectId');
$self->SUPER::Limit(ALIAS => $group_princs,
FIELD => 'PrincipalType',
VALUE => 'Group');
$self->Join( ALIAS1 => $group_princs, FIELD1 => 'id',
ALIAS2 => $groupmembers, FIELD2 => 'GroupId');
$self->Join( ALIAS1 => $groupmembers, FIELD1 => 'MemberId',
ALIAS2 => $member_princs, FIELD2 => 'id');
$self->Join (ALIAS1 => $member_princs, FIELD1 => 'ObjectId',
ALIAS2 => $users, FIELD2 => 'id');
#Find user watchers
my $subclause = undef;
my $aggregator = 'OR';
if ($restriction->{'OPERATOR'} =~ /!|NOT/i ){
$subclause = 'AndEmailIsNot';
$aggregator = 'AND';
}
$self->SUPER::Limit(ALIAS => $users,
SUBCLAUSE => $subclause,
FIELD => 'EmailAddress',
ENTRYAGGREGATOR => $aggregator,
VALUE => $restriction->{'VALUE'},
OPERATOR => $restriction->{'OPERATOR'},
CASESENSITIVE => 0
);
}
# }}}
# {{{ if it's a CUSTOMFIELD
elsif ( $TYPES{ $restriction->{'FIELD'} } eq 'CUSTOMFIELD' ) {
my $null_columns_ok;
my $TicketCFs = $self->Join( TYPE => 'left',
ALIAS1 => 'main',
FIELD1 => 'id',
TABLE2 => 'TicketCustomFieldValues',
FIELD2 => 'Ticket' );
$self->SUPER::Limit( ALIAS => $TicketCFs,
FIELD => 'Content',
OPERATOR => $restriction->{'OPERATOR'},
VALUE => $restriction->{'VALUE'},
QUOTEVALUE => $restriction->{'QUOTEVALUE'},
ENTRYAGGREGATOR => 'AND', );
if ( ( $restriction->{'OPERATOR'} =~ /^IS$/i ) or ( $restriction->{'OPERATOR'} 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 ( $restriction->{'OPERATOR'} eq '!=' ) {
$self->SUPER::Limit( ALIAS => $TicketCFs,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'OR', );
}
$self->SUPER::Limit( LEFTJOIN => $TicketCFs,
FIELD => 'CustomField',
VALUE => $restriction->{'CUSTOMFIELD'},
ENTRYAGGREGATOR => 'OR' );
}
# }}}
}
......
......@@ -164,8 +164,22 @@ sub Create {
$RT::Handle->BeginTransaction();
# Groups deal with principal ids, rather than user ids.
# When creating this user, set up a principal Id for it.
my $principal = RT::Principal->new($self->CurrentUser);
my $principal_id = $principal->Create(PrincipalType => 'User',
ObjectId => '0');
$principal->__Set(Field => 'ObjectId', Value => $principal_id);
# If we couldn't create a principal Id, get the fuck out.
unless ($principal_id) {
$RT::Handle->Rollback();
$self->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
return ( 0, $self->loc('Could not create user') );
}
my $id = $self->SUPER::Create(%args);
$self->SUPER::Create(id => $principal_id , %args);
my $id = $self->Id;
#If the create failed.
unless ($id) {
......@@ -182,19 +196,6 @@ sub Create {
#}
# Groups deal with principal ids, rather than user ids.
# When creating this user, set up a principal Id for it.
my $principal = RT::Principal->new($self->CurrentUser);
my $principal_id = $principal->Create(PrincipalType => 'User',
ObjectId => $id);