X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FTickets.pm;h=b6b349144dfc9c5a7ae97832fd713d92370aa1a2;hp=dd91126c473e2e234b5a646f348dd8163deb43ac;hb=ded0451e9582df33cae6099a2fb72b4ea25076cf;hpb=c0567c688084e89fcd11bf82348b6c418f1254ac diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm index dd91126c4..b6b349144 100755 --- a/rt/lib/RT/Tickets.pm +++ b/rt/lib/RT/Tickets.pm @@ -1,1789 +1,115 @@ -#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Tickets.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $ +# BEGIN LICENSE BLOCK +# +# Copyright (c) 1996-2003 Jesse Vincent +# +# (Except where explictly superceded by other copyright notices) +# +# 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. +# +# 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. +# +# +# END LICENSE BLOCK +# Autogenerated by DBIx::SearchBuilder factory (by ) +# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST. +# +# !! DO NOT EDIT THIS FILE !! +# -=head1 NAME +use strict; - RT::Tickets - A collection of Ticket objects +=head1 NAME + RT::Tickets -- Class Description + =head1 SYNOPSIS - use RT::Tickets; - my $tickets = new RT::Tickets($CurrentUser); + use RT::Tickets =head1 DESCRIPTION - A collection of RT::Tickets. =head1 METHODS -=begin testing - -ok (require RT::TestHarness); -ok (require RT::Tickets); - -=end testing - =cut package RT::Tickets; -use RT::EasySearch; -use RT::Ticket; -@ISA= qw(RT::EasySearch); - -use vars qw(%TYPES @SORTFIELDS); - -# {{{ TYPES - -%TYPES = ( Status => 'ENUM', - Queue => 'ENUM', - Type => 'ENUM', - Creator => 'ENUM', - LastUpdatedBy => 'ENUM', - Owner => 'ENUM', - EffectiveId => 'INT', - id => 'INT', - InitialPriority => 'INT', - FinalPriority => 'INT', - Priority => 'INT', - TimeLeft => 'INT', - TimeWorked => 'INT', - MemberOf => 'LINK', - DependsOn => 'LINK', - HasMember => 'LINK', - HasDepender => 'LINK', - RelatedTo => 'LINK', - Told => 'DATE', - StartsBy => 'DATE', - Started => 'DATE', - Due => 'DATE', - Resolved => 'DATE', - LastUpdated => 'DATE', - Created => 'DATE', - Subject => 'STRING', - Type => 'STRING', - Content => 'TRANSFIELD', - ContentType => 'TRANSFIELD', - TransactionDate => 'TRANSDATE', - Watcher => 'WATCHERFIELD', - LinkedTo => 'LINKFIELD', - Keyword => 'KEYWORDFIELD' - - ); - - -# }}} - -# {{{ sub SortFields - -@SORTFIELDS = qw(id Status Owner Created Due Starts Started - Queue Subject Told Started - Resolved LastUpdated Priority TimeWorked TimeLeft); - -=head2 SortFields - -Returns the list of fields that lists of tickets can easily be sorted by - -=cut - - -sub SortFields { - my $self = shift; - return(@SORTFIELDS); -} - - -# }}} - -# {{{ Limit the result set based on content - -# {{{ sub Limit - -=head2 Limit - -Takes a paramhash with the fields FIELD, OPERATOR, VALUE and DESCRIPTION -Generally best called from LimitFoo methods - -=cut -sub Limit { - my $self = shift; - my %args = ( FIELD => undef, - OPERATOR => '=', - VALUE => undef, - DESCRIPTION => undef, - @_ - ); - $args{'DESCRIPTION'} = "Autodescribed: ".$args{'FIELD'} . $args{'OPERATOR'} . $args{'VALUE'}, - if (!defined $args{'DESCRIPTION'}) ; - - my $index = $self->_NextIndex; - - #make the TicketRestrictions hash the equivalent of whatever we just passed in; - - %{$self->{'TicketRestrictions'}{$index}} = %args; - - $self->{'RecalcTicketLimits'} = 1; - - # If we're looking at the effective id, we don't want to append the other clause - # which limits us to tickets where id = effective id - if ($args{'FIELD'} eq 'EffectiveId') { - $self->{'looking_at_effective_id'} = 1; - } - - return ($index); -} - -# }}} - - - - -=head2 FreezeLimits - -Returns a frozen string suitable for handing back to ThawLimits. - -=cut -# {{{ sub FreezeLimits - -sub FreezeLimits { - my $self = shift; - require FreezeThaw; - return (FreezeThaw::freeze($self->{'TicketRestrictions'}, - $self->{'restriction_index'} - )); -} - -# }}} - -=head2 ThawLimits - -Take a frozen Limits string generated by FreezeLimits and make this tickets -object have that set of limits. - -=cut -# {{{ sub ThawLimits - -sub ThawLimits { - my $self = shift; - my $in = shift; - - #if we don't have $in, get outta here. - return undef unless ($in); - - $self->{'RecalcTicketLimits'} = 1; - - require FreezeThaw; - - #We don't need to die if the thaw fails. - - eval { - ($self->{'TicketRestrictions'}, - $self->{'restriction_index'} - ) = FreezeThaw::thaw($in); - } - -} - -# }}} - -# {{{ Limit by enum or foreign key - -# {{{ sub LimitQueue - -=head2 LimitQueue - -LimitQueue takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of = or !=. (It defaults to =). -VALUE is a queue id. - -=cut - -sub LimitQueue { - my $self = shift; - my %args = (VALUE => undef, - OPERATOR => '=', - @_); - - #TODO VALUE should also take queue names and queue objects - my $queue = new RT::Queue($self->CurrentUser); - $queue->Load($args{'VALUE'}); - - #TODO check for a valid queue here - - $self->Limit (FIELD => 'Queue', - VALUE => $queue->id(), - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Queue ' . $args{'OPERATOR'}. " ". $queue->Name - ); - -} -# }}} - -# {{{ sub LimitStatus - -=head2 LimitStatus - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of = or !=. -VALUE is a status. - -=cut - -sub LimitStatus { - my $self = shift; - my %args = ( OPERATOR => '=', - @_); - $self->Limit (FIELD => 'Status', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Status ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# {{{ sub LimitType - -=head2 LimitType - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of = or !=, it defaults to "=". -VALUE is a string to search for in the type of the ticket. - -=cut - -sub LimitType { - my $self = shift; - my %args = (OPERATOR => '=', - VALUE => undef, - @_); - $self->Limit (FIELD => 'Type', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Type ' . $args{'OPERATOR'}. " ". $args{'Limit'}, - ); -} - -# }}} - -# }}} - -# {{{ Limit by string field - -# {{{ sub LimitSubject - -=head2 LimitSubject - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of = or !=. -VALUE is a string to search for in the subject of the ticket. - -=cut - -sub LimitSubject { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'Subject', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Subject ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# }}} - -# {{{ Limit based on ticket numerical attributes -# Things that can be > < = != - -# {{{ sub LimitId - -=head2 LimitId - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a ticket Id to search for - -=cut - -sub LimitId { - my $self = shift; - my %args = (OPERATOR => '=', - @_); - - $self->Limit (FIELD => 'id', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Id ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# {{{ sub LimitPriority - -=head2 LimitPriority - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a value to match the ticket\'s priority against - -=cut - -sub LimitPriority { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'Priority', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Priority ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# {{{ sub LimitInitialPriority - -=head2 LimitInitialPriority - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a value to match the ticket\'s initial priority against - - -=cut - -sub LimitInitialPriority { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'InitialPriority', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Initial Priority ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} -# {{{ sub LimitFinalPriority - -=head2 LimitFinalPriority - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a value to match the ticket\'s final priority against - -=cut - -sub LimitFinalPriority { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'FinalPriority', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Final Priority ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# {{{ sub LimitTimeWorked - -=head2 LimitTimeWorked - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a value to match the ticket's TimeWorked attribute - -=cut - -sub LimitTimeWorked { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'TimeWorked', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Time worked ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# {{{ sub LimitTimeLeft - -=head2 LimitTimeLeft - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, >, < or !=. -VALUE is a value to match the ticket's TimeLeft attribute - -=cut - -sub LimitTimeLeft { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'TimeLeft', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Time left ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} - -# }}} - -# {{{ Limiting based on attachment attributes - -# {{{ sub LimitContent - -=head2 LimitContent - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, LIKE, NOT LIKE or !=. -VALUE is a string to search for in the body of the ticket - -=cut -sub LimitContent { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'Content', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Ticket content ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} - -# }}} -# {{{ sub LimitContentType - -=head2 LimitContentType - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of =, LIKE, NOT LIKE or !=. -VALUE is a content type to search ticket attachments for - -=cut - -sub LimitContentType { - my $self = shift; - my %args = (@_); - $self->Limit (FIELD => 'ContentType', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Ticket content type ' . $args{'OPERATOR'}. " ". $args{'VALUE'}, - ); -} -# }}} - -# }}} - -# {{{ Limiting based on people - -# {{{ sub LimitOwner - -=head2 LimitOwner - -Takes a paramhash with the fields OPERATOR and VALUE. -OPERATOR is one of = or !=. -VALUE is a user id. - -=cut - -sub LimitOwner { - my $self = shift; - my %args = ( OPERATOR => '=', - @_); - - my $owner = new RT::User($self->CurrentUser); - $owner->Load($args{'VALUE'}); - $self->Limit (FIELD => 'Owner', - VALUE => $owner->Id, - OPERATOR => $args{'OPERATOR'}, - DESCRIPTION => 'Owner ' . $args{'OPERATOR'}. " ". $owner->Name() - ); - -} - -# }}} - -# {{{ Limiting watchers - -# {{{ sub LimitWatcher +use RT::SearchBuilder; +use RT::Ticket; +use vars qw( @ISA ); +@ISA= qw(RT::SearchBuilder); -=head2 LimitWatcher - - Takes a paramhash with the fields OPERATOR, TYPE and VALUE. - OPERATOR is one of =, LIKE, NOT LIKE or !=. - 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 -=cut - -sub LimitWatcher { +sub _Init { my $self = shift; - my %args = ( OPERATOR => '=', - VALUE => undef, - TYPE => undef, - @_); - - - #build us up a description - my ($watcher_type, $desc); - if ($args{'TYPE'}) { - $watcher_type = $args{'TYPE'}; - } - else { - $watcher_type = "Watcher"; - } - $desc = "$watcher_type ".$args{'OPERATOR'}." ".$args{'VALUE'}; - - - $self->Limit (FIELD => 'Watcher', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - TYPE => $args{'TYPE'}, - DESCRIPTION => "$desc" - ); -} - -# }}} - -# {{{ sub LimitRequestor + $self->{'table'} = 'Tickets'; + $self->{'primary_key'} = 'id'; -=head2 LimitRequestor -It\'s like LimitWatcher, but it presets TYPE to Requestor - -=cut - - -sub LimitRequestor { - my $self = shift; - $self->LimitWatcher(TYPE=> 'Requestor', @_); + return ( $self->SUPER::_Init(@_) ); } -# }}} - -# {{{ sub LimitCc -=head2 LimitCC +=item NewItem -It\'s like LimitWatcher, but it presets TYPE to Cc +Returns an empty new RT::Ticket item =cut -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 { +sub NewItem { my $self = shift; - $self->LimitWatcher(TYPE=> 'AdminCc', @_); + return(RT::Ticket->new($self->CurrentUser)); } -# }}} - -# }}} - -# }}} + eval "require RT::Tickets_Overlay"; + if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Overlay.pm}) { + die $@; + }; -# {{{ Limiting based on links + eval "require RT::Tickets_Vendor"; + if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Vendor.pm}) { + die $@; + }; -# {{{ LimitLinkedTo + eval "require RT::Tickets_Local"; + if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Local.pm}) { + die $@; + }; -=head2 LimitLinkedTo -LimitLinkedTo takes a paramhash with two fields: TYPE and TARGET -TYPE limits the sort of relationship we want to search on -TARGET is the id or URI of the TARGET of the link -(TARGET used to be 'TICKET'. 'TICKET' is deprecated, but will be treated as TARGET -=cut - -sub LimitLinkedTo { - my $self = shift; - my %args = ( - TICKET => undef, - TARGET => undef, - TYPE => undef, - @_); - - - $self->Limit( FIELD => 'LinkedTo', - BASE => undef, - TARGET => ($args{'TARGET'} || $args{'TICKET'}), - TYPE => $args{'TYPE'}, - DESCRIPTION => "Tickets ".$args{'TYPE'}." by ".($args{'TARGET'} || $args{'TICKET'}) - ); -} - - -# }}} +=head1 SEE ALSO -# {{{ LimitLinkedFrom +This class allows "overlay" methods to be placed +into the following files _Overlay is for a System overlay by the original author, +_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations. -=head2 LimitLinkedFrom +These overlay files can contain new subs or subs to replace existing subs in this module. -LimitLinkedFrom takes a paramhash with two fields: TYPE and BASE -TYPE limits the sort of relationship we want to search on +If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line + no warnings qw(redefine); -BASE is the id or URI of the BASE of the link -(BASE used to be 'TICKET'. 'TICKET' is deprecated, but will be treated as BASE +so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. +RT::Tickets_Overlay, RT::Tickets_Vendor, RT::Tickets_Local =cut -sub LimitLinkedFrom { - my $self = shift; - my %args = ( BASE => undef, - TICKET => undef, - TYPE => undef, - @_); - - - $self->Limit( FIELD => 'LinkedTo', - TARGET => undef, - BASE => ($args{'BASE'} || $args{'TICKET'}), - TYPE => $args{'TYPE'}, - DESCRIPTION => "Tickets " .($args{'BASE'} || $args{'TICKET'}) ." ".$args{'TYPE'} - ); -} - - -# }}} - -# {{{ LimitMemberOf -sub LimitMemberOf { - my $self = shift; - my $ticket_id = shift; - $self->LimitLinkedTo ( TARGET=> "$ticket_id", - TYPE => 'MemberOf', - ); - -} -# }}} - -# {{{ LimitHasMember -sub LimitHasMember { - my $self = shift; - my $ticket_id =shift; - $self->LimitLinkedFrom ( BASE => "$ticket_id", - TYPE => 'MemberOf', - ); - -} -# }}} - -# {{{ LimitDependsOn - -sub LimitDependsOn { - my $self = shift; - my $ticket_id = shift; - $self->LimitLinkedTo ( TARGET => "$ticket_id", - TYPE => 'DependsOn', - ); - -} - -# }}} - -# {{{ LimitDependedOnBy - -sub LimitDependedOnBy { - my $self = shift; - my $ticket_id = shift; - $self->LimitLinkedFrom ( BASE => "$ticket_id", - TYPE => 'DependsOn', - ); - -} - -# }}} - - -# {{{ LimitRefersTo - -sub LimitRefersTo { - my $self = shift; - my $ticket_id = shift; - $self->LimitLinkedTo ( TARGET => "$ticket_id", - TYPE => 'RefersTo', - ); - -} - -# }}} - -# {{{ LimitReferredToBy - -sub LimitReferredToBy { - my $self = shift; - my $ticket_id = shift; - $self->LimitLinkedFrom ( BASE=> "$ticket_id", - TYPE => 'RefersTo', - ); - -} - -# }}} - -# }}} - -# {{{ limit based on ticket date attribtes - -# {{{ sub LimitDate - -=head2 LimitDate (FIELD => 'DateField', OPERATOR => $oper, VALUE => $ISODate) - -Takes a paramhash with the fields FIELD OPERATOR and VALUE. - -OPERATOR is one of > or < -VALUE is a date and time in ISO format in GMT -FIELD is one of Starts, Started, Told, Created, Resolved, LastUpdated - -There are also helper functions of the form LimitFIELD that eliminate -the need to pass in a FIELD argument. - -=cut - -sub LimitDate { - my $self = shift; - my %args = ( - FIELD => undef, - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - - @_); - - #Set the description if we didn't get handed it above - unless ($args{'DESCRIPTION'} ) { - $args{'DESCRIPTION'} = $args{'FIELD'} . " " .$args{'OPERATOR'}. " ". $args{'VALUE'} . " GMT" - } - - $self->Limit (%args); - -} - -# }}} - - - - -sub LimitCreated { - my $self = shift; - $self->LimitDate( FIELD => 'Created', @_); -} -sub LimitDue { - my $self = shift; - $self->LimitDate( FIELD => 'Due', @_); - -} -sub LimitStarts { - my $self = shift; - $self->LimitDate( FIELD => 'Starts', @_); - -} -sub LimitStarted { - my $self = shift; - $self->LimitDate( FIELD => 'Started', @_); -} -sub LimitResolved { - my $self = shift; - $self->LimitDate( FIELD => 'Resolved', @_); -} -sub LimitTold { - my $self = shift; - $self->LimitDate( FIELD => 'Told', @_); -} -sub LimitLastUpdated { - my $self = shift; - $self->LimitDate( FIELD => 'LastUpdated', @_); -} -# -# {{{ sub LimitTransactionDate - -=head2 LimitTransactionDate (OPERATOR => $oper, VALUE => $ISODate) - -Takes a paramhash with the fields FIELD OPERATOR and VALUE. - -OPERATOR is one of > or < -VALUE is a date and time in ISO format in GMT - - -=cut - -sub LimitTransactionDate { - my $self = shift; - my %args = ( - FIELD => 'TransactionDate', - VALUE => $args{'VALUE'}, - OPERATOR => $args{'OPERATOR'}, - - @_); - - #Set the description if we didn't get handed it above - unless ($args{'DESCRIPTION'} ) { - $args{'DESCRIPTION'} = $args{'FIELD'} . " " .$args{'OPERATOR'}. " ". $args{'VALUE'} . " GMT" - } - - $self->Limit (%args); - -} - -# }}} - -# }}} - -# {{{ sub LimitKeyword - -=head2 LimitKeyword - -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 LimitKeyword { - my $self = shift; - my %args = ( KEYWORD => undef, - KEYWORDSELECT => undef, - OPERATOR => '=', - DESCRIPTION => undef, - FIELD => 'Keyword', - QUOTEVALUE => 1, - @_ - ); - - use RT::KeywordSelect; - my $KeywordSelect = RT::KeywordSelect->new($self->CurrentUser); - $KeywordSelect->Load($args{KEYWORDSELECT}); - - - # Below, We're checking to see whether the keyword we're searching for - # is null or not. - # This could probably be rewritten to be easier to read and understand - - - #If we are looking to compare with a null value. - if ($args{'OPERATOR'} =~ /is/i) { - if ($args{'OPERATOR'} =~ /^is$/i) { - $args{'DESCRIPTION'} ||= "Keyword Selection ". $KeywordSelect->Name . " has no value"; - } - elsif ($args{'OPERATOR'} =~ /^is not$/i) { - $args{'DESCRIPTION'} ||= "Keyword Selection ". $KeywordSelect->Name . " has a value"; - } - } - # if we're not looking to compare with a null value - else { - use RT::Keyword; - my $Keyword = RT::Keyword->new($self->CurrentUser); - $Keyword->Load($args{KEYWORD}); - $args{'DESCRIPTION'} ||= "Keyword Selection " . $KeywordSelect->Name. " $args{OPERATOR} ". $Keyword->Name; - } - - $args{SingleValued} = $KeywordSelect->Single(); - - - my $index = $self->_NextIndex; - %{$self->{'TicketRestrictions'}{$index}} = %args; - - $self->{'RecalcTicketLimits'} = 1; - return ($index); -} - -# }}} - -# {{{ sub _NextIndex - -=head2 _NextIndex - -Keep track of the counter for the array of restrictions - -=cut - -sub _NextIndex { - my $self = shift; - return ($self->{'restriction_index'}++); -} -# }}} - -# }}} - -# {{{ Core bits to make this a DBIx::SearchBuilder object - -# {{{ sub _Init -sub _Init { - my $self = shift; - $self->{'table'} = "Tickets"; - $self->{'RecalcTicketLimits'} = 1; - $self->{'looking_at_effective_id'} = 0; - $self->{'restriction_index'} =1; - $self->{'primary_key'} = "id"; - $self->SUPER::_Init(@_); - -} -# }}} - -# {{{ sub NewItem -sub NewItem { - my $self = shift; - return(RT::Ticket->new($self->CurrentUser)); - -} -# }}} - -# {{{ sub Count -sub Count { - my $self = shift; - $self->_ProcessRestrictions if ($self->{'RecalcTicketLimits'} == 1 ); - return($self->SUPER::Count()); -} -# }}} - -# {{{ sub ItemsArrayRef - -=head2 ItemsArrayRef - -Returns a reference to the set of all items found in this search - -=cut - -sub ItemsArrayRef { - my $self = shift; - my @items; - - my $placeholder = $self->_ItemsCounter; - $self->GotoFirstItem(); - while (my $item = $self->Next) { - push (@items, $item); - } - - $self->GotoItem($placeholder); - return(\@items); -} -# }}} - -# {{{ sub Next -sub Next { - my $self = shift; - - $self->_ProcessRestrictions if ($self->{'RecalcTicketLimits'} == 1 ); - - my $Ticket = $self->SUPER::Next(); - if ((defined($Ticket)) and (ref($Ticket))) { - - #Make sure we _never_ show dead tickets - #TODO we should be doing this in the where clause. - #but you can't do multiple clauses on the same field just yet :/ - - if ($Ticket->Status eq 'dead') { - return($self->Next()); - } - elsif ($Ticket->CurrentUserHasRight('ShowTicket')) { - return($Ticket); - } - - #If the user doesn't have the right to show this ticket - else { - return($self->Next()); - } - } - #if there never was any ticket - else { - return(undef); - } - -} -# }}} - -# }}} - -# {{{ Deal with storing and restoring restrictions - -# {{{ sub LoadRestrictions - -=head2 LoadRestrictions - -LoadRestrictions takes a string which can fully populate the TicketRestrictons hash. -TODO It is not yet implemented - -=cut - -# }}} - -# {{{ sub DescribeRestrictions - -=head2 DescribeRestrictions - -takes nothing. -Returns a hash keyed by restriction id. -Each element of the hash is currently a one element hash that contains DESCRIPTION which -is a description of the purpose of that TicketRestriction - -=cut - -sub DescribeRestrictions { - my $self = shift; - - my ($row, %listing); - - foreach $row (keys %{$self->{'TicketRestrictions'}}) { - $listing{$row} = $self->{'TicketRestrictions'}{$row}{'DESCRIPTION'}; - } - return (%listing); -} -# }}} - -# {{{ sub RestrictionValues - -=head2 RestrictionValues FIELD - -Takes a restriction field and returns a list of values this field is restricted -to. - -=cut - -sub RestrictionValues { - my $self = shift; - my $field = shift; - map $self->{'TicketRestrictions'}{$_}{'VALUE'}, - grep { - $self->{'TicketRestrictions'}{$_}{'FIELD'} eq $field - && $self->{'TicketRestrictions'}{$_}{'OPERATOR'} eq "=" - } - keys %{$self->{'TicketRestrictions'}}; -} - -# }}} - -# {{{ sub ClearRestrictions - -=head2 ClearRestrictions - -Removes all restrictions irretrievably - -=cut - -sub ClearRestrictions { - my $self = shift; - delete $self->{'TicketRestrictions'}; - $self->{'looking_at_effective_id'} = 0; - $self->{'RecalcTicketLimits'} =1; -} - -# }}} - -# {{{ sub DeleteRestriction - -=head2 DeleteRestriction - -Takes the row Id of a restriction (From DescribeRestrictions' output, for example. -Removes that restriction from the session's limits. - -=cut - - -sub DeleteRestriction { - my $self = shift; - my $row = shift; - delete $self->{'TicketRestrictions'}{$row}; - - $self->{'RecalcTicketLimits'} = 1; - #make the underlying easysearch object forget all its preconceptions -} - -# }}} - -# {{{ sub _ProcessRestrictions - -sub _ProcessRestrictions { - my $self = shift; - - #Need to clean the EasySearch slate because it makes things too sticky - $self->CleanSlate(); - - #Blow away ticket aliases since we'll need to regenerate them for a new search - delete $self->{'TicketAliases'}; - delete $self->{KeywordsAliases}; - - my $row; - - foreach $row (keys %{$self->{'TicketRestrictions'}}) { - my $restriction = $self->{'TicketRestrictions'}{$row}; - # {{{ if it's an int - - if ($TYPES{$restriction->{'FIELD'}} eq 'INT' ) { - if ($restriction->{'OPERATOR'} =~ /^(=|!=|>|<|>=|<=)$/) { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => $restriction->{'OPERATOR'}, - VALUE => $restriction->{'VALUE'}, - ); - } - } - # }}} - # {{{ if it's an enum - elsif ($TYPES{$restriction->{'FIELD'}} eq 'ENUM') { - - if ($restriction->{'OPERATOR'} eq '=') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'OR', - OPERATOR => '=', - VALUE => $restriction->{'VALUE'}, - ); - } - elsif ($restriction->{'OPERATOR'} eq '!=') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => '!=', - VALUE => $restriction->{'VALUE'}, - ); - } - - } - # }}} - # {{{ if it's a date - - elsif ($TYPES{$restriction->{'FIELD'}} eq 'DATE') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => $restriction->{'OPERATOR'}, - VALUE => $restriction->{'VALUE'}, - ); - } - # }}} - # {{{ if it's a string - - elsif ($TYPES{$restriction->{'FIELD'}} eq 'STRING') { - - if ($restriction->{'OPERATOR'} eq '=') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'OR', - OPERATOR => '=', - VALUE => $restriction->{'VALUE'}, - CASESENSITIVE => 0 - ); - } - elsif ($restriction->{'OPERATOR'} eq '!=') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => '!=', - VALUE => $restriction->{'VALUE'}, - CASESENSITIVE => 0 - ); - } - elsif ($restriction->{'OPERATOR'} eq 'LIKE') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => 'LIKE', - VALUE => $restriction->{'VALUE'}, - CASESENSITIVE => 0 - ); - } - elsif ($restriction->{'OPERATOR'} eq 'NOT LIKE') { - $self->SUPER::Limit( FIELD => $restriction->{'FIELD'}, - ENTRYAGGREGATOR => 'AND', - OPERATOR => 'NOT LIKE', - VALUE => $restriction->{'VALUE'}, - CASESENSITIVE => 0 - ); - } - } - - # }}} - # {{{ if it's Transaction content that we're hunting for - elsif ($TYPES{$restriction->{'FIELD'}} eq 'TRANSFIELD') { - - #Basically, we want to make sure that the limits apply to the same attachment, - #rather than just another attachment for the same ticket, no matter how many - #clauses we lump on. - #We put them in TicketAliases so that they get nuked when we redo the join. - - unless (defined $self->{'TicketAliases'}{'TransFieldAlias'}) { - $self->{'TicketAliases'}{'TransFieldAlias'} = $self->NewAlias ('Transactions'); - } - unless (defined $self->{'TicketAliases'}{'TransFieldAttachAlias'}){ - $self->{'TicketAliases'}{'TransFieldAttachAlias'} = $self->NewAlias('Attachments'); - - } - #Join transactions to attachments - $self->Join( ALIAS1 => $self->{'TicketAliases'}{'TransFieldAttachAlias'}, - FIELD1 => 'TransactionId', - ALIAS2 => $self->{'TicketAliases'}{'TransFieldAlias'}, FIELD2=> 'id'); - - #Join transactions to tickets - $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'}, - ALIAS2 =>$self->{'TicketAliases'}{'TransFieldAlias'}, FIELD2 => 'Ticket'); - - #Search for the right field - $self->SUPER::Limit(ALIAS => $self->{'TicketAliases'}{'TransFieldAttachAlias'}, - ENTRYAGGREGATOR => 'AND', - FIELD => $restriction->{'FIELD'}, - OPERATOR => $restriction->{'OPERATOR'} , - VALUE => $restriction->{'VALUE'}, - CASESENSITIVE => 0 - ); - - - } - - # }}} - # {{{ if it's a Transaction date that we're hunting for - elsif ($TYPES{$restriction->{'FIELD'}} eq 'TRANSDATE') { - - #Basically, we want to make sure that the limits apply to the same attachment, - #rather than just another attachment for the same ticket, no matter how many - #clauses we lump on. - #We put them in TicketAliases so that they get nuked when we redo the join. - - unless (defined $self->{'TicketAliases'}{'TransFieldAlias'}) { - $self->{'TicketAliases'}{'TransFieldAlias'} = $self->NewAlias ('Transactions'); - } - - #Join transactions to tickets - $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'}, - ALIAS2 =>$self->{'TicketAliases'}{'TransFieldAlias'}, FIELD2 => 'Ticket'); - - #Search for the right field - $self->SUPER::Limit(ALIAS => $self->{'TicketAliases'}{'TransFieldAlias'}, - ENTRYAGGREGATOR => 'AND', - FIELD => 'Created', - OPERATOR => $restriction->{'OPERATOR'} , - VALUE => $restriction->{'VALUE'} ); - } - - # }}} - # {{{ if it's a relationship that we're hunting for - - # Takes FIELD: which is something like "LinkedTo" - # takes TARGET or BASE which is the TARGET or BASE id that we're searching for - # takes TYPE which is the type of link we're looking for. - - elsif ($TYPES{$restriction->{'FIELD'}} eq 'LINKFIELD') { - - - my $LinkAlias = $self->NewAlias ('Links'); - - - #Make sure we get the right type of link, if we're restricting it - if ($restriction->{'TYPE'}) { - $self->SUPER::Limit(ALIAS => $LinkAlias, - ENTRYAGGREGATOR => 'AND', - FIELD => 'Type', - OPERATOR => '=', - VALUE => $restriction->{'TYPE'} ); - } - - #If we're trying to limit it to things that are target of - if ($restriction->{'TARGET'}) { - - - # If the TARGET is an integer that means that we want to look at the LocalTarget - # field. otherwise, we want to look at the "Target" field - - my ($matchfield); - if ($restriction->{'TARGET'} =~/^(\d+)$/) { - $matchfield = "LocalTarget"; - } - else { - $matchfield = "Target"; - } - - $self->SUPER::Limit(ALIAS => $LinkAlias, - ENTRYAGGREGATOR => 'AND', - FIELD => $matchfield, - OPERATOR => '=', - VALUE => $restriction->{'TARGET'} ); - - - #If we're searching on target, join the base to ticket.id - $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'}, - ALIAS2 => $LinkAlias, - FIELD2 => 'LocalBase'); - - - - - } - #If we're trying to limit it to things that are base of - elsif ($restriction->{'BASE'}) { - - - # If we're trying to match a numeric link, we want to look at LocalBase, - # otherwise we want to look at "Base" - - my ($matchfield); - if ($restriction->{'BASE'} =~/^(\d+)$/) { - $matchfield = "LocalBase"; - } - else { - $matchfield = "Base"; - } - - - $self->SUPER::Limit(ALIAS => $LinkAlias, - ENTRYAGGREGATOR => 'AND', - FIELD => $matchfield, - OPERATOR => '=', - VALUE => $restriction->{'BASE'} ); - - #If we're searching on base, join the target to ticket.id - $self->Join( ALIAS1 => 'main', FIELD1 => $self->{'primary_key'}, - ALIAS2 => $LinkAlias, - FIELD2 => 'LocalTarget'); - - } - - } - - # }}} - # {{{ if it's a watcher that we're hunting for - elsif ($TYPES{$restriction->{'FIELD'}} eq 'WATCHERFIELD') { - - my $Watch = $self->NewAlias('Watchers'); - - #Join watchers to users - my $User = $self->Join( TYPE => 'left', - ALIAS1 => $Watch, - FIELD1 => 'Owner', - TABLE2 => 'Users', - FIELD2 => 'id', - ); - - #Join Ticket to watchers - $self->Join( ALIAS1 => 'main', FIELD1 => 'id', - ALIAS2 => $Watch, FIELD2 => 'Value'); - - - #Make sure we're only talking about ticket watchers - $self->SUPER::Limit( ALIAS => $Watch, - FIELD => 'Scope', - VALUE => 'Ticket', - OPERATOR => '='); - - - # Find email address watchers - $self->SUPER::Limit( SUBCLAUSE => 'WatcherEmailAddress', - ALIAS => $Watch, - FIELD => 'Email', - ENTRYAGGREGATOR => 'OR', - VALUE => $restriction->{'VALUE'}, - OPERATOR => $restriction->{'OPERATOR'}, - CASESENSITIVE => 0 - ); - - - - #Find user watchers - $self->SUPER::Limit( - SUBCLAUSE => 'WatcherEmailAddress', - ALIAS => $User, - FIELD => 'EmailAddress', - ENTRYAGGREGATOR => 'OR', - VALUE => $restriction->{'VALUE'}, - OPERATOR => $restriction->{'OPERATOR'}, - CASESENSITIVE => 0 - ); - - - #If we only want a specific type of watchers, then limit it to that - if ($restriction->{'TYPE'}) { - $self->SUPER::Limit( ALIAS => $Watch, - FIELD => 'Type', - ENTRYAGGREGATOR => 'OR', - VALUE => $restriction->{'TYPE'}, - OPERATOR => '='); - } - } - - # }}} - # {{{ if it's a keyword - elsif ($TYPES{$restriction->{'FIELD'}} eq 'KEYWORDFIELD') { - - my $null_columns_ok; - - my $ObjKeywordsAlias; - $ObjKeywordsAlias = $self->{KeywordsAliases}{$restriction->{'KEYWORDSELECT'}} - if $restriction->{SingleValued}; - unless (defined $ObjKeywordsAlias) { - $ObjKeywordsAlias = $self->Join( - TYPE => 'left', - ALIAS1 => 'main', - FIELD1 => 'id', - TABLE2 => 'ObjectKeywords', - FIELD2 => 'ObjectId' - ); - if ($restriction->{'SingleValued'}) { - $self->{KeywordsAliases}{$restriction->{'KEYWORDSELECT'}} - = $ObjKeywordsAlias; - } - } - - - $self->SUPER::Limit( - ALIAS => $ObjKeywordsAlias, - FIELD => 'Keyword', - OPERATOR => $restriction->{'OPERATOR'}, - VALUE => $restriction->{'KEYWORD'}, - QUOTEVALUE => $restriction->{'QUOTEVALUE'}, - ENTRYAGGREGATOR => 'OR', - ); - - 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 => $ObjKeywordsAlias, - FIELD => 'Keyword', - OPERATOR => 'IS', - VALUE => 'NULL', - QUOTEVALUE => 0, - ENTRYAGGREGATOR => 'OR', - ); - } - - - $self->SUPER::Limit(LEFTJOIN => $ObjKeywordsAlias, - FIELD => 'KeywordSelect', - VALUE => $restriction->{'KEYWORDSELECT'}, - ENTRYAGGREGATOR => 'OR'); - - - - $self->SUPER::Limit( ALIAS => $ObjKeywordsAlias, - FIELD => 'ObjectType', - VALUE => 'Ticket', - ENTRYAGGREGATOR => 'AND'); - - if ($null_columns_ok) { - $self->SUPER::Limit(ALIAS => $ObjKeywordsAlias, - FIELD => 'ObjectType', - OPERATOR => 'IS', - VALUE => 'NULL', - QUOTEVALUE => 0, - ENTRYAGGREGATOR => 'OR'); - } - - } - # }}} - - - } - - - # here, we make sure we don't get any tickets that have been merged into other tickets - # (Ticket Id == Ticket EffectiveId - # note that we _really_ don't want to do this if we're already looking at the effectiveid - if ($self->_isLimited && (! $self->{'looking_at_effective_id'})) { - $self->SUPER::Limit( FIELD => 'EffectiveId', - OPERATOR => '=', - QUOTEVALUE => 0, - VALUE => 'main.id'); #TODO, we shouldn't be hard coding the tablename to main. - } - $self->{'RecalcTicketLimits'} = 0; -} - -# }}} - -# }}} - -# {{{ Deal with displaying rows of the listing - -# -# Everything in this section is stub code for 2.2 -# It's not part of the API. It's not for your use -# It's not for our use. -# - - -# {{{ sub SetListingFormat - -=head2 SetListingFormat - -Takes a single Format string as specified below. parses that format string and makes the various listing output -things DTRT. - -=item Format strings - -Format strings are made up of a chain of Elements delimited with vertical pipes (|). -Elements of a Format string - - -FormatString: Element[::FormatString] - -Element: AttributeName[;HREF=][;TITLE=] - -AttributeName Id | Subject | Status | Owner | Priority | InitialPriority | TimeWorked | TimeLeft | - - Keywords[;SELECT=<KeywordSelect>] | - - <Created|Starts|Started|Contacted|Due|Resolved>Date<AsString|AsISO|AsAge> - - -=cut - - - - -#accept a format string - - - -sub SetListingFormat { - my $self = shift; - my $listing_format = shift; - - my ($element, $attribs); - my $i = 0; - foreach $element (split (/::/,$listing_format)) { - if ($element =~ /^(.*?);(.*)$/) { - $element = $1; - $attribs = $2; - } - $self->{'format_string'}->[$i]->{'Element'} = $element; - foreach $attrib (split (/;/, $attribs)) { - my $value = ""; - if ($attrib =~ /^(.*?)=(.*)$/) { - $attrib = $1; - $value = $2; - } - $self->{'format_string'}->[$i]->{"$attrib"} = $val; - - } - - } - return(1); -} - -# }}} - -# {{{ sub HeaderAsHTML -sub HeaderAsHTML { - my $self = shift; - my $header = ""; - my $col; - foreach $col ( @{[ $self->{'format_string'} ]}) { - $header .= "<TH>" . $self->_ColumnTitle($self->{'format_string'}->[$col]) . "</TH>"; - - } - return ($header); -} -# }}} - -# {{{ sub HeaderAsText -#Print text header -sub HeaderAsText { - my $self = shift; - my ($header); - - return ($header); -} -# }}} - -# {{{ sub TicketAsHTMLRow -#Print HTML row -sub TicketAsHTMLRow { - my $self = shift; - my $Ticket = shift; - my ($row, $col); - foreach $col (@{[$self->{'format_string'}]}) { - $row .= "<TD>" . $self->_TicketColumnValue($ticket,$self->{'format_string'}->[$col]) . "</TD>"; - - } - return ($row); -} -# }}} - -# {{{ sub TicketAsTextRow -#Print text row -sub TicketAsTextRow { - my $self = shift; - my ($row); - - #TODO implement - - return ($row); -} -# }}} - -# {{{ _ColumnTitle { - -sub _ColumnTitle { - my $self = shift; - - # Attrib is a hash - my $attrib = shift; - - # return either attrib->{'TITLE'} or.. - if ($attrib->{'TITLE'}) { - return($attrib->{'TITLE'}); - } - # failing that, Look up the title in a hash - else { - #TODO create $self->{'ColumnTitles'}; - return ($self->{'ColumnTitles'}->{$attrib->{'Element'}}); - } - -} - -# }}} - -# {{{ _TicketColumnValue -sub _TicketColumnValue { - my $self = shift; - my $Ticket = shift; - my $attrib = shift; - - - my $out; - - SWITCH: { - /^id/i && do { - $out = $Ticket->id; - last SWITCH; - }; - /^subj/i && do { - last SWITCH; - $Ticket->Subject; - }; - /^status/i && do { - last SWITCH; - $Ticket->Status; - }; - /^prio/i && do { - last SWITCH; - $Ticket->Priority; - }; - /^finalprio/i && do { - - last SWITCH; - $Ticket->FinalPriority - }; - /^initialprio/i && do { - - last SWITCH; - $Ticket->InitialPriority; - }; - /^timel/i && do { - - last SWITCH; - $Ticket->TimeWorked; - }; - /^timew/i && do { - - last SWITCH; - $Ticket->TimeLeft; - }; - - /^(.*?)date(.*)$/i && do { - my $o = $1; - my $m = $2; - my ($obj); - #TODO: optimize - $obj = $Ticket->DueObj if $o =~ /due/i; - $obj = $Ticket->CreatedObj if $o =~ /created/i; - $obj = $Ticket->StartsObj if $o =~ /starts/i; - $obj = $Ticket->StartedObj if $o =~ /started/i; - $obj = $Ticket->ToldObj if $o =~ /told/i; - $obj = $Ticket->LastUpdatedObj if $o =~ /lastu/i; - - $method = 'ISO' if $m =~ /iso/i; - - $method = 'AsString' if $m =~ /asstring/i; - $method = 'AgeAsString' if $m =~ /age/i; - last SWITCH; - $obj->$method(); - - }; - - /^watcher/i && do { - last SWITCH; - $Ticket->WatchersAsString(); - }; - - /^requestor/i && do { - last SWITCH; - $Ticket->RequestorsAsString(); - }; - /^cc/i && do { - last SWITCH; - $Ticket->CCAsString(); - }; - - - /^admincc/i && do { - last SWITCH; - $Ticket->AdminCcAsString(); - }; - - /^keywords/i && do { - last SWITCH; - #Limit it to the keyword select we're talking about, if we've got one. - my $objkeys =$Ticket->KeywordsObj($attrib->{'SELECT'}); - $objkeys->KeywordRelativePathsAsString(); - }; - - } - -} - -# }}} - -# }}} - -# {{{ POD -=head2 notes -"Enum" Things that get Is, IsNot - - -"Int" Things that get Is LessThan and GreaterThan -id -InitialPriority -FinalPriority -Priority -TimeLeft -TimeWorked - -"Text" Things that get Is, Like -Subject -TransactionContent - - -"Link" OPERATORs - - -"Date" OPERATORs Is, Before, After - =cut -# }}} 1;