summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Ticket.pm
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Ticket.pm')
-rwxr-xr-xrt/lib/RT/Ticket.pm3026
1 files changed, 342 insertions, 2684 deletions
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
index f7275e4..2f075a2 100755
--- a/rt/lib/RT/Ticket.pm
+++ b/rt/lib/RT/Ticket.pm
@@ -1,3004 +1,662 @@
-# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Ticket.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
-# (c) 1996-2001 Jesse Vincent <jesse@fsck.com>
-# This software is redistributable under the terms of the GNU GPL
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (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 <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
#
-=head1 NAME
+use strict;
- RT::Ticket - RT ticket object
-=head1 SYNOPSIS
+=head1 NAME
- use RT::Ticket;
- my $ticket = new RT::Ticket($CurrentUser);
- $ticket->Load($ticket_id);
+RT::Ticket
-=head1 DESCRIPTION
-This module lets you manipulate RT\'s ticket object.
+=head1 SYNOPSIS
+=head1 DESCRIPTION
=head1 METHODS
=cut
-
-
package RT::Ticket;
+use RT::Record;
use RT::Queue;
-use RT::User;
-use RT::Record;
-use RT::Link;
-use RT::Links;
-use RT::Date;
-use RT::Watcher;
-
-
-@ISA= qw(RT::Record);
-
-=begin testing
-use RT::TestHarness;
-
-ok(require RT::Ticket, "Loading the RT::Ticket library");
-
-=end testing
-
-=cut
-
-# {{{ sub _Init
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
sub _Init {
- my $self = shift;
- $self->{'table'} = "Tickets";
- return ($self->SUPER::_Init(@_));
-}
-
-# }}}
-
-# {{{ sub Load
-
-=head2 Load
-
-Takes a single argument. This can be a ticket id, ticket alias or
-local ticket uri. If the ticket can't be loaded, returns undef.
-Otherwise, returns the ticket id.
-
-=cut
-
-sub Load {
- my $self = shift;
- my $id = shift;
-
- #TODO modify this routine to look at EffectiveId and do the recursive load
- # thing. be careful to cache all the interim tickets we try so we don't loop forever.
-
- #If it's a local URI, turn it into a ticket id
- if ($id =~ /^$RT::TicketBaseURI(\d+)$/) {
- $id = $1;
- }
- #If it's a remote URI, we're going to punt for now
- elsif ($id =~ '://' ) {
- return (undef);
- }
-
- #If we have an integer URI, load the ticket
- if ( $id =~ /^\d+$/ ) {
- my $ticketid = $self->LoadById($id);
-
- unless ($ticketid) {
- $RT::Logger->debug("$self tried to load a bogus ticket: $id\n");
- return(undef);
- }
- }
-
- #It's not a URI. It's not a numerical ticket ID. Punt!
- else {
- return(undef);
- }
-
- #If we're merged, resolve the merge.
- if (($self->EffectiveId) and
- ($self->EffectiveId != $self->Id)) {
- return ($self->Load($self->EffectiveId));
- }
-
- #Ok. we're loaded. lets get outa here.
- return ($self->Id);
-
-}
-
-# }}}
+ my $self = shift;
-# {{{ sub LoadByURI
-
-=head2 LoadByURI
-
-Given a local ticket URI, loads the specified ticket.
-
-=cut
-
-sub LoadByURI {
- my $self = shift;
- my $uri = shift;
-
- if ($uri =~ /^$RT::TicketBaseURI(\d+)$/) {
- my $id = $1;
- return ($self->Load($id));
- }
- else {
- return(undef);
- }
+ $self->Table('Tickets');
+ $self->SUPER::_Init(@_);
}
-# }}}
-
-# {{{ sub Create
-
-=head2 Create (ARGS)
-
-Arguments: ARGS is a hash of named parameters. Valid parameters are:
-
- Queue - Either a Queue object or a Queue Name
- Requestor - A reference to a list of RT::User objects, email addresses or RT user Names
- Cc - A reference to a list of RT::User objects, email addresses or Names
- AdminCc - A reference to a list of RT::User objects, email addresses or Names
- Type -- The ticket\'s type. ignore this for now
- Owner -- This ticket\'s owner. either an RT::User object or this user\'s id
- Subject -- A string describing the subject of the ticket
- InitialPriority -- an integer from 0 to 99
- FinalPriority -- an integer from 0 to 99
- Status -- any valid status (Defined in RT::Queue)
- TimeWorked -- an integer
- TimeLeft -- an integer
- Starts -- an ISO date describing the ticket\'s start date and time in GMT
- Due -- an ISO date describing the ticket\'s due date and time in GMT
- MIMEObj -- a MIME::Entity object with the content of the initial ticket request.
-
- KeywordSelect-<id> -- an array of keyword ids for that keyword select
-
-
-Returns: TICKETID, Transaction Object, Error Message
-
-
-=begin testing
-
-my $t = RT::Ticket->new($RT::SystemUser);
-
-ok( $t->Create(Queue => 'General', Subject => 'This is a subject'), "Ticket Created");
-
-ok ( my $id = $t->Id, "Got ticket id");
-
-=end testing
-
-=cut
-
-sub Create {
- my $self = shift;
-
- my %args = (
- Queue => undef,
- Requestor => undef,
- Cc => undef,
- AdminCc => undef,
- Type => 'ticket',
- Owner => $RT::Nobody->UserObj,
- Subject => '[no subject]',
- InitialPriority => undef,
- FinalPriority => undef,
- Status => 'new',
- TimeWorked => "0",
- TimeLeft => 0,
- Due => undef,
- Starts => undef,
- MIMEObj => undef,
- @_);
-
- my ($ErrStr, $QueueObj, $Owner, $resolved);
- my (@non_fatal_errors);
-
- my $now = RT::Date->new($self->CurrentUser);
- $now->SetToNow();
-
- if ( (defined($args{'Queue'})) && (!ref($args{'Queue'})) ) {
- $QueueObj=RT::Queue->new($RT::SystemUser);
- $QueueObj->Load($args{'Queue'});
- }
- elsif (ref($args{'Queue'}) eq 'RT::Queue') {
- $QueueObj=RT::Queue->new($RT::SystemUser);
- $QueueObj->Load($args{'Queue'}->Id);
- }
- else {
- $RT::Logger->debug("$self ". $args{'Queue'} .
- " not a recognised queue object.");
- }
-
- #Can't create a ticket without a queue.
- unless (defined ($QueueObj)) {
- $RT::Logger->debug( "$self No queue given for ticket creation.");
- return (0, 0,'Could not create ticket. Queue not set');
- }
-
- #Now that we have a queue, Check the ACLS
- unless ($self->CurrentUser->HasQueueRight(Right => 'CreateTicket',
- QueueObj => $QueueObj )) {
- return (0,0,"No permission to create tickets in the queue '".
- $QueueObj->Name."'.");
- }
-
- #Since we have a queue, we can set queue defaults
- #Initial Priority
-
- # If there's no queue default initial priority and it's not set, set it to 0
- $args{'InitialPriority'} = ($QueueObj->InitialPriority || 0)
- unless (defined $args{'InitialPriority'});
-
- #Final priority
-
- # If there's no queue default final priority and it's not set, set it to 0
- $args{'FinalPriority'} = ($QueueObj->FinalPriority || 0)
- unless (defined $args{'FinalPriority'});
-
-
- #TODO we should see what sort of due date we're getting, rather +
- # than assuming it's in ISO format.
-
- #Set the due date. if we didn't get fed one, use the queue default due in
- my $due = new RT::Date($self->CurrentUser);
- if (defined $args{'Due'}) {
- $due->Set (Format => 'ISO',
- Value => $args{'Due'});
- }
- elsif (defined ($QueueObj->DefaultDueIn)) {
- $due->SetToNow;
- $due->AddDays($QueueObj->DefaultDueIn);
- }
-
- my $starts = new RT::Date($self->CurrentUser);
- if (defined $args{'Starts'}) {
- $starts->Set (Format => 'ISO',
- Value => $args{'Starts'});
- }
-
-
- # {{{ Deal with setting the owner
-
- if (ref($args{'Owner'}) eq 'RT::User') {
- $Owner = $args{'Owner'};
- }
- #If we've been handed something else, try to load the user.
- elsif ($args{'Owner'}) {
- $Owner = new RT::User($self->CurrentUser);
- $Owner->Load($args{'Owner'});
-
- }
- #If we can't handle it, call it nobody
- else {
- if (ref($args{'Owner'})) {
- $RT::Logger->warning("$ticket ->Create called with an Owner of ".
- "type ".ref($args{'Owner'}) .". Defaulting to nobody.\n");
-
- push @non_fatal_errors, "Invalid owner. Defaulting to 'nobody'.";
- }
- else {
- $RT::Logger->warning("$self ->Create called with an ".
- "unknown datatype for Owner: ".$args{'Owner'} .
- ". Defaulting to Nobody.\n");
- }
- }
-
- #If we have a proposed owner and they don't have the right
- #to own a ticket, scream about it and make them not the owner
- if ((defined ($Owner)) and
- ($Owner->Id != $RT::Nobody->Id) and
- (!$Owner->HasQueueRight( QueueObj => $QueueObj,
- Right => 'OwnTicket'))) {
-
- $RT::Logger->warning("$self user ".$Owner->Name . "(".$Owner->id .
- ") was proposed ".
- "as a ticket owner but has no rights to own ".
- "tickets in this queue\n");
-
- push @non_fatal_errors, "Invalid owner. Defaulting to 'nobody'.";
-
- $Owner = undef;
- }
-
- #If we haven't been handed a valid owner, make it nobody.
- unless (defined ($Owner)) {
- $Owner = new RT::User($self->CurrentUser);
- $Owner->Load($RT::Nobody->UserObj->Id);
- }
-
- # }}}
-
- unless ($self->ValidateStatus($args{'Status'})) {
- return (0,0,'Invalid value for status');
- }
-
- if ($args{'Status'} eq 'resolved') {
- $resolved = $now->ISO;
- } else{
- $resolved = undef;
- }
-
- my $id = $self->SUPER::Create(
- Queue => $QueueObj->Id,
- Owner => $Owner->Id,
- Subject => $args{'Subject'},
- InitialPriority => $args{'InitialPriority'},
- FinalPriority => $args{'FinalPriority'},
- Priority => $args{'InitialPriority'},
- Status => $args{'Status'},
- TimeWorked => $args{'TimeWorked'},
- TimeLeft => $args{'TimeLeft'},
- Type => $args{'Type'},
- Starts => $starts->ISO,
- Resolved => $resolved,
- Due => $due->ISO
- );
- #Set the ticket's effective ID now that we've created it.
- my ($val, $msg) = $self->__Set(Field => 'EffectiveId', Value => $id);
-
- unless ($val) {
- $RT::Logger->err("$self ->Create couldn't set EffectiveId: $msg\n");
- }
-
-
- my $watcher;
- foreach $watcher (@{$args{'Cc'}}) {
- my ($wval, $wmsg) =
- $self->_AddWatcher( Type => 'Cc', Person => $watcher, Silent => 1);
- push @non_fatal_errors, $wmsg unless ($wval);
- }
-
- foreach $watcher (@{$args{'Requestor'}}) {
- my ($wval, $wmsg) =
- $self->_AddWatcher( Type => 'Requestor', Person => $watcher, Silent => 1);
- push @non_fatal_errors, $wmsg unless ($wval);
- }
-
- foreach $watcher (@{$args{'AdminCc'}}) {
- # Note that we're using AddWatcher, rather than _AddWatcher, as we
- # actually _want_ that ACL check. Otherwise, random ticket creators
- # could make themselves adminccs and maybe get ticket rights. that would
- # be poor
- my ($wval, $wmsg) =
- $self->AddWatcher( Type => 'AdminCc', Person => $watcher, Silent => 1);
- push @non_fatal_errors, $wmsg unless ($wval);
- }
-
- # Iterate through all the KeywordSelect-<int> params passed in, calling _AddKeyword
- # for each of them
-
-
- foreach my $key (keys %args) {
-
- next unless ($key =~ /^KeywordSelect-(.*)$/);
-
- my $ks = $1;
-
-
- my @keywords = ref($args{$key}) eq 'ARRAY' ?
- @{$args{$key}} : ($args{$key});
-
- foreach my $keyword (@keywords) {
- my ($kval, $kmsg) = $self->_AddKeyword(KeywordSelect => $ks,
- Keyword => $keyword,
- Silent => 1);
- }
- push @non_fatal_errors, $kmsg unless ($kval);
- }
-
-
-
- #Add a transaction for the create
- my ($Trans, $Msg, $TransObj) =
- $self->_NewTransaction( Type => "Create",
- TimeTaken => 0,
- MIMEObj=>$args{'MIMEObj'});
-
- # Logging
- if ($self->Id && $Trans) {
- $ErrStr = "Ticket ".$self->Id . " created in queue '". $QueueObj->Name.
- "'.\n" . join("\n", @non_fatal_errors);
-
- $RT::Logger->info($ErrStr);
- }
- else {
- # TODO where does this get errstr from?
- $RT::Logger->warning("Ticket couldn't be created: $ErrStr");
- }
-
- return($self->Id, $TransObj->Id, $ErrStr);
-}
-# }}}
-# {{{ sub Import
-=head2 Import PARAMHASH
-Import a ticket.
-Doesn\'t create a transaction.
-Doesn\'t supply queue defaults, etc.
+=item Create PARAMHASH
-Arguments are identical to Create(), with the addition of
- Id - Ticket Id
+Create takes a hash of values and creates a row in the database:
-Returns: TICKETID
+ int(11) 'EffectiveId'.
+ int(11) 'Queue'.
+ varchar(16) 'Type'.
+ int(11) 'IssueStatement'.
+ int(11) 'Resolution'.
+ int(11) 'Owner'.
+ varchar(200) 'Subject' defaults to '[no subject]'.
+ int(11) 'InitialPriority'.
+ int(11) 'FinalPriority'.
+ int(11) 'Priority'.
+ int(11) 'TimeEstimated'.
+ int(11) 'TimeWorked'.
+ varchar(10) 'Status'.
+ int(11) 'TimeLeft'.
+ datetime 'Told'.
+ datetime 'Starts'.
+ datetime 'Started'.
+ datetime 'Due'.
+ datetime 'Resolved'.
+ smallint(6) 'Disabled'.
=cut
-sub Import {
- my $self = shift;
- my ( $ErrStr, $QueueObj, $Owner);
-
- my %args = (id => undef,
- EffectiveId => undef,
- Queue => undef,
- Requestor => undef,
- Type => 'ticket',
- Owner => $RT::Nobody->Id,
- Subject => '[no subject]',
- InitialPriority => undef,
- FinalPriority => undef,
- Status => 'new',
- TimeWorked => "0",
- Due => undef,
- Created => undef,
- Updated => undef,
- Resolved => undef,
- Told => undef,
- @_);
-
- if ( (defined($args{'Queue'})) && (!ref($args{'Queue'})) ) {
- $QueueObj=RT::Queue->new($RT::SystemUser);
- $QueueObj->Load($args{'Queue'});
- #TODO error check this and return 0 if it\'s not loading properly +++
- }
- elsif (ref($args{'Queue'}) eq 'RT::Queue') {
- $QueueObj=RT::Queue->new($RT::SystemUser);
- $QueueObj->Load($args{'Queue'}->Id);
- }
- else {
- $RT::Logger->debug("$self ". $args{'Queue'} .
- " not a recognised queue object.");
- }
-
- #Can't create a ticket without a queue.
- unless (defined ($QueueObj) and $QueueObj->Id) {
- $RT::Logger->debug( "$self No queue given for ticket creation.");
- return (0,'Could not create ticket. Queue not set');
- }
-
- #Now that we have a queue, Check the ACLS
- unless ($self->CurrentUser->HasQueueRight(Right => 'CreateTicket',
- QueueObj => $QueueObj )) {
- return (0,"No permission to create tickets in the queue '".
- $QueueObj->Name."'.");
- }
-
-
-
-
- # {{{ Deal with setting the owner
-
- # Attempt to take user object, user name or user id.
- # Assign to nobody if lookup fails.
- if (defined ($args{'Owner'})) {
- if ( ref($args{'Owner'}) ) {
- $Owner = $args{'Owner'};
- }
- else {
- $Owner = new RT::User($self->CurrentUser);
- $Owner->Load($args{'Owner'});
- if ( ! defined($Owner->id) ) {
- $Owner->Load($RT::Nobody->id);
- }
- }
- }
-
-
- #If we have a proposed owner and they don't have the right
- #to own a ticket, scream about it and make them not the owner
- if ((defined ($Owner)) and
- ($Owner->Id != $RT::Nobody->Id) and
- (!$Owner->HasQueueRight( QueueObj => $QueueObj,
- Right => 'OwnTicket'))) {
-
- $RT::Logger->warning("$self user ".$Owner->Name . "(".$Owner->id .
- ") was proposed ".
- "as a ticket owner but has no rights to own ".
- "tickets in '".$QueueObj->Name."'\n");
-
- $Owner = undef;
- }
-
- #If we haven't been handed a valid owner, make it nobody.
- unless (defined ($Owner)) {
- $Owner = new RT::User($self->CurrentUser);
- $Owner->Load($RT::Nobody->UserObj->Id);
- }
-
- # }}}
-
- unless ($self->ValidateStatus($args{'Status'})) {
- return (0,"'$args{'Status'}' is an invalid value for status");
- }
-
- $self->{'_AccessibleCache'}{Created} = { 'read'=>1, 'write'=>1 };
- $self->{'_AccessibleCache'}{Creator} = { 'read'=>1, 'auto'=>1 };
- $self->{'_AccessibleCache'}{LastUpdated} = { 'read'=>1, 'write'=>1 };
- $self->{'_AccessibleCache'}{LastUpdatedBy} = { 'read'=>1, 'auto'=>1 };
-
-
- # If we're coming in with an id, set that now.
- my $EffectiveId = undef;
- if ($args{'id'}) {
- $EffectiveId = $args{'id'};
-
- }
-
-
- my $id = $self->SUPER::Create(
- id => $args{'id'},
- EffectiveId => $EffectiveId,
- Queue => $QueueObj->Id,
- Owner => $Owner->Id,
- Subject => $args{'Subject'},
- InitialPriority => $args{'InitialPriority'},
- FinalPriority => $args{'FinalPriority'},
- Priority => $args{'InitialPriority'},
- Status => $args{'Status'},
- TimeWorked => $args{'TimeWorked'},
- Type => $args{'Type'},
- Created => $args{'Created'},
- Told => $args{'Told'},
- LastUpdated => $args{'Updated'},
- Resolved => $args{Resolved},
- Due => $args{'Due'},
- );
-
-
-
- # If the ticket didn't have an id
- # Set the ticket's effective ID now that we've created it.
- if ($args{'id'} ) {
- $self->Load($args{'id'});
- }
- else {
- my ($val, $msg) = $self->__Set(Field => 'EffectiveId', Value => $id);
-
- unless ($val) {
- $RT::Logger->err($self."->Import couldn't set EffectiveId: $msg\n");
- }
- }
-
- my $watcher;
- foreach $watcher (@{$args{'Cc'}}) {
- $self->_AddWatcher( Type => 'Cc', Person => $watcher, Silent => 1);
- }
- foreach $watcher (@{$args{'AdminCc'}}) {
- $self->_AddWatcher( Type => 'AdminCc', Person => $watcher, Silent => 1);
- }
- foreach $watcher (@{$args{'Requestor'}}) {
- $self->_AddWatcher( Type => 'Requestor', Person => $watcher, Silent => 1);
- }
-
- return($self->Id, $ErrStr);
-}
-
-# }}}
-
-# {{{ sub Delete
-
-sub Delete {
- my $self = shift;
- return (0, 'Deleting this object would violate referential integrity.'.
- ' That\'s bad.');
-}
-# }}}
-
-# {{{ Routines dealing with watchers.
-
-# {{{ Routines dealing with adding new watchers
-
-# {{{ sub AddWatcher
-
-=head2 AddWatcher
-
-AddWatcher takes a parameter hash. The keys are as follows:
-
-Email
-Type
-Owner
-
-If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
-
-=cut
-
-sub AddWatcher {
- my $self = shift;
- my %args = ( Email => undef,
- Type => undef,
- Owner => undef,
- @_
- );
-
- # {{{ Check ACLS
- #If the watcher we're trying to add is for the current user
- if ( ( $self->CurrentUser->EmailAddress &&
- ($args{'Email'} eq $self->CurrentUser->EmailAddress) ) or
- ($args{'Owner'} eq $self->CurrentUser->Id)
- ) {
-
-
- # If it's an AdminCc and they don't have
- # 'WatchAsAdminCc' or 'ModifyTicket', bail
- if ($args{'Type'} eq 'AdminCc') {
- unless ($self->CurrentUserHasRight('ModifyTicket') or
- $self->CurrentUserHasRight('WatchAsAdminCc')) {
- return(0, 'Permission Denied');
- }
- }
-
- # If it's a Requestor or Cc and they don't have
- # 'Watch' or 'ModifyTicket', bail
- elsif (($args{'Type'} eq 'Cc') or
- ($args{'Type'} eq 'Requestor')) {
-
- unless ($self->CurrentUserHasRight('ModifyTicket') or
- $self->CurrentUserHasRight('Watch')) {
- return(0, 'Permission Denied');
- }
- }
- else {
- $RT::Logger->warn("$self -> AddWatcher hit code".
- " it never should. We got passed ".
- " a type of ". $args{'Type'});
- return (0,'Error in parameters to TicketAddWatcher');
- }
- }
- # If the watcher isn't the current user
- # and the current user doesn't have 'ModifyTicket'
- # bail
- else {
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
- }
- # }}}
-
- return ($self->_AddWatcher(%args));
-}
-
-
-#This contains the meat of AddWatcher. but can be called from a routine like
-# Create, which doesn't need the additional acl check
-sub _AddWatcher {
- my $self = shift;
- my %args = (
- Type => undef,
- Silent => undef,
- Email => undef,
- Owner => 0,
- Person => undef,
- @_ );
-
-
-
- #clear the watchers cache
- $self->{'watchers_cache'} = undef;
-
- if (defined $args{'Person'}) {
- #if it's an RT::User object, pull out the id and shove it in Owner
- if (ref ($args{'Person'}) =~ /RT::User/) {
- $args{'Owner'} = $args{'Person'}->id;
- }
- #if it's an int, shove it in Owner
- elsif ($args{'Person'} =~ /^\d+$/) {
- $args{'Owner'} = $args{'Person'};
- }
- #if it's an email address, shove it in Email
- else {
- $args{'Email'} = $args{'Person'};
- }
- }
-
- # Turn an email address int a watcher if we possibly can.
- if ($args{'Email'}) {
- my $watcher = new RT::User($self->CurrentUser);
- $watcher->LoadByEmail($args{'Email'});
- if ($watcher->Id) {
- $args{'Owner'} = $watcher->Id;
- delete $args{'Email'};
- }
- }
-
-
- # see if this user is already a watcher. if we have an owner, check it
- # otherwise, we've got an email-address watcher. use that.
-
- if ($self->IsWatcher(Type => $args{'Type'},
- Id => ($args{'Owner'} || $args{'Email'}) ) ) {
-
-
- return(0, 'That user is already that sort of watcher for this ticket');
- }
-
-
- require RT::Watcher;
- my $Watcher = new RT::Watcher ($self->CurrentUser);
- my ($retval, $msg) = ($Watcher->Create( Value => $self->Id,
- Scope => 'Ticket',
- Email => $args{'Email'},
- Type => $args{'Type'},
- Owner => $args{'Owner'},
- ));
-
- unless ($args{'Silent'}) {
- $self->_NewTransaction( Type => 'AddWatcher',
- NewValue => $Watcher->Email,
- Field => $Watcher->Type);
- }
-
- return ($retval, $msg);
-}
-
-# }}}
-
-# {{{ sub AddRequestor
-
-=head2 AddRequestor
-
-AddRequestor takes what AddWatcher does, except it presets
-the "Type" parameter to \'Requestor\'
-
-=cut
-
-sub AddRequestor {
- my $self = shift;
- return ($self->AddWatcher ( Type => 'Requestor', @_));
-}
-
-# }}}
-
-# {{{ sub AddCc
-
-=head2 AddCc
-
-AddCc takes what AddWatcher does, except it presets
-the "Type" parameter to \'Cc\'
-
-=cut
-
-sub AddCc {
- my $self = shift;
- return ($self->AddWatcher ( Type => 'Cc', @_));
-}
-# }}}
-
-# {{{ sub AddAdminCc
-
-=head2 AddAdminCc
-
-AddAdminCc takes what AddWatcher does, except it presets
-the "Type" parameter to \'AdminCc\'
-
-=cut
-
-sub AddAdminCc {
- my $self = shift;
- return ($self->AddWatcher ( Type => 'AdminCc', @_));
-}
-
-# }}}
-
-# }}}
-
-# {{{ sub DeleteWatcher
-
-=head2 DeleteWatcher id [type]
-
-DeleteWatcher takes a single argument which is either an email address
-or a watcher id.
-If the first argument is an email address, you need to specify the watcher type you're talking
-about as the second argument. Valid values are 'Requestor', 'Cc' or 'AdminCc'.
-It removes that watcher from this Ticket\'s list of watchers.
-
-
-=cut
-#TODO It is lame that you can't call this the same way you can call AddWatcher
-sub DeleteWatcher {
+sub Create {
my $self = shift;
- my $id = shift;
-
- my $type;
-
- $type = shift if (@_);
-
- my $Watcher = new RT::Watcher($self->CurrentUser);
-
- #If it\'s a numeric watcherid
- if ($id =~ /^(\d*)$/) {
- $Watcher->Load($id);
- }
-
- #Otherwise, we'll assume it's an email address
- elsif ($type) {
- my ($result, $msg) =
- $Watcher->LoadByValue( Email => $id,
- Scope => 'Ticket',
- Value => $self->id,
- Type => $type);
- return (0,$msg) unless ($result);
- }
-
- else {
- return(0,"Can\'t delete a watcher by email address without specifying a type");
- }
-
- # {{{ Check ACLS
-
- #If the watcher we're trying to delete is for the current user
- if ($Watcher->Email eq $self->CurrentUser->EmailAddress) {
-
- # If it's an AdminCc and they don't have
- # 'WatchAsAdminCc' or 'ModifyTicket', bail
- if ($Watcher->Type eq 'AdminCc') {
- unless ($self->CurrentUserHasRight('ModifyTicket') or
- $self->CurrentUserHasRight('WatchAsAdminCc')) {
- return(0, 'Permission Denied');
- }
- }
-
- # If it's a Requestor or Cc and they don't have
- # 'Watch' or 'ModifyTicket', bail
- elsif (($Watcher->Type eq 'Cc') or
- ($Watcher->Type eq 'Requestor')) {
-
- unless ($self->CurrentUserHasRight('ModifyTicket') or
- $self->CurrentUserHasRight('Watch')) {
- return(0, 'Permission Denied');
- }
- }
- else {
- $RT::Logger->warn("$self -> DeleteWatcher hit code".
- " it never should. We got passed ".
- " a type of ". $args{'Type'});
- return (0,'Error in parameters to $self DeleteWatcher');
- }
- }
- # If the watcher isn't the current user
- # and the current user doesn't have 'ModifyTicket'
- # bail
- else {
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
- }
-
- # }}}
-
- unless (($Watcher->Scope eq 'Ticket') and
- ($Watcher->Value == $self->id) ) {
- return (0, "Not a watcher for this ticket");
- }
-
-
- #Clear out the watchers hash.
- $self->{'watchers'} = undef;
-
- #If we\'ve validated that it is a watcher for this ticket
- $self->_NewTransaction ( Type => 'DelWatcher',
- OldValue => $Watcher->Email,
- Field => $Watcher->Type,
- );
-
- my $retval = $Watcher->Delete();
-
- unless ($retval) {
- return(0,"Watcher could not be deleted. Database inconsistency possible.");
- }
-
- return(1, "Watcher deleted");
-}
-
-# {{{ sub DeleteRequestor
-
-=head2 DeleteRequestor EMAIL
+ my %args = (
+ EffectiveId => '0',
+ Queue => '0',
+ Type => '',
+ IssueStatement => '0',
+ Resolution => '0',
+ Owner => '0',
+ Subject => '[no subject]',
+ InitialPriority => '0',
+ FinalPriority => '0',
+ Priority => '0',
+ TimeEstimated => '0',
+ TimeWorked => '0',
+ Status => '',
+ TimeLeft => '0',
+ Told => '',
+ Starts => '',
+ Started => '',
+ Due => '',
+ Resolved => '',
+ Disabled => '0',
-Takes an email address. It calls DeleteWatcher with a preset
-type of 'Requestor'
-
-
-=cut
+ @_);
+ $self->SUPER::Create(
+ EffectiveId => $args{'EffectiveId'},
+ Queue => $args{'Queue'},
+ Type => $args{'Type'},
+ IssueStatement => $args{'IssueStatement'},
+ Resolution => $args{'Resolution'},
+ Owner => $args{'Owner'},
+ Subject => $args{'Subject'},
+ InitialPriority => $args{'InitialPriority'},
+ FinalPriority => $args{'FinalPriority'},
+ Priority => $args{'Priority'},
+ TimeEstimated => $args{'TimeEstimated'},
+ TimeWorked => $args{'TimeWorked'},
+ Status => $args{'Status'},
+ TimeLeft => $args{'TimeLeft'},
+ Told => $args{'Told'},
+ Starts => $args{'Starts'},
+ Started => $args{'Started'},
+ Due => $args{'Due'},
+ Resolved => $args{'Resolved'},
+ Disabled => $args{'Disabled'},
+);
-sub DeleteRequestor {
- my $self = shift;
- my $id = shift;
- return ($self->DeleteWatcher ($id, 'Requestor'))
}
-# }}}
-
-# {{{ sub DeleteCc
-
-=head2 DeleteCc EMAIL
-
-Takes an email address. It calls DeleteWatcher with a preset
-type of 'Cc'
-
-
-=cut
-
-sub DeleteCc {
- my $self = shift;
- my $id = shift;
- return ($self->DeleteWatcher ($id, 'Cc'))
-}
-
-# }}}
-
-# {{{ sub DeleteAdminCc
-
-=head2 DeleteAdminCc EMAIL
-
-Takes an email address. It calls DeleteWatcher with a preset
-type of 'AdminCc'
-
-
-=cut
-
-sub DeleteAdminCc {
- my $self = shift;
- my $id = shift;
- return ($self->DeleteWatcher ($id, 'AdminCc'))
-}
-
-# }}}
-
-
-# }}}
-# {{{ sub Watchers
-=head2 Watchers
+=item id
-Watchers returns a Watchers object preloaded with this ticket\'s watchers.
+Returns the current value of id.
+(In the database, id is stored as int(11).)
-# It should return only the ticket watchers. the actual FooAsString
-# methods capture the queue watchers too. I don't feel thrilled about this,
-# but we don't want the Cc Requestors and AdminCc objects to get filled up
-# with all the queue watchers too. we've got seperate objects for that.
- # should we rename these as s/(.*)AsString/$1Addresses/ or somesuch?
=cut
-sub Watchers {
- my $self = shift;
-
- require RT::Watchers;
- my $watchers=RT::Watchers->new($self->CurrentUser);
- if ($self->CurrentUserHasRight('ShowTicket')) {
- $watchers->LimitToTicket($self->id);
- }
-
- return($watchers);
-
-}
-
-# }}}
-# {{{ a set of [foo]AsString subs that will return the various sorts of watchers for a ticket/queue as a comma delineated string
+=item EffectiveId
-=head2 RequestorsAsString
+Returns the current value of EffectiveId.
+(In the database, EffectiveId is stored as int(11).)
- B<Returns> String: All Ticket Requestor email addresses as a string.
-=cut
-sub RequestorsAsString {
- my $self=shift;
+=item SetEffectiveId VALUE
- unless ($self->CurrentUserHasRight('ShowTicket')) {
- return undef;
- }
-
- return ($self->Requestors->EmailsAsString() );
-}
-=head2 WatchersAsString
+Set EffectiveId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, EffectiveId will be stored as a int(11).)
-B<Returns> String: All Ticket Watchers email addresses as a string
=cut
-sub WatchersAsString {
- my $self=shift;
-
- unless ($self->CurrentUserHasRight('ShowTicket')) {
- return (0, "Permission Denied");
- }
-
- return ($self->Watchers->EmailsAsString());
-
-}
-=head2 AdminCcAsString
+=item Queue
-returns String: All Ticket AdminCc email addresses as a string
+Returns the current value of Queue.
+(In the database, Queue is stored as int(11).)
-=cut
-sub AdminCcAsString {
- my $self=shift;
+=item SetQueue VALUE
- unless ($self->CurrentUserHasRight('ShowTicket')) {
- return undef;
- }
-
- return ($self->AdminCc->EmailsAsString());
-
-}
-=head2 CcAsString
+Set Queue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Queue will be stored as a int(11).)
-returns String: All Ticket Ccs as a string of email addresses
=cut
-sub CcAsString {
- my $self=shift;
-
- unless ($self->CurrentUserHasRight('ShowTicket')) {
- return undef;
- }
-
- return ($self->Cc->EmailsAsString());
-
-}
-
-# }}}
-
-# {{{ Routines that return RT::Watchers objects of Requestors, Ccs and AdminCcs
-# {{{ sub Requestors
+=item QueueObj
-=head2 Requestors
+Returns the Queue Object which has the id returned by Queue
-Takes nothing.
-Returns this ticket's Requestors as an RT::Watchers object
-
-=cut
-
-sub Requestors {
- my $self = shift;
-
- my $requestors = $self->Watchers();
- if ($self->CurrentUserHasRight('ShowTicket')) {
- $requestors->LimitToRequestors();
- }
-
- return($requestors);
-
-}
-
-# }}}
-
-# {{{ sub Cc
-
-=head2 Cc
-
-Takes nothing.
-Returns a watchers object which contains this ticket's Cc watchers
-
-=cut
-
-sub Cc {
- my $self = shift;
-
- my $cc = $self->Watchers();
-
- if ($self->CurrentUserHasRight('ShowTicket')) {
- $cc->LimitToCc();
- }
-
- return($cc);
-
-}
-
-# }}}
-
-# {{{ sub AdminCc
-
-=head2 AdminCc
-
-Takes nothing.
-Returns this ticket\'s administrative Ccs as an RT::Watchers object
-
-=cut
-
-sub AdminCc {
- my $self = shift;
-
- my $admincc = $self->Watchers();
- if ($self->CurrentUserHasRight('ShowTicket')) {
- $admincc->LimitToAdminCc();
- }
- return($admincc);
-}
-
-# }}}
-
-# }}}
-
-# {{{ IsWatcher,IsRequestor,IsCc, IsAdminCc
-
-# {{{ sub IsWatcher
-# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
-
-=head2 IsWatcher
-
-Takes a param hash with the attributes Type and User. User is either a user object or string containing an email address. Returns true if that user or string
-is a ticket watcher. Returns undef otherwise
-
-=cut
-
-sub IsWatcher {
- my $self = shift;
-
- my %args = ( Type => 'Requestor',
- Email => undef,
- Id => undef,
- @_
- );
-
- my %cols = ('Type' => $args{'Type'},
- 'Scope' => 'Ticket',
- 'Value' => $self->Id,
- 'Owner' => undef,
- 'Email' => undef
- );
-
- if (ref($args{'Id'})){
- #If it's a ref, it's an RT::User object;
- $cols{'Owner'} = $args{'Id'}->Id;
- }
- elsif ($args{'Id'} =~ /^\d+$/) {
- # if it's an integer, it's a reference to an RT::User obj
- $cols{'Owner'} = $args{'Id'};
- }
- else {
- $cols{'Email'} = $args{'Id'};
- }
-
- if ($args{'Email'}) {
- $cols{'Email'} = $args{'Email'};
- }
-
- my $description = join(":",%cols);
-
- #If we've cached a positive match...
- if (defined $self->{'watchers_cache'}->{"$description"}) {
- if ($self->{'watchers_cache'}->{"$description"} == 1) {
- return(1);
- }
- else { #If we've cached a negative match...
- return(undef);
- }
- }
-
-
- my $watcher = new RT::Watcher($self->CurrentUser);
- $watcher->LoadByCols(%cols);
-
-
- if ($watcher->id) {
- $self->{'watchers_cache'}->{"$description"} = 1;
- return(1);
- }
- else {
- $self->{'watchers_cache'}->{"$description"} = 0;
- return(undef);
- }
-
-}
-# }}}
-
-# {{{ sub IsRequestor
-
-=head2 IsRequestor
-
- Takes an email address, RT::User object or integer (RT user id)
- Returns true if the string is a requestor of the current ticket.
-
-
-=cut
-
-sub IsRequestor {
- my $self = shift;
- my $person = shift;
-
- return ($self->IsWatcher(Type => 'Requestor', Id => $person));
-
-};
-
-# }}}
-
-# {{{ sub IsCc
-
-=head2 IsCc
-
-Takes a string. Returns true if the string is a Cc watcher of the current ticket.
-
-=cut
-
-sub IsCc {
- my $self = shift;
- my $cc = shift;
-
- return ($self->IsWatcher( Type => 'Cc', Id => $cc ));
-
-}
-
-# }}}
-
-# {{{ sub IsAdminCc
-
-=head2 IsAdminCc
-
-Takes a string. Returns true if the string is an AdminCc watcher of the current ticket.
-
-=cut
-
-sub IsAdminCc {
- my $self = shift;
- my $person = shift;
-
- return ($self->IsWatcher( Type => 'AdminCc', Id => $person ));
-
-}
-
-# }}}
-
-# {{{ sub IsOwner
-
-=head2 IsOwner
-
- Takes an RT::User object. Returns true if that user is this ticket's owner.
-returns undef otherwise
-
-=cut
-
-sub IsOwner {
- my $self = shift;
- my $person = shift;
-
-
- # no ACL check since this is used in acl decisions
- # unless ($self->CurrentUserHasRight('ShowTicket')) {
- # return(undef);
- # }
-
-
- #Tickets won't yet have owners when they're being created.
- unless ($self->OwnerObj->id) {
- return(undef);
- }
-
- if ($person->id == $self->OwnerObj->id) {
- return(1);
- }
- else {
- return(undef);
- }
-}
-
-
-# }}}
-
-# }}}
-
-# }}}
-
-# {{{ Routines dealing with queues
-
-# {{{ sub ValidateQueue
-
-sub ValidateQueue {
- my $self = shift;
- my $Value = shift;
-
- #TODO I don't think this should be here. We shouldn't allow anything to have an undef queue,
- if (!$Value) {
- $RT::Logger->warning( " RT:::Queue::ValidateQueue called with a null value. this isn't ok.");
- return (1);
- }
-
- my $QueueObj = RT::Queue->new($self->CurrentUser);
- my $id = $QueueObj->Load($Value);
-
- if ($id) {
- return (1);
- }
- else {
- return (undef);
- }
-}
-
-# }}}
-
-# {{{ sub SetQueue
-
-sub SetQueue {
- my $self = shift;
- my $NewQueue = shift;
-
- #Redundant. ACL gets checked in _Set;
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
-
- my $NewQueueObj = RT::Queue->new($self->CurrentUser);
- $NewQueueObj->Load($NewQueue);
-
- unless ($NewQueueObj->Id()) {
- return (0, "That queue does not exist");
- }
-
- if ($NewQueueObj->Id == $self->QueueObj->Id) {
- return (0, 'That is the same value');
- }
- unless ($self->CurrentUser->HasQueueRight(Right =>'CreateTicket',
- QueueObj => $NewQueueObj )) {
- return (0, "You may not create requests in that queue.");
- }
-
- unless ($self->OwnerObj->HasQueueRight(Right=> 'OwnTicket',
- QueueObj => $NewQueueObj)) {
- $self->Untake();
- }
-
- return($self->_Set(Field => 'Queue', Value => $NewQueueObj->Id()));
-
-}
-
-# }}}
-
-# {{{ sub QueueObj
-
-=head2 QueueObj
-
-Takes nothing. returns this ticket's queue object
=cut
sub QueueObj {
- my $self = shift;
-
- my $queue_obj = RT::Queue->new($self->CurrentUser);
- #We call __Value so that we can avoid the ACL decision and some deep recursion
- my ($result) = $queue_obj->Load($self->__Value('Queue'));
- return ($queue_obj);
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
}
+=item Type
-# }}}
+Returns the current value of Type.
+(In the database, Type is stored as varchar(16).)
-# }}}
-# {{{ Date printing routines
-# {{{ sub DueObj
-
-=head2 DueObj
-
- Returns an RT::Date object containing this ticket's due date
-
-=cut
-sub DueObj {
- my $self = shift;
-
- my $time = new RT::Date($self->CurrentUser);
-
- # -1 is RT::Date slang for never
- if ($self->Due) {
- $time->Set(Format => 'sql', Value => $self->Due );
- }
- else {
- $time->Set(Format => 'unix', Value => -1);
- }
-
- return $time;
-}
-# }}}
+=item SetType VALUE
-# {{{ sub DueAsString
-=head2 DueAsString
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(16).)
-Returns this ticket's due date as a human readable string
=cut
-sub DueAsString {
- my $self = shift;
- return $self->DueObj->AsString();
-}
-
-# }}}
-# {{{ sub GraceTimeAsString
+=item IssueStatement
-=head2 GraceTimeAsString
+Returns the current value of IssueStatement.
+(In the database, IssueStatement is stored as int(11).)
-Return the time until this ticket is due as a string
-
-=cut
-
-# TODO This should be deprecated
-
-sub GraceTimeAsString {
- my $self=shift;
-
- if ($self->Due) {
- return ($self->DueObj->AgeAsString());
- } else {
- return "";
- }
-}
-# }}}
+=item SetIssueStatement VALUE
-# {{{ sub ResolvedObj
-=head2 ResolvedObj
+Set IssueStatement to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, IssueStatement will be stored as a int(11).)
- Returns an RT::Date object of this ticket's 'resolved' time.
=cut
-sub ResolvedObj {
- my $self = shift;
- my $time = new RT::Date($self->CurrentUser);
- $time->Set(Format => 'sql', Value => $self->Resolved);
- return $time;
-}
-# }}}
+=item Resolution
-# {{{ sub SetStarted
+Returns the current value of Resolution.
+(In the database, Resolution is stored as int(11).)
-=head2 SetStarted
-Takes a date in ISO format or undef
-Returns a transaction id and a message
-The client calls "Start" to note that the project was started on the date in $date.
-A null date means "now"
-=cut
-
-sub SetStarted {
- my $self = shift;
- my $time = shift || 0;
-
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- #We create a date object to catch date weirdness
- my $time_obj = new RT::Date($self->CurrentUser());
- if ($time != 0) {
- $time_obj->Set(Format => 'ISO', Value => $time);
- }
- else {
- $time_obj->SetToNow();
- }
-
- #Now that we're starting, open this ticket
- #TODO do we really want to force this as policy? it should be a scrip
-
- #We need $TicketAsSystem, in case the current user doesn't have
- #ShowTicket
- #
- my $TicketAsSystem = new RT::Ticket($RT::SystemUser);
- $TicketAsSystem->Load($self->Id);
- if ($TicketAsSystem->Status eq 'new') {
- $TicketAsSystem->Open();
- }
-
- return ($self->_Set(Field => 'Started', Value =>$time_obj->ISO));
-
-}
+=item SetResolution VALUE
-# }}}
-# {{{ sub StartedObj
+Set Resolution to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Resolution will be stored as a int(11).)
-=head2 StartedObj
-
- Returns an RT::Date object which contains this ticket's
-'Started' time.
=cut
-sub StartedObj {
- my $self = shift;
-
- my $time = new RT::Date($self->CurrentUser);
- $time->Set(Format => 'sql', Value => $self->Started);
- return $time;
-}
-# }}}
+=item Owner
-# {{{ sub StartsObj
+Returns the current value of Owner.
+(In the database, Owner is stored as int(11).)
-=head2 StartsObj
- Returns an RT::Date object which contains this ticket's
-'Starts' time.
-=cut
-
-sub StartsObj {
- my $self = shift;
-
- my $time = new RT::Date($self->CurrentUser);
- $time->Set(Format => 'sql', Value => $self->Starts);
- return $time;
-}
-# }}}
+=item SetOwner VALUE
-# {{{ sub ToldObj
-=head2 ToldObj
+Set Owner to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Owner will be stored as a int(11).)
- Returns an RT::Date object which contains this ticket's
-'Told' time.
=cut
-sub ToldObj {
- my $self = shift;
-
- my $time = new RT::Date($self->CurrentUser);
- $time->Set(Format => 'sql', Value => $self->Told);
- return $time;
-}
+=item Subject
-# }}}
+Returns the current value of Subject.
+(In the database, Subject is stored as varchar(200).)
-# {{{ sub LongSinceToldAsString
-# TODO this should be deprecated
+=item SetSubject VALUE
-sub LongSinceToldAsString {
- my $self = shift;
-
- if ($self->Told) {
- return $self->ToldObj->AgeAsString();
- } else {
- return "Never";
- }
-}
-# }}}
-
-# {{{ sub ToldAsString
-=head2 ToldAsString
+Set Subject to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Subject will be stored as a varchar(200).)
-A convenience method that returns ToldObj->AsString
-
-TODO: This should be deprecated
=cut
-sub ToldAsString {
- my $self = shift;
- if ($self->Told) {
- return $self->ToldObj->AsString();
- }
- else {
- return("Never");
- }
-}
-# }}}
-
-# {{{ sub TimeWorkedAsString
+=item InitialPriority
-=head2 TimeWorkedAsString
+Returns the current value of InitialPriority.
+(In the database, InitialPriority is stored as int(11).)
-Returns the amount of time worked on this ticket as a Text String
-=cut
-sub TimeWorkedAsString {
- my $self=shift;
- return "0" unless $self->TimeWorked;
-
- #This is not really a date object, but if we diff a number of seconds
- #vs the epoch, we'll get a nice description of time worked.
-
- my $worked = new RT::Date($self->CurrentUser);
- #return the #of minutes worked turned into seconds and written as
- # a simple text string
-
- return($worked->DurationAsString($self->TimeWorked*60));
-}
+=item SetInitialPriority VALUE
-# }}}
+Set InitialPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, InitialPriority will be stored as a int(11).)
-# }}}
-
-# {{{ Routines dealing with correspondence/comments
-
-# {{{ sub Comment
-
-=head2 Comment
-
-Comment on this ticket.
-Takes a hashref with the follwoing attributes:
-
-MIMEObj, TimeTaken, CcMessageTo, BccMessageTo
=cut
-sub Comment {
- my $self = shift;
-
- my %args = (
- CcMessageTo => undef,
- BccMessageTo => undef,
- MIMEObj => undef,
- TimeTaken => 0,
- @_ );
-
- unless (($self->CurrentUserHasRight('CommentOnTicket')) or
- ($self->CurrentUserHasRight('ModifyTicket'))) {
- return (0, "Permission Denied");
- }
-
- unless ($args{'MIMEObj'}) {
- return(0,"No correspondence attached");
- }
-
- # If we've been passed in CcMessageTo and BccMessageTo fields,
- # add them to the mime object for passing on to the transaction handler
- # The "NotifyOtherRecipients" scripAction will look for RT--Send-Cc: and
- # RT-Send-Bcc: headers
-
- $args{'MIMEObj'}->head->add('RT-Send-Cc', $args{'CcMessageTo'});
- $args{'MIMEObj'}->head->add('RT-Send-Bcc', $args{'BccMessageTo'});
-
- #Record the correspondence (write the transaction)
- my ($Trans, $Msg, $TransObj) = $self->_NewTransaction( Type => 'Comment',
- Data =>($args{'MIMEObj'}->head->get('subject') || 'No Subject'),
- TimeTaken => $args{'TimeTaken'},
- MIMEObj => $args{'MIMEObj'}
- );
-
-
- return ($Trans, "The comment has been recorded");
-}
-
-# }}}
-# {{{ sub Correspond
+=item FinalPriority
-=head2 Correspond
+Returns the current value of FinalPriority.
+(In the database, FinalPriority is stored as int(11).)
-Correspond on this ticket.
-Takes a hashref with the following attributes:
-
-
-MIMEObj, TimeTaken, CcMessageTo, BccMessageTo
-
-=cut
-
-sub Correspond {
- my $self = shift;
- my %args = (
- CcMessageTo => undef,
- BccMessageTo => undef,
- MIMEObj => undef,
- TimeTaken => 0,
- @_ );
-
- unless (($self->CurrentUserHasRight('ReplyToTicket')) or
- ($self->CurrentUserHasRight('ModifyTicket'))) {
- return (0, "Permission Denied");
- }
-
- unless ($args{'MIMEObj'}) {
- return(0,"No correspondence attached");
- }
-
- # If we've been passed in CcMessageTo and BccMessageTo fields,
- # add them to the mime object for passing on to the transaction handler
- # The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and RT-Send-Bcc:
- # headers
-
- $args{'MIMEObj'}->head->add('RT-Send-Cc', $args{'CcMessageTo'});
- $args{'MIMEObj'}->head->add('RT-Send-Bcc', $args{'BccMessageTo'});
-
- #Record the correspondence (write the transaction)
- my ($Trans,$msg, $TransObj) = $self->_NewTransaction
- (Type => 'Correspond',
- Data => ($args{'MIMEObj'}->head->get('subject') || 'No Subject'),
- TimeTaken => $args{'TimeTaken'},
- MIMEObj=> $args{'MIMEObj'}
- );
-
- # TODO this bit of logic should really become a scrip for 2.2
- my $TicketAsSystem = new RT::Ticket($RT::SystemUser);
- $TicketAsSystem->Load($self->Id);
-
- if (
- ($TicketAsSystem->Status ne 'open') and
- ($TicketAsSystem->Status ne 'new')
- ) {
-
- my $oldstatus = $TicketAsSystem->Status();
- $TicketAsSystem->__Set(Field => 'Status', Value => 'open');
- $TicketAsSystem->_NewTransaction
- ( Type => 'Set',
- Field => 'Status',
- OldValue => $oldstatus,
- NewValue => 'open',
- Data => 'Ticket auto-opened on incoming correspondence'
- );
- }
-
- unless ($Trans) {
- $RT::Logger->err("$self couldn't init a transaction ($msg)\n");
- return ($Trans, "correspondence (probably) not sent", $args{'MIMEObj'});
- }
-
- #Set the last told date to now if this isn't mail from the requestor.
- #TODO: Note that this will wrongly ack mail from any non-requestor as a "told"
-
- unless ($TransObj->IsInbound) {
- $self->_SetTold;
- }
-
- return ($Trans, "correspondence sent");
-}
-# }}}
-# }}}
+=item SetFinalPriority VALUE
-# {{{ Routines dealing with Links and Relations between tickets
-# {{{ Link Collections
+Set FinalPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, FinalPriority will be stored as a int(11).)
-# {{{ sub Members
-
-=head2 Members
-
- This returns an RT::Links object which references all the tickets
-which are 'MembersOf' this ticket
=cut
-sub Members {
- my $self = shift;
- return ($self->_Links('Target', 'MemberOf'));
-}
-
-# }}}
-# {{{ sub MemberOf
+=item Priority
-=head2 MemberOf
+Returns the current value of Priority.
+(In the database, Priority is stored as int(11).)
- This returns an RT::Links object which references all the tickets that this
-ticket is a 'MemberOf'
-=cut
-
-sub MemberOf {
- my $self = shift;
- return ($self->_Links('Base', 'MemberOf'));
-}
-# }}}
+=item SetPriority VALUE
-# {{{ RefersTo
-=head2 RefersTo
+Set Priority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Priority will be stored as a int(11).)
- This returns an RT::Links object which shows all references for which this ticket is a base
=cut
-sub RefersTo {
- my $self = shift;
- return ($self->_Links('Base', 'RefersTo'));
-}
-
-# }}}
-# {{{ ReferredToBy
+=item TimeEstimated
-=head2 ReferredToBy
+Returns the current value of TimeEstimated.
+(In the database, TimeEstimated is stored as int(11).)
- This returns an RT::Links object which shows all references for which this ticket is a target
-=cut
-sub ReferredToBy {
- my $self = shift;
- return ($self->_Links('Target', 'RefersTo'));
-}
+=item SetTimeEstimated VALUE
-# }}}
-# {{{ DependedOnBy
+Set TimeEstimated to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeEstimated will be stored as a int(11).)
-=head2 DependedOnBy
-
- This returns an RT::Links object which references all the tickets that depend on this one
=cut
-sub DependedOnBy {
- my $self = shift;
- return ($self->_Links('Target','DependsOn'));
-}
-
-# }}}
-
-# {{{ DependsOn
-
-=head2 DependsOn
-
- This returns an RT::Links object which references all the tickets that this ticket depends on
-
-=cut
-sub DependsOn {
- my $self = shift;
- return ($self->_Links('Base','DependsOn'));
-}
-
-# }}}
-
-# {{{ sub _Links
-
-sub _Links {
- my $self = shift;
-
- #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
- #tobias meant by $f
- my $field = shift;
- my $type =shift || "";
-
- unless ($self->{"$field$type"}) {
- $self->{"$field$type"} = new RT::Links($self->CurrentUser);
- if ($self->CurrentUserHasRight('ShowTicket')) {
-
- $self->{"$field$type"}->Limit(FIELD=>$field, VALUE=>$self->URI);
- $self->{"$field$type"}->Limit(FIELD=>'Type',
- VALUE=>$type) if ($type);
- }
- }
- return ($self->{"$field$type"});
-}
-
-# }}}
-
-# }}}
-# {{{ sub DeleteLink
+=item TimeWorked
-=head2 DeleteLink
+Returns the current value of TimeWorked.
+(In the database, TimeWorked is stored as int(11).)
-Delete a link. takes a paramhash of Base, Target and Type.
-Either Base or Target must be null. The null value will
-be replaced with this ticket\'s id
-=cut
-sub DeleteLink {
- my $self = shift;
- my %args = ( Base => undef,
- Target => undef,
- Type => undef,
- @_ );
-
- #check acls
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- $RT::Logger->debug("No permission to delete links\n");
- return (0, 'Permission Denied');
-
-
- }
-
- #we want one of base and target. we don't care which
- #but we only want _one_
-
- if ($args{'Base'} and $args{'Target'}) {
- $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target\n");
- return (0, 'Can\'t specifiy both base and target');
- }
- elsif ($args{'Base'}) {
- $args{'Target'} = $self->Id();
- }
- elsif ($args{'Target'}) {
- $args{'Base'} = $self->Id();
- }
- else {
- $RT::Logger->debug("$self: Base or Target must be specified\n");
- return (0, 'Either base or target must be specified');
- }
-
- my $link = new RT::Link($self->CurrentUser);
- $RT::Logger->debug("Trying to load link: ". $args{'Base'}." ". $args{'Type'}. " ". $args{'Target'}. "\n");
-
- $link->Load($args{'Base'}, $args{'Type'}, $args{'Target'});
-
-
-
- #it's a real link.
- if ($link->id) {
- $RT::Logger->debug("We're going to delete link ".$link->id."\n");
- $link->Delete();
-
- my $TransString=
- "Ticket $args{'Base'} no longer $args{Type} ticket $args{'Target'}.";
- my ($Trans, $Msg, $TransObj) = $self->_NewTransaction
- (Type => 'DeleteLink',
- Field => $args{'Type'},
- Data => $TransString,
- TimeTaken => 0
- );
-
- return ($linkid, "Link deleted ($TransString)", $transactionid);
- }
- #if it's not a link we can find
- else {
- $RT::Logger->debug("Couldn't find that link\n");
- return (0, "Link not found");
- }
-}
+=item SetTimeWorked VALUE
-# }}}
-# {{{ sub AddLink
-
-=head2 AddLink
-
-Takes a paramhash of Type and one of Base or Target. Adds that link to this ticket.
+Set TimeWorked to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeWorked will be stored as a int(11).)
=cut
-sub AddLink {
- my $self = shift;
- my %args = ( Target => '',
- Base => '',
- Type => '',
- @_ );
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- if ($args{'Base'} and $args{'Target'}) {
- $RT::Logger->debug("$self tried to delete a link. both base and target were specified\n");
- return (0, 'Can\'t specifiy both base and target');
- }
- elsif ($args{'Base'}) {
- $args{'Target'} = $self->Id();
- }
- elsif ($args{'Target'}) {
- $args{'Base'} = $self->Id();
- }
- else {
- return (0, 'Either base or target must be specified');
- }
-
- # {{{ We don't want references to ourself
- if ($args{Base} eq $args{Target}) {
- return (0, "Can\'t link a ticket to itself");
- }
-
- # }}}
-
- # If the base isn't a URI, make it a URI.
- # If the target isn't a URI, make it a URI.
-
- # {{{ Check if the link already exists - we don't want duplicates
- my $old_link= new RT::Link ($self->CurrentUser);
- $old_link->Load($args{'Base'}, $args{'Type'}, $args{'Target'});
- if ($old_link->Id) {
- $RT::Logger->debug("$self Somebody tried to duplicate a link");
- return ($old_link->id, "Link already exists",0);
- }
- # }}}
-
- # Storing the link in the DB.
- my $link = RT::Link->new($self->CurrentUser);
- my ($linkid) = $link->Create(Target => $args{Target},
- Base => $args{Base},
- Type => $args{Type});
-
- unless ($linkid) {
- return (0,"Link could not be created");
- }
- #Write the transaction
-
- my $TransString="Ticket $args{'Base'} $args{Type} ticket $args{'Target'}.";
-
- my ($Trans, $Msg, $TransObj) = $self->_NewTransaction
- (Type => 'AddLink',
- Field => $args{'Type'},
- Data => $TransString,
- TimeTaken => 0
- );
-
- return ($Trans, "Link created ($TransString)");
-
-
-}
-# }}}
-# {{{ sub URI
+=item Status
-=head2 URI
+Returns the current value of Status.
+(In the database, Status is stored as varchar(10).)
-Returns this ticket's URI
-=cut
-sub URI {
- my $self = shift;
- return $RT::TicketBaseURI.$self->id;
-}
+=item SetStatus VALUE
-# }}}
-# {{{ sub MergeInto
+Set Status to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Status will be stored as a varchar(10).)
-=head2 MergeInto
-MergeInto take the id of the ticket to merge this ticket into.
=cut
-sub MergeInto {
- my $self = shift;
- my $MergeInto = shift;
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- # Load up the new ticket.
- my $NewTicket = RT::Ticket->new($RT::SystemUser);
- $NewTicket->Load($MergeInto);
-
- # make sure it exists.
- unless (defined $NewTicket->Id) {
- return (0, 'New ticket doesn\'t exist');
- }
-
-
- # Make sure the current user can modify the new ticket.
- unless ($NewTicket->CurrentUserHasRight('ModifyTicket')) {
- $RT::Logger->debug("failed...");
- return (0, "Permission Denied");
- }
-
- $RT::Logger->debug("checking if the new ticket has the same id and effective id...");
- unless ($NewTicket->id == $NewTicket->EffectiveId) {
- $RT::Logger->err('$self trying to merge into '.$NewTicket->Id .
- ' which is itself merged.\n');
- return (0, "Can't merge into a merged ticket. ".
- "You should never get this error");
- }
-
-
- # We use EffectiveId here even though it duplicates information from
- # the links table becasue of the massive performance hit we'd take
- # by trying to do a seperate database query for merge info everytime
- # loaded a ticket.
-
-
- #update this ticket's effective id to the new ticket's id.
- my ($id_val, $id_msg) = $self->__Set(Field => 'EffectiveId',
- Value => $NewTicket->Id());
-
- unless ($id_val) {
- $RT::Logger->error("Couldn't set effective ID for ".$self->Id.
- ": $id_msg");
- return(0,"Merge failed. Couldn't set EffectiveId");
- }
-
- my ($status_val, $status_msg) = $self->__Set(Field => 'Status',
- Value => 'resolved');
-
- unless ($status_val) {
- $RT::Logger->error("$self couldn't set status to resolved.".
- "RT's Database may be inconsistent.");
- }
-
- #make a new link: this ticket is merged into that other ticket.
- $self->AddLink( Type =>'MergedInto',
- Target => $NewTicket->Id() );
-
- #add all of this ticket's watchers to that ticket.
- my $watchers = $self->Watchers();
-
- while (my $watcher = $watchers->Next()) {
- unless (
- ($watcher->Owner &&
- $NewTicket->IsWatcher (Type => $watcher->Type,
- Id => $watcher->Owner)) or
- ($watcher->Email &&
- $NewTicket->IsWatcher (Type => $watcher->Type,
- Id => $watcher->Email))
- ) {
-
-
-
- $NewTicket->_AddWatcher(Silent => 1,
- Type => $watcher->Type,
- Email => $watcher->Email,
- Owner => $watcher->Owner);
- }
- }
-
-
- #find all of the tickets that were merged into this ticket.
- my $old_mergees = new RT::Tickets($self->CurrentUser);
- $old_mergees->Limit( FIELD => 'EffectiveId',
- OPERATOR => '=',
- VALUE => $self->Id );
-
- # update their EffectiveId fields to the new ticket's id
- while (my $ticket = $old_mergees->Next()) {
- my ($val, $msg) = $ticket->__Set(Field => 'EffectiveId',
- Value => $NewTicket->Id());
- }
- $NewTicket->_SetLastUpdated;
-
- return ($TransactionObj, "Merge Successful");
-}
-
-# }}}
-
-# }}}
-
-# {{{ Routines dealing with keywords
-
-# {{{ sub KeywordsObj
-
-=head2 KeywordsObj [KEYWORD_SELECT_ID]
-
- Returns an B<RT::ObjectKeywords> object preloaded with this ticket's ObjectKeywords.
-If the optional KEYWORD_SELECT_ID parameter is set, limit the keywords object to that keyword
-select.
-
-=cut
-
-sub KeywordsObj {
- my $self = shift;
- my $keyword_select;
-
- $keyword_select = shift if (@_);
-
- use RT::ObjectKeywords;
- my $Keywords = new RT::ObjectKeywords($self->CurrentUser);
-
- #ACL check
- if ($self->CurrentUserHasRight('ShowTicket')) {
- $Keywords->LimitToTicket($self->id);
- if ($keyword_select) {
- $Keywords->LimitToKeywordSelect($keyword_select);
- }
- }
- return ($Keywords);
-}
-# }}}
-# {{{ sub AddKeyword
+=item TimeLeft
-=head2 AddKeyword
+Returns the current value of TimeLeft.
+(In the database, TimeLeft is stored as int(11).)
-Takes a paramhash of Keyword and KeywordSelect. If Keyword is a valid choice
-for KeywordSelect, creates a KeywordObject. If the KeywordSelect says this should
-be a single KeywordObject, automatically removes the old value.
- Issues: probably doesn't enforce the depth restrictions or make sure that keywords
-are coming from the right part of the tree. really should.
-=cut
+=item SetTimeLeft VALUE
-sub AddKeyword {
- my $self = shift;
- #ACL check
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, 'Permission Denied');
- }
-
- return($self->_AddKeyword(@_));
-
-}
+Set TimeLeft to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeLeft will be stored as a int(11).)
-# Helper version of AddKeyword without that pesky ACL check
-sub _AddKeyword {
- my $self = shift;
- my %args = ( KeywordSelect => undef, # id of a keyword select record
- Keyword => undef, #id of the keyword to add
- Silent => 0,
- @_
- );
-
- my ($OldValue);
-
- #TODO make sure that $args{'Keyword'} is valid for $args{'KeywordSelect'}
-
- #TODO: make sure that $args{'KeywordSelect'} applies to this ticket's queue.
-
- my $Keyword = new RT::Keyword($self->CurrentUser);
- unless ($Keyword->Load($args{'Keyword'}) ) {
- $RT::Logger->err("$self Couldn't load Keyword ".$args{'Keyword'} ."\n");
- return(0, "Couldn't load keyword");
- }
-
- my $KeywordSelectObj = new RT::KeywordSelect($self->CurrentUser);
- unless ($KeywordSelectObj->Load($args{'KeywordSelect'})) {
- $RT::Logger->err("$self Couldn't load KeywordSelect ".$args{'KeywordSelect'});
- return(0, "Couldn't load keywordselect");
- }
-
- my $Keywords = $self->KeywordsObj($KeywordSelectObj->id);
-
- #If the ticket already has this keyword, just get out of here.
- if ($Keywords->HasEntry($Keyword->id)) {
- return(0, "That is already the current value");
- }
-
- #If the keywordselect wants this to be a singleton:
-
- if ($KeywordSelectObj->Single) {
-
- #Whack any old values...keep track of the last value that we get.
- #we shouldn't need a loop ehre, but we do it anyway, to try to
- # help keep the database clean.
- while (my $OldKey = $Keywords->Next) {
- $OldValue = $OldKey->KeywordObj->Name;
- $OldKey->Delete();
- }
-
-
- }
-
- # create the new objectkeyword
- my $ObjectKeyword = new RT::ObjectKeyword($self->CurrentUser);
- my $result = $ObjectKeyword->Create( Keyword => $Keyword->Id,
- ObjectType => 'Ticket',
- ObjectId => $self->Id,
- KeywordSelect => $KeywordSelectObj->Id );
-
-
- # record a single transaction, unless we were told not to
- unless ($args{'Silent'}) {
- my ($TransactionId, $Msg, $TransactionObj) =
- $self->_NewTransaction( Type => 'Keyword',
- Field => $KeywordSelectObj->Id,
- OldValue => $OldValue,
- NewValue => $Keyword->Name );
- }
- return ($TransactionId, "Keyword ".$ObjectKeyword->KeywordObj->Name ." added.");
-
-}
-
-# }}}
-
-# {{{ sub DeleteKeyword
-
-=head2 DeleteKeyword
-
- Takes a paramhash. Deletes the Keyword denoted by the I<Keyword> parameter from this
- ticket's object keywords.
=cut
-sub DeleteKeyword {
- my $self = shift;
- my %args = ( Keyword => undef,
- KeywordSelect => undef,
- @_ );
-
- #ACL check
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, 'Permission Denied');
- }
-
-
- #Load up the ObjectKeyword we\'re talking about
- my $ObjectKeyword = new RT::ObjectKeyword($self->CurrentUser);
- $ObjectKeyword->LoadByCols(Keyword => $args{'Keyword'},
- KeywordSelect => $args{'KeywordSelect'},
- ObjectType => 'Ticket',
- ObjectId => $self->id()
- );
-
- #if we can\'t find it, bail
- unless ($ObjectKeyword->id) {
- $RT::Logger->err("Couldn't find the keyword ".$args{'Keyword'} .
- " for keywordselect ". $args{'KeywordSelect'} .
- "for ticket ".$self->id );
- return (undef, "Couldn't load keyword while trying to delete it.");
- };
-
- #record transaction here.
- my ($TransactionId, $Msg, $TransObj) =
- $self->_NewTransaction( Type => 'Keyword',
- OldValue => $ObjectKeyword->KeywordObj->Name);
-
- $ObjectKeyword->Delete();
-
- return ($TransactionId, "Keyword ".$ObjectKeyword->KeywordObj->Name ." deleted.");
-
-}
-# }}}
+=item Told
-# }}}
+Returns the current value of Told.
+(In the database, Told is stored as datetime.)
-# {{{ Routines dealing with ownership
-# {{{ sub OwnerObj
-=head2 OwnerObj
+=item SetTold VALUE
-Takes nothing and returns an RT::User object of
-this ticket's owner
-=cut
+Set Told to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Told will be stored as a datetime.)
-sub OwnerObj {
- my $self = shift;
-
- #If this gets ACLed, we lose on a rights check in User.pm and
- #get deep recursion. if we need ACLs here, we need
- #an equiv without ACLs
-
- $owner = new RT::User ($self->CurrentUser);
- $owner->Load($self->__Value('Owner'));
-
- #Return the owner object
- return ($owner);
-}
-
-# }}}
-
-# {{{ sub OwnerAsString
-
-=head2 OwnerAsString
-
-Returns the owner's email address
=cut
-sub OwnerAsString {
- my $self = shift;
- return($self->OwnerObj->EmailAddress);
-}
+=item Starts
-# }}}
+Returns the current value of Starts.
+(In the database, Starts is stored as datetime.)
-# {{{ sub SetOwner
-=head2 SetOwner
-
-Takes two arguments:
- the Id or Name of the owner
-and (optionally) the type of the SetOwner Transaction. It defaults
-to 'Give'. 'Steal' is also a valid option.
-
-=cut
-
-sub SetOwner {
- my $self = shift;
- my $NewOwner = shift;
- my $Type = shift || "Give";
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- my $NewOwnerObj = RT::User->new($self->CurrentUser);
- my $OldOwnerObj = $self->OwnerObj;
-
- $NewOwnerObj->Load($NewOwner);
- if (!$NewOwnerObj->Id) {
- return (0, "That user does not exist");
- }
-
- #If thie ticket has an owner and it's not the current user
-
- if (($Type ne 'Steal' ) and ($Type ne 'Force') and #If we're not stealing
- ($self->OwnerObj->Id != $RT::Nobody->Id ) and #and the owner is set
- ($self->CurrentUser->Id ne $self->OwnerObj->Id())) { #and it's not us
- return(0, "You can only reassign tickets that you own or that are unowned");
- }
-
- #If we've specified a new owner and that user can't modify the ticket
- elsif (($NewOwnerObj->Id) and
- (!$NewOwnerObj->HasQueueRight(Right => 'OwnTicket',
- QueueObj => $self->QueueObj,
- TicketObj => $self))
- ) {
- return (0, "That user may not own requests in that queue");
- }
-
-
- #If the ticket has an owner and it's the new owner, we don't need
- #To do anything
- elsif (($self->OwnerObj) and ($NewOwnerObj->Id eq $self->OwnerObj->Id)) {
- return(0, "That user already owns that request");
- }
-
-
- my ($trans,$msg)=$self->_Set(Field => 'Owner',
- Value => $NewOwnerObj->Id,
- TimeTaken => 0,
- TransactionType => $Type);
-
- if ($trans) {
- $msg = "Owner changed from ".$OldOwnerObj->Name." to ".$NewOwnerObj->Name;
- }
- return ($trans, $msg);
-
-}
-# }}}
+=item SetStarts VALUE
-# {{{ sub Take
-=head2 Take
+Set Starts to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Starts will be stored as a datetime.)
-A convenince method to set the ticket's owner to the current user
=cut
-sub Take {
- my $self = shift;
- return ($self->SetOwner($self->CurrentUser->Id, 'Take'));
-}
-# }}}
+=item Started
-# {{{ sub Untake
+Returns the current value of Started.
+(In the database, Started is stored as datetime.)
-=head2 Untake
-Convenience method to set the owner to 'nobody' if the current user is the owner.
-=cut
+=item SetStarted VALUE
-sub Untake {
- my $self = shift;
- return($self->SetOwner($RT::Nobody->UserObj->Id, 'Untake'));
-}
-# }}}
-
-# {{{ sub Steal
-=head2 Steal
+Set Started to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Started will be stored as a datetime.)
-A convenience method to change the owner of the current ticket to the
-current user. Even if it's owned by another user.
=cut
-sub Steal {
- my $self = shift;
-
- if ($self->IsOwner($self->CurrentUser)) {
- return (0,"You already own this ticket");
- } else {
- return($self->SetOwner($self->CurrentUser->Id, 'Steal'));
-
- }
-
-}
-
-# }}}
-
-# }}}
-# {{{ Routines dealing with status
+=item Due
-# {{{ sub ValidateStatus
+Returns the current value of Due.
+(In the database, Due is stored as datetime.)
-=head2 ValidateStatus STATUS
-Takes a string. Returns true if that status is a valid status for this ticket.
-Returns false otherwise.
-=cut
-
-sub ValidateStatus {
- my $self = shift;
- my $status = shift;
+=item SetDue VALUE
- #Make sure the status passed in is valid
- unless ($self->QueueObj->IsValidStatus($status)) {
- return (undef);
- }
-
- return (1);
-}
+Set Due to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Due will be stored as a datetime.)
-# }}}
+=cut
-# {{{ sub SetStatus
-=head2 SetStatus STATUS
+=item Resolved
-Set this ticket\'s status. STATUS can be one of: new, open, stalled, resolved or dead.
+Returns the current value of Resolved.
+(In the database, Resolved is stored as datetime.)
-=cut
-sub SetStatus {
- my $self = shift;
- my $status = shift;
-
- #Check ACL
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, 'Permission Denied');
- }
-
- my $now = new RT::Date($self->CurrentUser);
- $now->SetToNow();
-
- #If we're changing the status from new, record that we've started
- if (($self->Status =~ /new/) && ($status ne 'new')) {
- #Set the Started time to "now"
- $self->_Set(Field => 'Started',
- Value => $now->ISO,
- RecordTransaction => 0);
- }
-
-
- if ($status eq 'resolved') {
- #When we resolve a ticket, set the 'Resolved' attribute to now.
- $self->_Set(Field => 'Resolved',
- Value => $now->ISO,
- RecordTransaction => 0);
- }
-
-
- #Actually update the status
- return($self->_Set(Field => 'Status',
- Value => $status,
- TimeTaken => 0,
- TransactionType => 'Status'));
-}
-# }}}
+=item SetResolved VALUE
-# {{{ sub Kill
-=head2 Kill
+Set Resolved to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Resolved will be stored as a datetime.)
-Takes no arguments. Marks this ticket for garbage collection
=cut
-sub Kill {
- my $self = shift;
- return ($self->SetStatus('dead'));
- # TODO: garbage collection
-}
-
-# }}}
-# {{{ sub Stall
+=item LastUpdatedBy
-=head2 Stall
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
-Sets this ticket's status to stalled
=cut
-sub Stall {
- my $self = shift;
- return ($self->SetStatus('stalled'));
-}
-
-# }}}
-# {{{ sub Open
+=item LastUpdated
-=head2 Open
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
-Sets this ticket\'s status to Open
=cut
-sub Open {
- my $self = shift;
- return ($self->SetStatus('open'));
-}
-
-# }}}
-# {{{ sub Resolve
+=item Creator
-=head2 Resolve
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
-Sets this ticket\'s status to Resolved
=cut
-sub Resolve {
- my $self = shift;
- return ($self->SetStatus('resolved'));
-}
-
-# }}}
-
-# }}}
-
-# {{{ Actions + Routines dealing with transactions
-# {{{ sub SetTold and _SetTold
+=item Created
-=head2 SetTold ISO [TIMETAKEN]
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
-Updates the told and records a transaction
=cut
-sub SetTold {
- my $self=shift;
- my $told;
- $told = shift if (@_);
- my $timetaken=shift || 0;
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- my $datetold = new RT::Date($self->CurrentUser);
- if ($told) {
- $datetold->Set( Format => 'iso',
- Value => $told);
- }
- else {
- $datetold->SetToNow();
- }
-
- return($self->_Set(Field => 'Told',
- Value => $datetold->ISO,
- TimeTaken => $timetaken,
- TransactionType => 'Told'));
-}
-=head2 _SetTold
+=item Disabled
-Updates the told without a transaction or acl check. Useful when we're sending replies.
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
-=cut
-sub _SetTold {
- my $self=shift;
-
- my $now = new RT::Date($self->CurrentUser);
- $now->SetToNow();
- #use __Set to get no ACLs ;)
- return($self->__Set(Field => 'Told',
- Value => $now->ISO));
-}
-# }}}
+=item SetDisabled VALUE
-# {{{ sub Transactions
-=head2 Transactions
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
- Returns an RT::Transactions object of all transactions on this ticket
=cut
-
-sub Transactions {
- my $self = shift;
-
- use RT::Transactions;
- my $transactions = RT::Transactions->new($self->CurrentUser);
-
- #If the user has no rights, return an empty object
- if ($self->CurrentUserHasRight('ShowTicket')) {
- my $tickets = $transactions->NewAlias('Tickets');
- $transactions->Join( ALIAS1 => 'main',
- FIELD1 => 'Ticket',
- ALIAS2 => $tickets,
- FIELD2 => 'id');
- $transactions->Limit( ALIAS => $tickets,
- FIELD => 'EffectiveId',
- VALUE => $self->id());
- # if the user may not see comments do not return them
- unless ($self->CurrentUserHasRight('ShowTicketComments')) {
- $transactions->Limit( FIELD => 'Type',
- OPERATOR => '!=',
- VALUE => "Comment");
- }
- }
-
- return($transactions);
-}
-
-# }}}
-
-# {{{ sub _NewTransaction
-
-sub _NewTransaction {
- my $self = shift;
- my %args = ( TimeTaken => 0,
- Type => undef,
- OldValue => undef,
- NewValue => undef,
- Data => undef,
- Field => undef,
- MIMEObj => undef,
- @_ );
-
-
- require RT::Transaction;
- my $trans = new RT::Transaction($self->CurrentUser);
- my ($transaction, $msg) =
- $trans->Create( Ticket => $self->Id,
- TimeTaken => $args{'TimeTaken'},
- Type => $args{'Type'},
- Data => $args{'Data'},
- Field => $args{'Field'},
- NewValue => $args{'NewValue'},
- OldValue => $args{'OldValue'},
- MIMEObj => $args{'MIMEObj'}
- );
-
- $RT::Logger->warning($msg) unless $transaction;
-
- $self->_SetLastUpdated;
-
- if (defined $args{'TimeTaken'} ) {
- $self->_UpdateTimeTaken($args{'TimeTaken'});
- }
- return($transaction, $msg, $trans);
-}
-# }}}
-# }}}
-
-# {{{ PRIVATE UTILITY METHODS. Mostly needed so Ticket can be a DBIx::Record
-
-# {{{ sub _ClassAccessible
sub _ClassAccessible {
{
- EffectiveId => { 'read' => 1, 'write' => 1, 'public' => 1 },
- Queue => { 'read' => 1, 'write' => 1 },
- Requestors => { 'read' => 1, 'write' => 1 },
- Owner => { 'read' => 1, 'write' => 1 },
- Subject => { 'read' => 1, 'write' => 1 },
- InitialPriority => { 'read' => 1, 'write' => 1 },
- FinalPriority => { 'read' => 1, 'write' => 1 },
- Priority => { 'read' => 1, 'write' => 1 },
- Status => { 'read' => 1, 'write' => 1 },
- TimeWorked => { 'read' => 1, 'write' => 1 },
- TimeLeft => { 'read' => 1, 'write' => 1 },
- Created => { 'read' => 1, 'auto' => 1 },
- Creator => { 'read' => 1, 'auto' => 1 },
- Told => { 'read' => 1, 'write' => 1 },
- Resolved => {'read' => 1},
- Starts => { 'read' => 1, 'write' => 1 },
- Started => { 'read' => 1, 'write' => 1 },
- Due => { 'read' => 1, 'write' => 1 },
- Creator => { 'read' => 1, 'auto' => 1 },
- Created => { 'read' => 1, 'auto' => 1 },
- LastUpdatedBy => { 'read' => 1, 'auto' => 1 },
- LastUpdated => { 'read' => 1, 'auto' => 1 }
- };
-
-}
-
-# }}}
-
-# {{{ sub _Set
-
-sub _Set {
- my $self = shift;
-
- unless ($self->CurrentUserHasRight('ModifyTicket')) {
- return (0, "Permission Denied");
- }
-
- my %args = (Field => undef,
- Value => undef,
- TimeTaken => 0,
- RecordTransaction => 1,
- TransactionType => 'Set',
- @_
- );
- #if the user is trying to modify the record
-
- #Take care of the old value we really don't want to get in an ACL loop.
- # so ask the super::_Value
- my $Old=$self->SUPER::_Value("$args{'Field'}");
-
- #Set the new value
- my ($ret, $msg)=$self->SUPER::_Set(Field => $args{'Field'},
- Value=> $args{'Value'});
-
- #If we can't actually set the field to the value, don't record
- # a transaction. instead, get out of here.
- if ($ret==0) {return (0,$msg);}
-
- if ($args{'RecordTransaction'} == 1) {
-
- my ($Trans, $Msg, $TransObj) =
- $self->_NewTransaction(Type => $args{'TransactionType'},
- Field => $args{'Field'},
- NewValue => $args{'Value'},
- OldValue => $Old,
- TimeTaken => $args{'TimeTaken'},
- );
- return ($Trans,$TransObj->Description);
- }
- else {
- return ($ret, $msg);
- }
-}
-
-# }}}
-
-# {{{ sub _Value
-
-=head2 _Value
-
-Takes the name of a table column.
-Returns its value as a string, if the user passes an ACL check
-
-=cut
-
-sub _Value {
-
- my $self = shift;
- my $field = shift;
-
-
- #if the field is public, return it.
- if ($self->_Accessible($field, 'public')) {
- #$RT::Logger->debug("Skipping ACL check for $field\n");
- return($self->SUPER::_Value($field));
-
- }
-
- #If the current user doesn't have ACLs, don't let em at it.
-
- unless ($self->CurrentUserHasRight('ShowTicket')) {
- return (undef);
- }
- return($self->SUPER::_Value($field));
-
-}
-
-# }}}
-
-# {{{ sub _UpdateTimeTaken
-
-=head2 _UpdateTimeTaken
-
-This routine will increment the timeworked counter. it should
-only be called from _NewTransaction
-
-=cut
-
-sub _UpdateTimeTaken {
- my $self = shift;
- my $Minutes = shift;
- my ($Total);
-
- $Total = $self->SUPER::_Value("TimeWorked");
- $Total = ($Total || 0) + ($Minutes || 0);
- $self->SUPER::_Set(Field => "TimeWorked",
- Value => $Total);
-
- return ($Total);
-}
+
+ id =>
+ {read => 1, type => 'int(11)', default => ''},
+ EffectiveId =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Queue =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Type =>
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
+ IssueStatement =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Resolution =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Owner =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Subject =>
+ {read => 1, write => 1, type => 'varchar(200)', default => '[no subject]'},
+ InitialPriority =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ FinalPriority =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Priority =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ TimeEstimated =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ TimeWorked =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Status =>
+ {read => 1, write => 1, type => 'varchar(10)', default => ''},
+ TimeLeft =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Told =>
+ {read => 1, write => 1, type => 'datetime', default => ''},
+ Starts =>
+ {read => 1, write => 1, type => 'datetime', default => ''},
+ Started =>
+ {read => 1, write => 1, type => 'datetime', default => ''},
+ Due =>
+ {read => 1, write => 1, type => 'datetime', default => ''},
+ Resolved =>
+ {read => 1, write => 1, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, type => 'datetime', default => ''},
+ Creator =>
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, type => 'datetime', default => ''},
+ Disabled =>
+ {read => 1, write => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
-# }}}
-# }}}
+ eval "require RT::Ticket_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Overlay.pm}) {
+ die $@;
+ };
-# {{{ Routines dealing with ACCESS CONTROL
+ eval "require RT::Ticket_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Vendor.pm}) {
+ die $@;
+ };
-# {{{ sub CurrentUserHasRight
+ eval "require RT::Ticket_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Local.pm}) {
+ die $@;
+ };
-=head2 CurrentUserHasRight
- Takes the textual name of a Ticket scoped right (from RT::ACE) and returns
-1 if the user has that right. It returns 0 if the user doesn't have that right.
-=cut
-sub CurrentUserHasRight {
- my $self = shift;
- my $right = shift;
-
- return ($self->HasRight( Principal=> $self->CurrentUser->UserObj(),
- Right => "$right"));
+=head1 SEE ALSO
-}
+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.
-# }}}
+These overlay files can contain new subs or subs to replace existing subs in this module.
-# {{{ sub HasRight
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
-=head2 HasRight
+ no warnings qw(redefine);
- Takes a paramhash with the attributes 'Right' and 'Principal'
- 'Right' is a ticket-scoped textual right from RT::ACE
- 'Principal' is an RT::User object
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
- Returns 1 if the principal has the right. Returns undef if not.
+RT::Ticket_Overlay, RT::Ticket_Vendor, RT::Ticket_Local
=cut
-sub HasRight {
- my $self = shift;
- my %args = ( Right => undef,
- Principal => undef,
- @_);
-
- unless ((defined $args{'Principal'}) and (ref($args{'Principal'}))) {
- $RT::Logger->warning("Principal attrib undefined for Ticket::HasRight");
- }
-
- return($args{'Principal'}->HasQueueRight(TicketObj => $self,
- Right => $args{'Right'}));
-}
-
-# }}}
-
-# }}}
-
1;
-
-=head1 AUTHOR
-
-Jesse Vincent, jesse@fsck.com
-
-=head1 SEE ALSO
-
-RT
-
-=cut
-
-