import rt 3.4.6
[freeside.git] / rt / lib / RT / Ticket_Overlay.pm
index 5960f0b..a294dca 100644 (file)
@@ -2,7 +2,7 @@
 # 
 # COPYRIGHT:
 #  
-# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC 
+# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
 #                                          <jesse@bestpractical.com>
 # 
 # (Except where explicitly superseded by other copyright notices)
@@ -22,9 +22,7 @@
 # 
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/copyleft/gpl.html.
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 # 
 # 
 # CONTRIBUTION SUBMISSION POLICY:
@@ -153,7 +151,6 @@ use RT::Date;
 use RT::CustomFields;
 use RT::Tickets;
 use RT::Transactions;
-use RT::Reminders;
 use RT::URI::fsck_com_rt;
 use RT::URI;
 use MIME::Entity;
@@ -330,20 +327,10 @@ Arguments: ARGS is a hash of named parameters.  Valid parameters are:
   MIMEObj -- a MIME::Entity object with the content of the initial ticket request.
   CustomField-<n> -- a scalar or array of values for the customfield with the id <n>
 
-Ticket links can be set up during create by passing the link type as a hask key and
-the ticket id to be linked to as a value (or a URI when linking to other objects).
-Multiple links of the same type can be created by passing an array ref. For example:
-
-  Parent => 45,
-  DependsOn => [ 15, 22 ],
-  RefersTo => 'http://www.bestpractical.com',
-
-Supported link types are C<MemberOf>, C<HasMember>, C<RefersTo>, C<ReferredToBy>,
-C<DependsOn> and C<DependedOnBy>. Also, C<Parents> is alias for C<MemberOf> and
-C<Members> and C<Children> are aliases for C<HasMember>.
 
 Returns: TICKETID, Transaction Object, Error Message
 
+
 =begin testing
 
 my $t = RT::Ticket->new($RT::SystemUser);
