summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Tickets.pm
diff options
context:
space:
mode:
authorivan <ivan>2002-08-12 06:17:09 +0000
committerivan <ivan>2002-08-12 06:17:09 +0000
commit3ef62a0570055da710328937e7f65dbb2c027c62 (patch)
treed549158b172fd499b4f81a2981b62aabbde4f99b /rt/lib/RT/Tickets.pm
parent030438c9cb1c12ccb79130979ef0922097b4311a (diff)
import rt 2.0.14
Diffstat (limited to 'rt/lib/RT/Tickets.pm')
-rwxr-xr-xrt/lib/RT/Tickets.pm1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
new file mode 100755
index 000000000..dd91126c4
--- /dev/null
+++ b/rt/lib/RT/Tickets.pm
@@ -0,0 +1,1789 @@
+#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Tickets.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+
+=head1 NAME
+
+ RT::Tickets - A collection of Ticket objects
+
+
+=head1 SYNOPSIS
+
+ use RT::Tickets;
+ my $tickets = new RT::Tickets($CurrentUser);
+
+=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
+
+
+=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 {
+ 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
+
+=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
+
+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', @_);
+}
+
+# }}}
+
+# }}}
+
+# }}}
+
+# {{{ Limiting based on links
+
+# {{{ LimitLinkedTo
+
+=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'})
+ );
+}
+
+
+# }}}
+
+# {{{ LimitLinkedFrom
+
+=head2 LimitLinkedFrom
+
+LimitLinkedFrom takes a paramhash with two fields: TYPE and BASE
+TYPE limits the sort of relationship we want to search on
+
+
+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
+
+
+=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=<URL>][;TITLE=<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;