@@ -476,9 +463,10 @@ sub Create {
     #If the status is an inactive status, set the resolved date
     if ( $QueueObj->IsInactiveStatus( $args{'Status'} ) && !$args{'Resolved'} )
     {
-        $RT::Logger->debug( "Got a ". $args{'Status'}
-            ." ticket with undefined resolved date. Setting to now."
-        );
+        $RT::Logger->debug( "Got a "
+              . $args{'Status'}
+              . "ticket with a resolved of "
+              . $args{'Resolved'} );
         $Resolved->SetToNow;
     }
 
@@ -690,20 +678,6 @@ sub Create {
         foreach my $link (
             ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
         {
-            # Check rights on the other end of the link if we must
-            # then run _AddLink that doesn't check for ACLs
-            if ( $RT::StrictLinkACL ) {
-                my ($val, $msg, $obj) = $self->__GetTicketFromURI( URI => $link );
-                unless ( $val ) {
-                    push @non_fatal_errors, $msg;
-                    next;
-                }
-                if ( $obj && !$obj->CurrentUserHasRight('ModifyTicket') ) {
-                    push @non_fatal_errors, $self->loc('Linking. Permission denied');
-                    next;
-                }
-            }
-            
             my ( $wval, $wmsg ) = $self->_AddLink(
                 Type                          => $LINKTYPEMAP{$type}->{'Type'},
                 $LINKTYPEMAP{$type}->{'Mode'} => $link,
@@ -1347,10 +1321,7 @@ sub AddWatcher {
 
     # {{{ Check ACLS
     #If the watcher we're trying to add is for the current user
-    if ( $self->CurrentUser->PrincipalId == ($args{'PrincipalId'} || 0)
-       or    lc( $self->CurrentUser->UserObj->EmailAddress )
-          eq lc( RT::User::CanonicalizeEmailAddress(undef, $args{'Email'}) || '' ) )
-    {
+    if ( $self->CurrentUser->PrincipalId  eq $args{'PrincipalId'}) {
         #  If it's an AdminCc and they don't have 
         #   'WatchAsAdminCc' or 'ModifyTicket', bail
         if ( $args{'Type'} eq 'AdminCc' ) {
@@ -1514,7 +1485,7 @@ sub DeleteWatcher {
 
     # {{{ Check ACLS
     #If the watcher we're trying to add is for the current user
-    if ( $self->CurrentUser->PrincipalId == $principal->id ) {
+    if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'} ) {
 
         #  If it's an AdminCc and they don't have
         #   'WatchAsAdminCc' or 'ModifyTicket', bail
@@ -2125,12 +2096,12 @@ sub SetStarted {
     my $time = shift || 0;
 
     unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
-        return ( 0, $self->loc("Permission Denied") );
+        return ( 0, self->loc("Permission Denied") );
     }
 
     #We create a date object to catch date weirdness
     my $time_obj = new RT::Date( $self->CurrentUser() );
-    if ( $time ) {
+    if ( $time != 0 ) {
         $time_obj->Set( Format => 'ISO', Value => $time );
     }
     else {
@@ -2423,9 +2394,8 @@ sub _RecordNote {
     # If this is from an external source, we need to come up with its
     # internal Message-ID now, so all emails sent because of this
     # message have a common Message-ID
-    unless ( ($args{'MIMEObj'}->head->get('Message-ID') || '')
-            =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$RT::Organization>/ )
-    {
+    unless ($args{'MIMEObj'}->head->get('Message-ID')
+            =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@$RT::Organization>/) {
         $args{'MIMEObj'}->head->set( 'RT-Message-ID',
             "<rt-"
             . $RT::VERSION . "-"
@@ -2515,29 +2485,11 @@ sub DeleteLink {
         @_
     );
 
-    unless ( $args{'Target'} || $args{'Base'} ) {
-        $RT::Logger->error("Base or Target must be specified\n");
-        return ( 0, $self->loc('Either base or target must be specified') );
-    }
-
     #check acls
-    my $right = 0;
-    $right++ if $self->CurrentUserHasRight('ModifyTicket');
-    if ( !$right && $RT::StrictLinkACL ) {
-        return ( 0, $self->loc("Permission Denied") );
-    }
+    unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+        $RT::Logger->debug("No permission to delete links\n");
+        return ( 0, $self->loc('Permission Denied'))
 
-    # If the other URI is an RT::Ticket, we want to make sure the user
-    # can modify it too...
-    my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
-    return (0, $msg) unless $status;
-    if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
-        $right++;
-    }
-    if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
-         ( $RT::StrictLinkACL && $right < 2 ) )
-    {
-        return ( 0, $self->loc("Permission Denied") );
     }
 
     my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
@@ -2605,52 +2557,13 @@ sub AddLink {
                  Silent => undef,
                  @_ );
 
-    unless ( $args{'Target'} || $args{'Base'} ) {
-        $RT::Logger->error("Base or Target must be specified\n");
-        return ( 0, $self->loc('Either base or target must be specified') );
-    }
 
-    my $right = 0;
-    $right++ if $self->CurrentUserHasRight('ModifyTicket');
-    if ( !$right && $RT::StrictLinkACL ) {
-        return ( 0, $self->loc("Permission Denied") );
-    }
-
-    # If the other URI is an RT::Ticket, we want to make sure the user
-    # can modify it too...
-    my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
-    return (0, $msg) unless $status;
-    if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
-        $right++;
-    }
-    if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
-         ( $RT::StrictLinkACL && $right < 2 ) )
-    {
+    unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
         return ( 0, $self->loc("Permission Denied") );
     }
 
-    return $self->_AddLink(%args);
-}
-
-sub __GetTicketFromURI {
-    my $self = shift;
-    my %args = ( URI => '', @_ );
-
-    # If the other URI is an RT::Ticket, we want to make sure the user
-    # can modify it too...
-    my $uri_obj = RT::URI->new( $self->CurrentUser );
-    $uri_obj->FromURI( $args{'URI'} );
 
-    unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
-           my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
-        $RT::Logger->warning( "$msg\n" );
-        return( 0, $msg );
-    }
-    my $obj = $uri_obj->Resolver->Object;
-    unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
-        return (1, 'Found not a ticket', undef);
-    }
-    return (1, 'Found ticket', $obj);
+    $self->_AddLink(%args);
 }
 
 =head2 _AddLink  
@@ -2667,8 +2580,45 @@ sub _AddLink {
                  Silent => undef,
                  @_ );
 
-    my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
-    return ($val, $msg) if !$val || $exist;
+    # {{{ If the other URI is an RT::Ticket, we want to make sure the user
+    # can modify it too...
+    my $other_ticket_uri = RT::URI->new($self->CurrentUser);
+
+    if ( $args{'Target'} ) {
+        $other_ticket_uri->FromURI( $args{'Target'} );
+
+    }
+    elsif ( $args{'Base'} ) {
+        $other_ticket_uri->FromURI( $args{'Base'} );
+    }
+
+    unless ( $other_ticket_uri->Resolver && $other_ticket_uri->Scheme ) {
+       my $msg = $args{'Target'} ? $self->loc("Couldn't resolve target '[_1]' into a URI.", $args{'Target'})
+          : $self->loc("Couldn't resolve base '[_1]' into a URI.", $args{'Base'});
+        $RT::Logger->warning( "$self $msg\n" );
+
+        return( 0, $msg );
+    }
+
+    if ( $other_ticket_uri->Resolver->Scheme eq 'fsck.com-rt') {
+        my $object = $other_ticket_uri->Resolver->Object;
+
+        if (   UNIVERSAL::isa( $object, 'RT::Ticket' )
+            && $object->id
+            && !$object->CurrentUserHasRight('ModifyTicket') )
+        {
+            return ( 0, $self->loc("Permission Denied") );
+        }
+
+    }
+
+    # }}}
+
+    my ($val, $Msg) = $self->SUPER::_AddLink(%args);
+
+    if (!$val) {
+       return ($val, $Msg);
+    }
 
     my ($direction, $remote_link);
     if ( $args{'Target'} ) {
@@ -2681,10 +2631,10 @@ sub _AddLink {
 
     # Don't write the transaction if we're doing this on create
     if ( $args{'Silent'} ) {
-        return ( $val, $msg );
+        return ( $val, $Msg );
     }
     else {
-        my $remote_uri = RT::URI->new( $self->CurrentUser );
+       my $remote_uri = RT::URI->new( $self->CurrentUser );
        $remote_uri->FromURI( $remote_link );
 
         #Write the transaction
@@ -2782,24 +2732,15 @@ sub MergeInto {
         return ( 0, $self->loc("Merge failed. Couldn't set EffectiveId") );
     }
 
+    my ( $status_val, $status_msg ) = $self->__Set( Field => 'Status', Value => 'resolved');
 
-    if ( $self->__Value('Status') ne 'resolved' ) {
-
-        my ( $status_val, $status_msg )
-            = $self->__Set( Field => 'Status', Value => 'resolved' );
-
-        unless ($status_val) {
-            $RT::Handle->Rollback();
-            $RT::Logger->error(
-                $self->loc(
-                    "[_1] couldn't set status to resolved. RT's Database may be inconsistent.",
-                    $self
-                )
-            );
-            return ( 0, $self->loc("Merge failed. Couldn't set Status") );
-        }
+    unless ($status_val) {
+        $RT::Handle->Rollback();
+        $RT::Logger->error( $self->loc("[_1] couldn't set status to resolved. RT's Database may be inconsistent.", $self) );
+        return ( 0, $self->loc("Merge failed. Couldn't set Status") );
     }
 
+
     # update all the links that point to that old ticket
     my $old_links_to = RT::Links->new($self->CurrentUser);
     $old_links_to->Limit(FIELD => 'Target', VALUE => $self->URI);
@@ -2975,8 +2916,6 @@ my $txns = RT::Transactions->new($RT::SystemUser);
 $txns->OrderBy(FIELD => 'id', ORDER => 'DESC');
 $txns->Limit(FIELD => 'ObjectId', VALUE => '1');
 $txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket');
-$txns->Limit(FIELD => 'Type', OPERATOR => '!=',  VALUE => 'EmailRecord');
-
 my $steal  = $txns->First;
 ok($steal->OldValue == $root->Id , "Stolen from root");
 ok($steal->NewValue == $RT::SystemUser->Id , "Stolen by the systemuser");
@@ -2990,77 +2929,68 @@ sub SetOwner {
     my $NewOwner = shift;
     my $Type     = shift || "Give";
 
-    $RT::Handle->BeginTransaction();
-
-    $self->_SetLastUpdated(); # lock the ticket
-    $self->Load( $self->id ); # in case $self changed while waiting for lock
-
-    my $OldOwnerObj = $self->OwnerObj;
-
-    my $NewOwnerObj = RT::User->new( $self->CurrentUser );
-    $NewOwnerObj->Load( $NewOwner );
-    unless ( $NewOwnerObj->Id ) {
-        $RT::Handle->Rollback();
-        return ( 0, $self->loc("That user does not exist") );
-    }
-
-
     # must have ModifyTicket rights
     # or TakeTicket/StealTicket and $NewOwner is self
     # see if it's a take
-    if ( $OldOwnerObj->Id == $RT::Nobody->Id ) {
+    if ( $self->OwnerObj->Id == $RT::Nobody->Id ) {
         unless (    $self->CurrentUserHasRight('ModifyTicket')
                  || $self->CurrentUserHasRight('TakeTicket') ) {
-            $RT::Handle->Rollback();
             return ( 0, $self->loc("Permission Denied") );
         }
     }
 
     # see if it's a steal
-    elsif (    $OldOwnerObj->Id != $RT::Nobody->Id
-            && $OldOwnerObj->Id != $self->CurrentUser->id ) {
+    elsif (    $self->OwnerObj->Id != $RT::Nobody->Id
+            && $self->OwnerObj->Id != $self->CurrentUser->id ) {
 
         unless (    $self->CurrentUserHasRight('ModifyTicket')
                  || $self->CurrentUserHasRight('StealTicket') ) {
-            $RT::Handle->Rollback();
             return ( 0, $self->loc("Permission Denied") );
         }
     }
     else {
         unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
-            $RT::Handle->Rollback();
             return ( 0, $self->loc("Permission Denied") );
         }
     }
+    my $NewOwnerObj = RT::User->new( $self->CurrentUser );
+    my $OldOwnerObj = $self->OwnerObj;
 
-    # If we're not stealing and the ticket has an owner and it's not
-    # the current user
-    if ( $Type ne 'Steal' and $Type ne 'Force'
-         and $OldOwnerObj->Id != $RT::Nobody->Id
-         and $OldOwnerObj->Id != $self->CurrentUser->Id )
-    {
-        $RT::Handle->Rollback();
-        return ( 0, $self->loc("You can only take tickets that are unowned") )
-            if $NewOwnerObj->id == $self->CurrentUser->id;
-        return (
-            0,
-            $self->loc("You can only reassign tickets that you own or that are unowned" )
-        );
+    $NewOwnerObj->Load($NewOwner);
+    if ( !$NewOwnerObj->Id ) {
+        return ( 0, $self->loc("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,
+                 $self->loc(
+"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->HasRight( Right => 'OwnTicket', Object => $self ) ) {
-        $RT::Handle->Rollback();
+    elsif ( ( $NewOwnerObj->Id )
+            and ( !$NewOwnerObj->HasRight( Right  => 'OwnTicket',
+                                           Object => $self ) )
+      ) {
         return ( 0, $self->loc("That user may not own tickets in that queue") );
     }
 
-    # If the ticket has an owner and it's the new owner, we don't need
-    # To do anything
-    elsif ( $NewOwnerObj->Id == $OldOwnerObj->Id ) {
-        $RT::Handle->Rollback();
+    #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, $self->loc("That user already owns that ticket") );
     }
 
+    $RT::Handle->BeginTransaction();
+
     # Delete the owner in the owner group, then add a new one
     # TODO: is this safe? it's not how we really want the API to work
     # for most things, but it's fast.
@@ -3095,6 +3025,8 @@ sub SetOwner {
         return ( 0, $self->loc("Could not change owner. ") . $msg );
     }
 
+    $RT::Handle->Commit();
+
     ($val, $msg) = $self->_NewTransaction(
         Type      => $Type,
         Field     => 'Owner',
@@ -3106,14 +3038,9 @@ sub SetOwner {
     if ( $val ) {
         $msg = $self->loc( "Owner changed from [_1] to [_2]",
                            $OldOwnerObj->Name, $NewOwnerObj->Name );
-    }
-    else {
-        $RT::Handle->Rollback();
-        return ( 0, $msg );
-    }
-
-    $RT::Handle->Commit();
 
+        # TODO: make sure the trans committed properly
+    }
     return ( $val, $msg );
 }
 
@@ -3458,8 +3385,6 @@ sub DESTROY {
     return if $self->{_Destroyed}++;
 
     my $batch = $self->TransactionBatch or return;
-    return unless @$batch;
-
     require RT::Scrips;
     RT::Scrips->new($RT::SystemUser)->Apply(
        Stage           => 'TransactionBatch',
@@ -3692,26 +3617,6 @@ sub HasRight {
 
 # }}}
 
-=head2 Reminders
-
-Return the Reminders object for this ticket. (It's an RT::Reminders object.)
-It isn't acutally a searchbuilder collection itself.
-
-=cut
-
-sub Reminders {
-    my $self = shift;
-    
-    unless ($self->{'__reminders'}) {
-        $self->{'__reminders'} = RT::Reminders->new($self->CurrentUser);
-        $self->{'__reminders'}->Ticket($self->id);
-    }
-    return $self->{'__reminders'};
-
-}
-
-
-
 # {{{ sub Transactions 
 
 =head2 Transactions
@@ -3756,7 +3661,7 @@ sub Transactions {
 
 =head2 TransactionCustomFields
 
-    Returns the custom fields that transactions on tickets will have.
+    Returns the custom fields that transactions on tickets will ahve.
 
 =cut
 
@@ -3791,6 +3696,7 @@ sub CustomFieldValues {
             # If we didn't find a valid cfid, give up.
             return RT::CustomFieldValues->new($self->CurrentUser);
         }
+        $field = $cf->id;
     }
     return $self->SUPER::CustomFieldValues($field);
 }