diff options
Diffstat (limited to 'rt/lib/RT/Action')
-rw-r--r-- | rt/lib/RT/Action/AutoOpen.pm | 38 | ||||
-rwxr-xr-x | rt/lib/RT/Action/Autoreply.pm | 50 | ||||
-rw-r--r-- | rt/lib/RT/Action/CreateTickets.pm | 1256 | ||||
-rw-r--r-- | rt/lib/RT/Action/EscalatePriority.pm | 38 | ||||
-rwxr-xr-x | rt/lib/RT/Action/Generic.pm | 59 | ||||
-rwxr-xr-x | rt/lib/RT/Action/Notify.pm | 73 | ||||
-rwxr-xr-x | rt/lib/RT/Action/NotifyAsComment.pm | 38 | ||||
-rw-r--r-- | rt/lib/RT/Action/RecordComment.pm | 142 | ||||
-rw-r--r-- | rt/lib/RT/Action/RecordCorrespondence.pm | 143 | ||||
-rw-r--r-- | rt/lib/RT/Action/ResolveMembers.pm | 38 | ||||
-rwxr-xr-x | rt/lib/RT/Action/SendEmail.pm | 680 | ||||
-rw-r--r-- | rt/lib/RT/Action/SetPriority.pm | 38 | ||||
-rw-r--r-- | rt/lib/RT/Action/UserDefined.pm | 38 |
13 files changed, 1990 insertions, 641 deletions
diff --git a/rt/lib/RT/Action/AutoOpen.pm b/rt/lib/RT/Action/AutoOpen.pm index 7c8c2b89d..b28c50d3e 100644 --- a/rt/lib/RT/Action/AutoOpen.pm +++ b/rt/lib/RT/Action/AutoOpen.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK # This Action will open the BASE if a dependent is resolved. package RT::Action::AutoOpen; diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm index f58b8f284..6d2894f54 100755 --- a/rt/lib/RT/Action/Autoreply.pm +++ b/rt/lib/RT/Action/Autoreply.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# # +# CONTRIBUTION SUBMISSION POLICY: # -# END LICENSE BLOCK +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK package RT::Action::Autoreply; require RT::Action::SendEmail; @@ -28,6 +50,18 @@ use strict; use vars qw/@ISA/; @ISA = qw(RT::Action::SendEmail); +=head2 Prepare + +Set up the relevant recipients, then call our parent. + +=cut + + +sub Prepare { + my $self = shift; + $self->SetRecipients(); + $self->SUPER::Prepare(); +} # {{{ sub SetRecipients diff --git a/rt/lib/RT/Action/CreateTickets.pm b/rt/lib/RT/Action/CreateTickets.pm index e1c6f4a9b..68f402e4e 100644 --- a/rt/lib/RT/Action/CreateTickets.pm +++ b/rt/lib/RT/Action/CreateTickets.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,17 +20,34 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# # +# CONTRIBUTION SUBMISSION POLICY: # -# END LICENSE BLOCK +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK package RT::Action::CreateTickets; require RT::Action::Generic; use strict; +use warnings; use vars qw/@ISA/; @ISA = qw(RT::Action::Generic); @@ -39,7 +62,7 @@ Create one or more tickets according to an externally supplied template. =head1 SYNOPSIS - ===Create-Ticket: codereview + ===Create-Ticket codereview Subject: Code review for {$Tickets{'TOP'}->Subject} Depended-On-By: TOP Content: Someone has created a ticket. you should review and approve it, @@ -220,27 +243,11 @@ ok ($approvalsq->Id, "Created Approvals test queue"); my $approvals = '===Create-Ticket: approval -{ my $name = "HR"; - my $groups = RT::Groups->new($RT::SystemUser); - $groups->LimitToUserDefinedGroups(); - $groups->Limit(FIELD => "Name", OPERATOR => "=", VALUE => "$name"); - $groups->WithMember($Transaction->CreatorObj->Id); - - my $groupid = $groups->First->Id; - - my $adminccs = RT::Users->new($RT::SystemUser); - $adminccs->WhoHaveRight(Right => "AdminGroup", IncludeSystemRights => undef, IncludeSuperusers => 0, IncludeSubgroupMembers => 0, Object => $groups->First); - - my @admins; - while (my $admin = $adminccs->Next) { - push (@admins, $admin->EmailAddress); - } -} Queue: Approvals Type: Approval AdminCc: {join ("\nAdminCc: ",@admins) } -Depended-On-By: TOP -Refers-To: TOP +Depended-On-By: {$Tickets{"TOP"}->Id} +Refers-To: TOP Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject} Due: {time + 86400} Content-Type: text/plain @@ -250,11 +257,11 @@ Blah ENDOFCONTENT ===Create-Ticket: two Subject: Manager approval. -Depends-On: {$Tickets{"approval"}->Id} +Depended-On-By: approval Queue: Approvals Content-Type: text/plain Content: -Your minion approved this ticket. you ok with that? +Your minion approved ticket {$Tickets{"TOP"}->Id}. you ok with that? ENDOFCONTENT '; @@ -281,10 +288,173 @@ ok ($scrip->ConditionObj->Id, "Created the scrip condition"); ok ($scrip->ActionObj->Id, "Created the scrip action"); my $t = RT::Ticket->new($RT::SystemUser); -$t->Create(Subject => "Sample workflow test", +my($tid, $ttrans, $tmsg) = $t->Create(Subject => "Sample workflow test", Owner => "root", Queue => $q->Id); +ok ($tid,$tmsg); + +my $deps = $t->DependsOn; +is ($deps->Count, 1, "The ticket we created depends on one other ticket"); +my $dependson= $deps->First->TargetObj; +ok ($dependson->Id, "It depends on a real ticket"); +unlike ($dependson->Subject, qr/{/, "The subject doesn't have braces in it. that means we're interpreting expressions"); +is ($t->ReferredToBy->Count,1, "It's only referred to by one other ticket"); +is ($t->ReferredToBy->First->BaseObj->Id,$t->DependsOn->First->TargetObj->Id, "The same ticket that depends on it refers to it."); +use RT::Action::CreateTickets; +my $action = RT::Action::CreateTickets->new( CurrentUser => $RT::SystemUser);; + +# comma-delimited templates +my $commas = <<"EOF"; +id,Queue,Subject,Owner,Content +ticket1,General,"foo, bar",root,blah +ticket2,General,foo bar,root,blah +ticket3,General,foo' bar,root,blah'boo +ticket4,General,foo' bar,,blah'boo +EOF + + +# Comma delimited templates with missing data +my $sparse_commas = <<"EOF"; +id,Queue,Subject,Owner,Requestor +ticket14,General,,,bobby +ticket15,General,,,tommy +ticket16,General,,suzie,tommy +ticket17,General,Foo "bar" baz,suzie,tommy +ticket18,General,'Foo "bar" baz',suzie,tommy +ticket19,General,'Foo bar' baz,suzie,tommy +EOF + + +# tab-delimited templates +my $tabs = <<"EOF"; +id\tQueue\tSubject\tOwner\tContent +ticket10\tGeneral\t"foo' bar"\troot\tblah' +ticket11\tGeneral\tfoo, bar\troot\tblah +ticket12\tGeneral\tfoo' bar\troot\tblah'boo +ticket13\tGeneral\tfoo' bar\t\tblah'boo +EOF + +my %expected; + +$expected{ticket1} = <<EOF; +Queue: General +Subject: foo, bar +Owner: root +Content: blah +ENDOFCONTENT +EOF + +$expected{ticket2} = <<EOF; +Queue: General +Subject: foo bar +Owner: root +Content: blah +ENDOFCONTENT +EOF + +$expected{ticket3} = <<EOF; +Queue: General +Subject: foo' bar +Owner: root +Content: blah'boo +ENDOFCONTENT +EOF + +$expected{ticket4} = <<EOF; +Queue: General +Subject: foo' bar +Owner: +Content: blah'boo +ENDOFCONTENT +EOF + +$expected{ticket10} = <<EOF; +Queue: General +Subject: foo' bar +Owner: root +Content: blah' +ENDOFCONTENT +EOF + +$expected{ticket11} = <<EOF; +Queue: General +Subject: foo, bar +Owner: root +Content: blah +ENDOFCONTENT +EOF + +$expected{ticket12} = <<EOF; +Queue: General +Subject: foo' bar +Owner: root +Content: blah'boo +ENDOFCONTENT +EOF + +$expected{ticket13} = <<EOF; +Queue: General +Subject: foo' bar +Owner: +Content: blah'boo +ENDOFCONTENT +EOF + + +$expected{'ticket14'} = <<EOF; +Queue: General +Subject: +Owner: +Requestor: bobby +EOF +$expected{'ticket15'} = <<EOF; +Queue: General +Subject: +Owner: +Requestor: tommy +EOF +$expected{'ticket16'} = <<EOF; +Queue: General +Subject: +Owner: suzie +Requestor: tommy +EOF +$expected{'ticket17'} = <<EOF; +Queue: General +Subject: Foo "bar" baz +Owner: suzie +Requestor: tommy +EOF +$expected{'ticket18'} = <<EOF; +Queue: General +Subject: Foo "bar" baz +Owner: suzie +Requestor: tommy +EOF +$expected{'ticket19'} = <<EOF; +Queue: General +Subject: 'Foo bar' baz +Owner: suzie +Requestor: tommy +EOF + + + + +$action->Parse(Content =>$commas); +$action->Parse(Content =>$sparse_commas); +$action->Parse(Content => $tabs); + +my %got; +foreach (@{ $action->{'create_tickets'} }) { + $got{$_} = $action->{'templates'}->{$_}; +} + +foreach my $id ( sort keys %expected ) { + ok(exists($got{"create-$id"}), "template exists for $id"); + is($got{"create-$id"}, $expected{$id}, "template is correct for $id"); +} =end testing @@ -300,267 +470,869 @@ perl(1). =cut my %LINKTYPEMAP = ( - MemberOf => { Type => 'MemberOf', - Mode => 'Target', }, - Members => { Type => 'MemberOf', - Mode => 'Base', }, - HasMember => { Type => 'MemberOf', - Mode => 'Base', }, - RefersTo => { Type => 'RefersTo', - Mode => 'Target', }, - ReferredToBy => { Type => 'RefersTo', - Mode => 'Base', }, - DependsOn => { Type => 'DependsOn', - Mode => 'Target', }, - DependedOnBy => { Type => 'DependsOn', - Mode => 'Base', }, + MemberOf => { + Type => 'MemberOf', + Mode => 'Target', + }, + Parents => { + Type => 'MemberOf', + Mode => 'Target', + }, + Members => { + Type => 'MemberOf', + Mode => 'Base', + }, + Children => { + Type => 'MemberOf', + Mode => 'Base', + }, + HasMember => { + Type => 'MemberOf', + Mode => 'Base', + }, + RefersTo => { + Type => 'RefersTo', + Mode => 'Target', + }, + ReferredToBy => { + Type => 'RefersTo', + Mode => 'Base', + }, + DependsOn => { + Type => 'DependsOn', + Mode => 'Target', + }, + DependedOnBy => { + Type => 'DependsOn', + Mode => 'Base', + }, ); # {{{ Scrip methods (Commit, Prepare) -# {{{ sub Commit +# {{{ sub Commit #Do what we need to do and send it out. sub Commit { my $self = shift; - my (@links, @postponed); + + # Create all the tickets we care about + return (1) unless $self->TicketObj->Type eq 'ticket'; + + $self->CreateByTemplate( $self->TicketObj ); + $self->UpdateByTemplate( $self->TicketObj ); + return (1); +} + +# }}} + +# {{{ sub Prepare + +sub Prepare { + my $self = shift; + + unless ( $self->TemplateObj ) { + $RT::Logger->warning("No template object handed to $self\n"); + } + + unless ( $self->TransactionObj ) { + $RT::Logger->warning("No transaction object handed to $self\n"); + + } + + unless ( $self->TicketObj ) { + $RT::Logger->warning("No ticket object handed to $self\n"); + + } + + $self->Parse( Content => $self->TemplateObj->Content, _ActiveContent => 1); + return 1; + +} + +# }}} + +# }}} + +sub CreateByTemplate { + my $self = shift; + my $top = shift; + + $RT::Logger->debug("In CreateByTemplate"); + + my @results; # XXX: cargo cult programming that works. i'll be back. use bytes; - # Create all the tickets we care about - return(1) unless $self->TicketObj->Type eq 'ticket'; - %T::Tickets = (); - foreach my $template_id ( @{ $self->{'template_order'} } ) { - $T::Tickets{'TOP'} = $T::TOP = $self->TicketObj; - $RT::Logger->debug("Workflow: processing $template_id of $T::TOP"); + my $ticketargs; + my ( @links, @postponed ); + foreach my $template_id ( @{ $self->{'create_tickets'} } ) { + $T::Tickets{'TOP'} = $T::TOP = $top if $top; + $RT::Logger->debug("Workflow: processing $template_id of $T::TOP") + if $T::TOP; + + $T::ID = $template_id; + @T::AllID = @{ $self->{'create_tickets'} }; + + ( $T::Tickets{$template_id}, $ticketargs ) = + $self->ParseLines( $template_id, \@links, \@postponed ); + + # Now we have a %args to work with. + # Make sure we have at least the minimum set of + # reasonable data and do our thang + + my ( $id, $transid, $msg ) = + $T::Tickets{$template_id}->Create(%$ticketargs); + + foreach my $res ( split( '\n', $msg ) ) { + push @results, + $T::Tickets{$template_id} + ->loc( "Ticket [_1]", $T::Tickets{$template_id}->Id ) . ': ' + . $res; + } + if ( !$id ) { + if ( $self->TicketObj ) { + $msg = + "Couldn't create related ticket $template_id for " + . $self->TicketObj->Id . " " + . $msg; + } + else { + $msg = "Couldn't create ticket $template_id " . $msg; + } + + $RT::Logger->error($msg); + next; + } - $T::ID = $template_id; - @T::AllID = @{ $self->{'template_order'} }; + $RT::Logger->debug("Assigned $template_id with $id"); + $T::Tickets{$template_id}->SetOriginObj( $self->TicketObj ) + if $self->TicketObj + && $T::Tickets{$template_id}->can('SetOriginObj'); - my $template = Text::Template->new( - TYPE => 'STRING', - SOURCE => $self->{'templates'}->{$template_id} + } + + $self->PostProcess( \@links, \@postponed ); + + return @results; +} + +sub UpdateByTemplate { + my $self = shift; + my $top = shift; + + # XXX: cargo cult programming that works. i'll be back. + use bytes; + + my @results; + %T::Tickets = (); + + my $ticketargs; + my ( @links, @postponed ); + foreach my $template_id ( @{ $self->{'update_tickets'} } ) { + $RT::Logger->debug("Update Workflow: processing $template_id"); + + $T::ID = $template_id; + @T::AllID = @{ $self->{'update_tickets'} }; + + ( $T::Tickets{$template_id}, $ticketargs ) = + $self->ParseLines( $template_id, \@links, \@postponed ); + + # Now we have a %args to work with. + # Make sure we have at least the minimum set of + # reasonable data and do our thang + + my @attribs = qw( + Subject + FinalPriority + Priority + TimeEstimated + TimeWorked + TimeLeft + Status + Queue + Due + Starts + Started + Resolved + ); + + my $id = $template_id; + $id =~ s/update-(\d+).*/$1/; + $T::Tickets{$template_id}->Load($id); + + my $msg; + if ( !$T::Tickets{$template_id}->Id ) { + $msg = "Couldn't update ticket $template_id " . $msg; + + $RT::Logger->error($msg); + next; + } + + my $current = $self->GetBaseTemplate( $T::Tickets{$template_id} ); + + $template_id =~ m/^update-(.*)/; + my $base_id = "base-$1"; + my $base = $self->{'templates'}->{$base_id}; + if ($base) { + $base =~ s/\r//g; + $base =~ s/\n+$//; + $current =~ s/\n+$//; + + # If we have no base template, set what we can. + if ($base ne $current) { + push @results, + "Could not update ticket " + . $T::Tickets{$template_id}->Id + . ": Ticket has changed"; + next; + } + } + push @results, $T::Tickets{$template_id}->Update( + AttributesRef => \@attribs, + ARGSRef => $ticketargs ); - $RT::Logger->debug("Workflow: evaluating\n$self->{templates}{$template_id}"); + push @results, + $self->UpdateWatchers( $T::Tickets{$template_id}, $ticketargs ); + + next unless exists $ticketargs->{'UpdateType'}; + if ( $ticketargs->{'UpdateType'} =~ /^(private|public)$/ ) { + my ( $Transaction, $Description, $Object ) = + $T::Tickets{$template_id}->Comment( + CcMessageTo => $ticketargs->{'Cc'}, + BccMessageTo => $ticketargs->{'Bcc'}, + MIMEObj => $ticketargs->{'MIMEObj'}, + TimeTaken => $ticketargs->{'TimeWorked'} + ); + push( @results, + $T::Tickets{$template_id} + ->loc( "Ticket [_1]", $T::Tickets{$template_id}->id ) . ': ' + . $Description ); + } + elsif ( $ticketargs->{'UpdateType'} eq 'response' ) { + my ( $Transaction, $Description, $Object ) = + $T::Tickets{$template_id}->Correspond( + CcMessageTo => $ticketargs->{'Cc'}, + BccMessageTo => $ticketargs->{'Bcc'}, + MIMEObj => $ticketargs->{'MIMEObj'}, + TimeTaken => $ticketargs->{'TimeWorked'} + ); + push( @results, + $T::Tickets{$template_id} + ->loc( "Ticket [_1]", $T::Tickets{$template_id}->id ) . ': ' + . $Description ); + } + else { + push( @results, + $T::Tickets{$template_id} + ->loc("Update type was neither correspondence nor comment.") + . " " + . $T::Tickets{$template_id}->loc("Update not recorded.") ); + } + } + + $self->PostProcess( \@links, \@postponed ); - my $err; - my $filled_in = $template->fill_in( PACKAGE => 'T', BROKEN => sub { - $err = { @_ }->{error}; - } ); + return @results; +} + +=head2 Parse TEMPLATE_CONTENT, DEFAULT_QUEUE, DEFAULT_REQEUESTOR ACTIVE + +Parse a template from TEMPLATE_CONTENT + +If $active is set to true, then we'll use Text::Template to parse the templates, +allowing you to embed active perl in your templates. + +=cut - $RT::Logger->debug("Workflow: yielding\n$filled_in"); +sub Parse { + my $self = shift; + my %args = ( Content => undef, + Queue => undef, + Requestor => undef, + _ActiveContent => undef, + @_); - if ($err) { - $RT::Logger->error("Ticket creation failed for ".$self->TicketObj->Id." ".$err); - while (my ($k, $v) = each %T::X) { - $RT::Logger->debug("Eliminating $template_id from ${k}'s parents."); - delete $v->{$template_id}; - } - next; + if ($args{'_ActiveContent'}) { + $self->{'UsePerlTextTemplate'} =1; + } else { + + $self->{'UsePerlTextTemplate'} = 0; + } + + my @template_order; + my $template_id; + my ( $queue, $requestor ); + if ( substr( $args{'Content'}, 0, 3 ) eq '===' ) { + $RT::Logger->debug("Line: ==="); + foreach my $line ( split( /\n/, $args{'Content'} ) ) { + $line =~ s/\r$//; + $RT::Logger->debug("Line: $line"); + if ( $line =~ /^===/ ) { + if ( $template_id && !$queue && $args{'Queue'} ) { + $self->{'templates'}->{$template_id} .= "Queue: $args{'Queue'}\n"; + } + if ( $template_id && !$requestor && $args{'Requestor'} ) { + $self->{'templates'}->{$template_id} .= + "Requestor: $args{'Requestor'}\n"; + } + $queue = 0; + $requestor = 0; + } + if ( $line =~ /^===Create-Ticket: (.*)$/ ) { + $template_id = "create-$1"; + $RT::Logger->debug("**** Create ticket: $template_id"); + push @{ $self->{'create_tickets'} }, $template_id; + } + elsif ( $line =~ /^===Update-Ticket: (.*)$/ ) { + $template_id = "update-$1"; + $RT::Logger->debug("**** Update ticket: $template_id"); + push @{ $self->{'update_tickets'} }, $template_id; + } + elsif ( $line =~ /^===Base-Ticket: (.*)$/ ) { + $template_id = "base-$1"; + $RT::Logger->debug("**** Base ticket: $template_id"); + push @{ $self->{'base_tickets'} }, $template_id; + } + elsif ( $line =~ /^===#.*$/ ) { # a comment + next; + } + else { + if ( $line =~ /^Queue:(.*)/i ) { + $queue = 1; + my $value = $1; + $value =~ s/^\s//; + $value =~ s/\s$//; + if ( !$value && $args{'Queue'}) { + $value = $args{'Queue'}; + $line = "Queue: $value"; + } + } + if ( $line =~ /^Requestor:(.*)/i ) { + $requestor = 1; + my $value = $1; + $value =~ s/^\s//; + $value =~ s/\s$//; + if ( !$value && $args{'Requestor'}) { + $value = $args{'Requestor'}; + $line = "Requestor: $value"; + } + } + $self->{'templates'}->{$template_id} .= $line . "\n"; + } + } + if ( $template_id && !$queue && $args{'Queue'} ) { + $self->{'templates'}->{$template_id} .= "Queue: $args{'Queue'}\n"; } + } + elsif ( substr( $args{'Content'}, 0, 2 ) =~ /^id$/i ) { + $RT::Logger->debug("Line: id"); + use Regexp::Common qw(delimited); + my $first = substr( $args{'Content'}, 0, index( $args{'Content'}, "\n" ) ); + $first =~ s/\r$//; + + my $delimiter; + if ( $first =~ /\t/ ) { + $delimiter = "\t"; + } + else { + $delimiter = ','; + } + my @fields = split( /$delimiter/, $first ); + - my %args; - my @lines = ( split ( /\n/, $filled_in ) ); - while ( defined(my $line = shift @lines) ) { - if ( $line =~ /^(.*?):(?:\s+(.*))?$/ ) { - my $value = $2; - my $tag = lc ($1); - $tag =~ s/-//g; - - if (ref($args{$tag})) { #If it's an array, we want to push the value - push @{$args{$tag}}, $value; - } - elsif (defined ($args{$tag})) { #if we're about to get a second value, make it an array - $args{$tag} = [$args{$tag}, $value]; - } - else { #if there's nothing there, just set the value - $args{ $tag } = $value; - } - - if ( $tag eq 'content' ) { #just build up the content - # convert it to an array - $args{$tag} = defined($value) ? [ $value."\n" ] : []; - while ( defined(my $l = shift @lines) ) { - last if ($l =~ /^ENDOFCONTENT\s*$/) ; - push @{$args{'content'}}, $l."\n"; + my $delimiter_re = qr[$delimiter]; + + my $delimited = qr[[^$delimiter]+]; + my $empty = qr[^[$delimiter](?=[$delimiter])]; + my $justquoted = qr[$RE{quoted}]; + + $args{'Content'} = substr( $args{'Content'}, index( $args{'Content'}, "\n" ) + 1 ); + $RT::Logger->debug("First: $first"); + + my $queue; + foreach my $line ( split( /\n/, $args{'Content'} ) ) { + next unless $line; + $RT::Logger->debug("Line: $line"); + + # first item is $template_id + my $i = 0; + my $template_id; + while ($line && $line =~ s/^($justquoted|.*?)(?:$delimiter_re|$)//ix) { + if ( $i == 0 ) { + $queue = 0; + $requestor = 0; + my $tid = $1; + $tid =~ s/^\s//; + $tid =~ s/\s$//; + next unless $tid; + + + if ($tid =~ /^\d+$/) { + $template_id = 'update-' . $tid; + push @{ $self->{'update_tickets'} }, $template_id; + + } elsif ($tid =~ /^#base-(\d+)$/) { + + $template_id = 'base-' . $1; + push @{ $self->{'base_tickets'} }, $template_id; + + } else { + $template_id = 'create-' . $tid; + push @{ $self->{'create_tickets'} }, $template_id; + } + $RT::Logger->debug("template_id: $tid"); + } + else { + my $value = $1; + $value = '' if ( $value =~ /^$delimiter$/ ); + if ($value =~ /^$RE{delimited}{-delim=>qq{\'\"}}$/) { + substr($value,0,1) = ""; + substr($value,-1,1) = ""; + } + my $field = $fields[$i]; + next unless $field; + $field =~ s/^\s//; + $field =~ s/\s$//; + if ( $field =~ /Body/i + || $field =~ /Data/i + || $field =~ /Message/i ) + { + $field = 'Content'; + } + if ( $field =~ /Summary/i ) { + $field = 'Subject'; + } + if ( $field =~ /Queue/i ) { + $queue = 1; + if ( !$value && $args{'Queue'} ) { + $value = $args{'Queue'}; + } + } + if ( $field =~ /Requestor/i ) { + $requestor = 1; + if ( !$value && $args{'Requestor'} ) { + $value = $args{'Requestor'}; } + } + $self->{'templates'}->{$template_id} .= $field . ": "; + $self->{'templates'}->{$template_id} .= $value || ""; + $self->{'templates'}->{$template_id} .= "\n"; + $self->{'templates'}->{$template_id} .= "ENDOFCONTENT\n" + if $field =~ /content/i; } + $i++; } - } + if ( !$queue && $args{'Queue'} ) { + $self->{'templates'}->{$template_id} .= "Queue: $args{'Queue'}\n"; + } + if ( !$requestor && $args{'Requestor'} ) { + $self->{'templates'}->{$template_id} .= + "Requestor: $args{'Requestor'}\n"; + } + } + } +} - foreach my $date qw(due starts started resolved) { - my $dateobj = RT::Date->new($RT::SystemUser); - next unless $args{$date}; - if ($args{$date} =~ /^\d+$/) { - $dateobj->Set(Format => 'unix', Value => $args{$date}); - } else { - $dateobj->Set(Format => 'unknown', Value => $args{$date}); - } - $args{$date} = $dateobj->ISO; - } - my $mimeobj = MIME::Entity->new(); - $mimeobj->build(Type => $args{'contenttype'}, - Data => $args{'content'}); - # Now we have a %args to work with. - # Make sure we have at least the minimum set of - # reasonable data and do our thang - $T::Tickets{$template_id} ||= RT::Ticket->new($RT::SystemUser); - - # Deferred processing - push @links, ( - $T::Tickets{$template_id}, { - DependsOn => $args{'dependson'}, - DependedOnBy => $args{'dependedonby'}, - RefersTo => $args{'refersto'}, - ReferredToBy => $args{'referredtoby'}, - Members => $args{'members'}, - MemberOf => $args{'memberof'}, - } - ); - - push @postponed, ( - # Status is postponed so we don't violate dependencies - $T::Tickets{$template_id}, { - Status => $args{'status'}, - } - ); - - $args{'requestor'} ||= $self->TicketObj->Requestors->MemberEmailAddresses; - - $args{'type'} ||= 'ticket'; - - my %ticketargs = ( Queue => $args{'queue'}, - Subject=> $args{'subject'}, - Status => 'new', - Due => $args{'due'}, - Starts => $args{'starts'}, - Started => $args{'started'}, - Resolved => $args{'resolved'}, - Owner => $args{'owner'}, - Requestor => $args{'requestor'}, - Cc => $args{'cc'}, - AdminCc=> $args{'admincc'}, - TimeWorked =>$args{'timeworked'}, - TimeEstimated =>$args{'timeestimated'}, - TimeLeft =>$args{'timeleft'}, - InitialPriority => $args{'initialpriority'}, - FinalPriority => $args{'finalpriority'}, - Type => $args{'type'}, - MIMEObj => $mimeobj); - - - foreach my $key (keys(%args)) { - $key =~ /^customfield(\d+)$/ or next; - $ticketargs{ "CustomField-" . $1 } = $args{$key}; - } +sub ParseLines { + my $self = shift; + my $template_id = shift; + my $links = shift; + my $postponed = shift; - my ($id, $transid, $msg) = $T::Tickets{$template_id}->Create(%ticketargs); - if (!$id) { - $RT::Logger->error( - "Couldn't create related ticket $template_id for ". - $self->TicketObj->Id." ".$msg - ); - next; - } - $RT::Logger->debug("Assigned $template_id with $id"); - $T::Tickets{$template_id}->SetOriginObj($self->TicketObj) - if $T::Tickets{$template_id}->can('SetOriginObj'); + my $content = $self->{'templates'}->{$template_id}; + + if ( $self->{'UsePerlTextTemplate'} ) { + + $RT::Logger->debug( + "Workflow: evaluating\n$self->{templates}{$template_id}"); + + my $template = Text::Template->new( + TYPE => 'STRING', + SOURCE => $content + ); + + my $err; + $content = $template->fill_in( + PACKAGE => 'T', + BROKEN => sub { + $err = {@_}->{error}; + } + ); + + $RT::Logger->debug("Workflow: yielding\n$content"); + + if ($err) { + $RT::Logger->error( "Ticket creation failed: " . $err ); + while ( my ( $k, $v ) = each %T::X ) { + $RT::Logger->debug( + "Eliminating $template_id from ${k}'s parents."); + delete $v->{$template_id}; + } + next; + } } + + my $TicketObj ||= RT::Ticket->new($self->CurrentUser); + + my %args; + my @lines = ( split( /\n/, $content ) ); + while ( defined( my $line = shift @lines ) ) { + if ( $line =~ /^(.*?):(?:\s+)(.*?)(?:\s*)$/ ) { + my $value = $2; + my $tag = lc($1); + $tag =~ s/-//g; + + if ( ref( $args{$tag} ) ) + { #If it's an array, we want to push the value + push @{ $args{$tag} }, $value; + } + elsif ( defined( $args{$tag} ) ) + { #if we're about to get a second value, make it an array + $args{$tag} = [ $args{$tag}, $value ]; + } + else { #if there's nothing there, just set the value + $args{$tag} = $value; + } - # postprocessing: add links + if ( $tag eq 'content' ) { #just build up the content + # convert it to an array + $args{$tag} = defined($value) ? [ $value . "\n" ] : []; + while ( defined( my $l = shift @lines ) ) { + last if ( $l =~ /^ENDOFCONTENT\s*$/ ); + push @{ $args{'content'} }, $l . "\n"; + } + } + else { - while (my $ticket = shift(@links)) { - $RT::Logger->debug("Handling links for " . $ticket->Id); - my %args = %{shift(@links)}; - - foreach my $type ( keys %LINKTYPEMAP ) { - next unless (defined $args{$type}); - foreach my $link ( - ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) ) - { - if (!exists $T::Tickets{$link}) { - $RT::Logger->debug("Skipping $type link for $link (non-existent)"); - next; - } - $RT::Logger->debug("Building $type link for $link: " . $T::Tickets{$link}->Id); - $link = $T::Tickets{$link}->Id; - - my ( $wval, $wmsg ) = $ticket->AddLink( - Type => $LINKTYPEMAP{$type}->{'Type'}, - $LINKTYPEMAP{$type}->{'Mode'} => $link, - Silent => 1 - ); - - $RT::Logger->warning("AddLink thru $link failed: $wmsg") unless $wval; - # push @non_fatal_errors, $wmsg unless ($wval); - } + # if it's not content, strip leading and trailing spaces + if ( $args{$tag} ) { + $args{$tag} =~ s/^\s+//g; + $args{$tag} =~ s/\s+$//g; + } + } + } + } - } + foreach my $date qw(due starts started resolved) { + my $dateobj = RT::Date->new($self->CurrentUser); + next unless $args{$date}; + if ( $args{$date} =~ /^\d+$/ ) { + $dateobj->Set( Format => 'unix', Value => $args{$date} ); + } + else { + $dateobj->Set( Format => 'unknown', Value => $args{$date} ); + } + $args{$date} = $dateobj->ISO; } - # postponed actions -- Status only, currently - while (my $ticket = shift(@postponed)) { - $RT::Logger->debug("Handling postponed actions for $ticket"); - my %args = %{shift(@postponed)}; + $args{'requestor'} ||= $self->TicketObj->Requestors->MemberEmailAddresses + if $self->TicketObj; + + $args{'type'} ||= 'ticket'; + + my %ticketargs = ( + Queue => $args{'queue'}, + Subject => $args{'subject'}, + Status => 'new', + Due => $args{'due'}, + Starts => $args{'starts'}, + Started => $args{'started'}, + Resolved => $args{'resolved'}, + Owner => $args{'owner'}, + Requestor => $args{'requestor'}, + Cc => $args{'cc'}, + AdminCc => $args{'admincc'}, + TimeWorked => $args{'timeworked'}, + TimeEstimated => $args{'timeestimated'}, + TimeLeft => $args{'timeleft'}, + InitialPriority => $args{'initialpriority'} || 0, + FinalPriority => $args{'finalpriority'} || 0, + Type => $args{'type'}, + ); - $ticket->SetStatus($args{Status}) if defined $args{Status}; + if ($args{content}) { + my $mimeobj = MIME::Entity->new(); + $mimeobj->build( + Type => $args{'contenttype'}, + Data => $args{'content'} + ); + $ticketargs{MIMEObj} = $mimeobj; + $ticketargs{UpdateType} = $args{'updatetype'} if $args{'updatetype'}; } - return(1); + foreach my $key ( keys(%args) ) { + $key =~ /^customfield(\d+)$/ or next; + $ticketargs{ "CustomField-" . $1 } = $args{$key}; + } + + $self->GetDeferred( \%args, $template_id, $links, $postponed ); + + return $TicketObj, \%ticketargs; } -# }}} -# {{{ sub Prepare - -sub Prepare { - my $self = shift; - - unless ($self->TemplateObj) { - $RT::Logger->warning("No template object handed to $self\n"); - } - - unless ($self->TransactionObj) { - $RT::Logger->warning("No transaction object handed to $self\n"); - - } - - unless ($self->TicketObj) { - $RT::Logger->warning("No ticket object handed to $self\n"); - - } - +sub GetDeferred { + my $self = shift; + my $args = shift; + my $id = shift; + my $links = shift; + my $postponed = shift; + + # Deferred processing + push @$links, + ( + $id, + { + DependsOn => $args->{'dependson'}, + DependedOnBy => $args->{'dependedonby'}, + RefersTo => $args->{'refersto'}, + ReferredToBy => $args->{'referredtoby'}, + Children => $args->{'children'}, + Parents => $args->{'parents'}, + } + ); + + push @$postponed, ( + + # Status is postponed so we don't violate dependencies + $id, { Status => $args->{'status'}, } + ); +} - +sub GetUpdateTemplate { + my $self = shift; + my $t = shift; + + my $string; + $string .= "Queue: " . $t->QueueObj->Name . "\n"; + $string .= "Subject: " . $t->Subject . "\n"; + $string .= "Status: " . $t->Status . "\n"; + $string .= "UpdateType: response\n"; + $string .= "Content: \n"; + $string .= "ENDOFCONTENT\n"; + $string .= "Due: " . $t->DueObj->AsString . "\n"; + $string .= "Starts: " . $t->StartsObj->AsString . "\n"; + $string .= "Started: " . $t->StartedObj->AsString . "\n"; + $string .= "Resolved: " . $t->ResolvedObj->AsString . "\n"; + $string .= "Owner: " . $t->OwnerObj->Name . "\n"; + $string .= "Requestor: " . $t->RequestorAddresses . "\n"; + $string .= "Cc: " . $t->CcAddresses . "\n"; + $string .= "AdminCc: " . $t->AdminCcAddresses . "\n"; + $string .= "TimeWorked: " . $t->TimeWorked . "\n"; + $string .= "TimeEstimated: " . $t->TimeEstimated . "\n"; + $string .= "TimeLeft: " . $t->TimeLeft . "\n"; + $string .= "InitialPriority: " . $t->Priority . "\n"; + $string .= "FinalPriority: " . $t->FinalPriority . "\n"; + + foreach my $type ( sort keys %LINKTYPEMAP ) { + + # don't display duplicates + if ( $type eq "HasMember" + || $type eq "Members" + || $type eq "MemberOf" ) + { + next; + } + $string .= "$type: "; + + my $mode = $LINKTYPEMAP{$type}->{Mode}; + my $method = $LINKTYPEMAP{$type}->{Type}; + + my $links; + while ( my $link = $t->$method->Next ) { + $links .= ", " if $links; + + my $object = $mode . "Obj"; + my $member = $link->$object; + $links .= $member->Id if $member; + } + $string .= $links; + $string .= "\n"; + } -my $template_id; -foreach my $line (split(/\n/,$self->TemplateObj->Content)) { - if ($line =~ /^===Create-Ticket: (.*)$/) { - $template_id = $1; - push @{$self->{'template_order'}},$template_id; - } else { - $self->{'templates'}->{$template_id} .= $line."\n"; - } - - + return $string; } - - return 1; - + +sub GetBaseTemplate { + my $self = shift; + my $t = shift; + + my $string; + $string .= "Queue: " . $t->Queue . "\n"; + $string .= "Subject: " . $t->Subject . "\n"; + $string .= "Status: " . $t->Status . "\n"; + $string .= "Due: " . $t->DueObj->Unix . "\n"; + $string .= "Starts: " . $t->StartsObj->Unix . "\n"; + $string .= "Started: " . $t->StartedObj->Unix . "\n"; + $string .= "Resolved: " . $t->ResolvedObj->Unix . "\n"; + $string .= "Owner: " . $t->Owner . "\n"; + $string .= "Requestor: " . $t->RequestorAddresses . "\n"; + $string .= "Cc: " . $t->CcAddresses . "\n"; + $string .= "AdminCc: " . $t->AdminCcAddresses . "\n"; + $string .= "TimeWorked: " . $t->TimeWorked . "\n"; + $string .= "TimeEstimated: " . $t->TimeEstimated . "\n"; + $string .= "TimeLeft: " . $t->TimeLeft . "\n"; + $string .= "InitialPriority: " . $t->Priority . "\n"; + $string .= "FinalPriority: " . $t->FinalPriority . "\n"; + + return $string; } -# }}} +sub GetCreateTemplate { + my $self = shift; -# }}} + my $string; + + $string .= "Queue: General\n"; + $string .= "Subject: \n"; + $string .= "Status: new\n"; + $string .= "Content: \n"; + $string .= "ENDOFCONTENT\n"; + $string .= "Due: \n"; + $string .= "Starts: \n"; + $string .= "Started: \n"; + $string .= "Resolved: \n"; + $string .= "Owner: \n"; + $string .= "Requestor: \n"; + $string .= "Cc: \n"; + $string .= "AdminCc:\n"; + $string .= "TimeWorked: \n"; + $string .= "TimeEstimated: \n"; + $string .= "TimeLeft: \n"; + $string .= "InitialPriority: \n"; + $string .= "FinalPriority: \n"; + + foreach my $type ( keys %LINKTYPEMAP ) { + + # don't display duplicates + if ( $type eq "HasMember" + || $type eq 'Members' + || $type eq 'MemberOf' ) + { + next; + } + $string .= "$type: \n"; + } + return $string; +} + +sub UpdateWatchers { + my $self = shift; + my $ticket = shift; + my $args = shift; + + my @results; + + foreach my $type qw(Requestor Cc AdminCc) { + my $method = $type . 'Addresses'; + my $oldaddr = $ticket->$method; + + + # Skip unless we have a defined field + next unless defined $args->{$type}; + my $newaddr = $args->{$type}; + + my @old = split( ', ', $oldaddr ); + my @new = split( ', ', $newaddr ); + my %oldhash = map { $_ => 1 } @old; + my %newhash = map { $_ => 1 } @new; + + my @add = grep( !defined $oldhash{$_}, @new ); + my @delete = grep( !defined $newhash{$_}, @old ); + + foreach (@add) { + my ( $val, $msg ) = $ticket->AddWatcher( + Type => $type, + Email => $_ + ); + + push @results, + $ticket->loc( "Ticket [_1]", $ticket->Id ) . ': ' . $msg; + } + + foreach (@delete) { + my ( $val, $msg ) = $ticket->DeleteWatcher( + Type => $type, + Email => $_ + ); + push @results, + $ticket->loc( "Ticket [_1]", $ticket->Id ) . ': ' . $msg; + } + } + return @results; +} + +sub PostProcess { + my $self = shift; + my $links = shift; + my $postponed = shift; + + # postprocessing: add links + + while ( my $template_id = shift(@$links) ) { + my $ticket = $T::Tickets{$template_id}; + $RT::Logger->debug( "Handling links for " . $ticket->Id ); + my %args = %{ shift(@$links) }; + + foreach my $type ( keys %LINKTYPEMAP ) { + next unless ( defined $args{$type} ); + foreach my $link ( + ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) ) + { + next unless $link; + + if ($link =~ /^TOP$/i) { + $RT::Logger->debug( "Building $type link for $link: " . $T::Tickets{TOP}->Id ); + $link = $T::Tickets{TOP}->Id; + + } + elsif ( $link !~ m/^\d+$/ ) { + my $key = "create-$link"; + if ( !exists $T::Tickets{$key} ) { + $RT::Logger->debug( "Skipping $type link for $key (non-existent)"); + next; + } + $RT::Logger->debug( "Building $type link for $link: " . $T::Tickets{$key}->Id ); + $link = $T::Tickets{$key}->Id; + } + else { + $RT::Logger->debug("Building $type link for $link"); + } + + my ( $wval, $wmsg ) = $ticket->AddLink( + Type => $LINKTYPEMAP{$type}->{'Type'}, + $LINKTYPEMAP{$type}->{'Mode'} => $link, + Silent => 1 + ); + + $RT::Logger->warning("AddLink thru $link failed: $wmsg") + unless $wval; + + # push @non_fatal_errors, $wmsg unless ($wval); + } + + } + } + + # postponed actions -- Status only, currently + while ( my $template_id = shift(@$postponed) ) { + my $ticket = $T::Tickets{$template_id}; + $RT::Logger->debug("Handling postponed actions for ".$ticket->id); + my %args = %{ shift(@$postponed) }; + $ticket->SetStatus( $args{Status} ) if defined $args{Status}; + } + +} eval "require RT::Action::CreateTickets_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Vendor.pm}); +die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Vendor.pm} ); eval "require RT::Action::CreateTickets_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Local.pm}); +die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Local.pm} ); 1; diff --git a/rt/lib/RT/Action/EscalatePriority.pm b/rt/lib/RT/Action/EscalatePriority.pm index e24e0541a..ace72dd1b 100644 --- a/rt/lib/RT/Action/EscalatePriority.pm +++ b/rt/lib/RT/Action/EscalatePriority.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK =head1 NAME RT::Action::EscalatePriority diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm index 007d299c7..5e80f40d7 100755 --- a/rt/lib/RT/Action/Generic.pm +++ b/rt/lib/RT/Action/Generic.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: # +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # -# END LICENSE BLOCK +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK =head1 NAME RT::Action::Generic - a generic baseclass for RT Actions @@ -44,6 +66,9 @@ ok (require RT::Action::Generic); package RT::Action::Generic; use strict; +use Scalar::Util; + +use base qw/RT::Base/; # {{{ sub new sub new { @@ -56,13 +81,6 @@ sub new { } # }}} -# {{{ sub new -sub loc { - my $self = shift; - return $self->{'ScripObj'}->loc(@_); -} -# }}} - # {{{ sub _Init sub _Init { my $self = shift; @@ -72,6 +90,7 @@ sub _Init { TemplateObj => undef, Argument => undef, Type => undef, + CurrentUser => undef, @_ ); @@ -81,6 +100,16 @@ sub _Init { $self->{'TransactionObj'} = $args{'TransactionObj'}; $self->{'TemplateObj'} = $args{'TemplateObj'}; $self->{'Type'} = $args{'Type'}; + $self->CurrentUser( $args{'CurrentUser'}); + Scalar::Util::weaken($self->{'ScripObj'}); + Scalar::Util::weaken($self->{'TicketObj'}); + Scalar::Util::weaken($self->{'TemplateObj'}); + Scalar::Util::weaken($self->{'TransactionObj'}); + + + + + } # }}} diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm index 1e4e4c073..4131a8c68 100755 --- a/rt/lib/RT/Action/Notify.pm +++ b/rt/lib/RT/Action/Notify.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,20 +20,51 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK +# package RT::Action::Notify; require RT::Action::SendEmail; - +use Mail::Address; use strict; use vars qw/@ISA/; @ISA = qw(RT::Action::SendEmail); + +=head2 Prepare + +Set up the relevant recipients, then call our parent. + +=cut + + +sub Prepare { + my $self = shift; + $self->SetRecipients(); + $self->SUPER::Prepare(); +} + # {{{ sub SetRecipients =head2 SetRecipients @@ -47,10 +84,18 @@ sub SetRecipients { my ( @To, @PseudoTo, @Cc, @Bcc ); - if ($arg =~ /\bOtherRecipients\b/) { - if ($self->TransactionObj->Attachments->First) { - push (@Cc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Cc')); - push (@Bcc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Bcc')); + if ( $arg =~ /\bOtherRecipients\b/ ) { + if ( $self->TransactionObj->Attachments->First ) { + my @cc_addresses = Mail::Address->parse($self->TransactionObj->Attachments->First->GetHeader('RT-Send-Cc')); + foreach my $addr (@cc_addresses) { + push @Cc, $addr->address; + } + my @bcc_addresses = Mail::Address->parse($self->TransactionObj->Attachments->First->GetHeader('RT-Send-Bcc')); + + foreach my $addr (@bcc_addresses) { + push @Bcc, $addr->address; + } + } } @@ -118,7 +163,7 @@ sub SetRecipients { @{ $self->{'Bcc'} } = grep ( !/^$creator$/, @Bcc ); } @{ $self->{'PseudoTo'} } = @PseudoTo; - return (1); + } diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm index 210e4ab15..475e3555f 100755 --- a/rt/lib/RT/Action/NotifyAsComment.pm +++ b/rt/lib/RT/Action/NotifyAsComment.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK package RT::Action::NotifyAsComment; require RT::Action::Notify; diff --git a/rt/lib/RT/Action/RecordComment.pm b/rt/lib/RT/Action/RecordComment.pm new file mode 100644 index 000000000..b548a26eb --- /dev/null +++ b/rt/lib/RT/Action/RecordComment.pm @@ -0,0 +1,142 @@ +# {{{ BEGIN BPS TAGGED BLOCK +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# +# 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. +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK +# 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 +# +package RT::Action::RecordComment; +require RT::Action::Generic; +use strict; +use vars qw/@ISA/; +@ISA = qw(RT::Action::Generic); + +=head1 NAME + +RT::Action::RecordComment - An Action which can be used from an +external tool, or in any situation where a ticket transaction has not +been started, to make a comment on the ticket. + +=head1 SYNOPSIS + +my $action_obj = RT::Action::RecordComment->new('TicketObj' => $ticket_obj, + 'TemplateObj' => $template_obj, + ); +my $result = $action_obj->Prepare(); +$action_obj->Commit() if $result; + +=head1 METHODS + +=head2 Prepare + +Check for the existence of a Transaction. If a Transaction already +exists, and is of type "Comment" or "Correspond", abort because that +will give us a loop. + +=cut + + +sub Prepare { + my $self = shift; + if (defined $self->{'TransactionObj'} && + $self->{'TransactionObj'}->Type =~ /^(Comment|Correspond)$/) { + return undef; + } + return 1; +} + +=head2 Commit + +Create a Transaction by calling the ticket's Comment method on our +parsed Template, which may have an RT-Send-Cc or RT-Send-Bcc header. +The Transaction will be of type Comment. This Transaction can then be +used by the scrips that actually send the email. + +=cut + +sub Commit { + my $self = shift; + $self->CreateTransaction(); +} + +sub CreateTransaction { + my $self = shift; + + my ($result, $msg) = $self->{'TemplateObj'}->Parse( + TicketObj => $self->{'TicketObj'}); + return undef unless $result; + + my ($trans, $desc, $transaction) = $self->{'TicketObj'}->Comment( + MIMEObj => $self->TemplateObj->MIMEObj); + $self->{'TransactionObj'} = $transaction; +} + + +eval "require RT::Action::RecordComment_Vendor"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordComment_Vendor.pm}); +eval "require RT::Action::RecordComment_Local"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordComment_Local.pm}); + +1; diff --git a/rt/lib/RT/Action/RecordCorrespondence.pm b/rt/lib/RT/Action/RecordCorrespondence.pm new file mode 100644 index 000000000..c6770c574 --- /dev/null +++ b/rt/lib/RT/Action/RecordCorrespondence.pm @@ -0,0 +1,143 @@ +# {{{ BEGIN BPS TAGGED BLOCK +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# +# 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. +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK +# 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 +# +package RT::Action::RecordCorrespondence; +require RT::Action::Generic; +use strict; +use vars qw/@ISA/; +@ISA = qw(RT::Action::Generic); + +=head1 NAME + +RT::Action::RecordCorrespondence - An Action which can be used from an +external tool, or in any situation where a ticket transaction has not +been started, to make a comment on the ticket. + +=head1 SYNOPSIS + +my $action_obj = RT::Action::RecordCorrespondence->new( + 'TicketObj' => $ticket_obj, + 'TemplateObj' => $template_obj, + ); +my $result = $action_obj->Prepare(); +$action_obj->Commit() if $result; + +=head1 METHODS + +=head2 Prepare + +Check for the existence of a Transaction. If a Transaction already +exists, and is of type "Comment" or "Correspond", abort because that +will give us a loop. + +=cut + + +sub Prepare { + my $self = shift; + if (defined $self->{'TransactionObj'} && + $self->{'TransactionObj'}->Type =~ /^(Comment|Correspond)$/) { + return undef; + } + return 1; +} + +=head2 Commit + +Create a Transaction by calling the ticket's Correspond method on our +parsed Template, which may have an RT-Send-Cc or RT-Send-Bcc header. +The Transaction will be of type Correspond. This Transaction can then +be used by the scrips that actually send the email. + +=cut + +sub Commit { + my $self = shift; + $self->CreateTransaction(); +} + +sub CreateTransaction { + my $self = shift; + + my ($result, $msg) = $self->{'TemplateObj'}->Parse( + TicketObj => $self->{'TicketObj'}); + return undef unless $result; + + my ($trans, $desc, $transaction) = $self->{'TicketObj'}->Correspond( + MIMEObj => $self->TemplateObj->MIMEObj); + $self->{'TransactionObj'} = $transaction; +} + + +eval "require RT::Action::RecordCorrespondence_Vendor"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordCorrespondence_Vendor.pm}); +eval "require RT::Action::RecordCorrespondence_Local"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordCorrespondence_Local.pm}); + +1; diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm index 02ff3a58c..4d751eb6d 100644 --- a/rt/lib/RT/Action/ResolveMembers.pm +++ b/rt/lib/RT/Action/ResolveMembers.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK # This Action will resolve all members of a resolved group ticket package RT::Action::ResolveMembers; diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm index 645c5d99d..a85c169b8 100755 --- a/rt/lib/RT/Action/SendEmail.pm +++ b/rt/lib/RT/Action/SendEmail.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: # +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # -# END LICENSE BLOCK +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# }}} END BPS TAGGED BLOCK # Portions Copyright 2000 Tobias Brox <tobix@cpan.org> package RT::Action::SendEmail; @@ -33,6 +55,7 @@ use vars qw/@ISA/; use MIME::Words qw(encode_mimeword); use RT::EmailParser; +use Mail::Address; =head1 NAME @@ -51,13 +74,6 @@ RT::Action::AutoReply is a good example subclass. Basically, you create another module RT::Action::YourAction which ISA RT::Action::SendEmail. -If you want to set the recipients of the mail to something other than -the addresses mentioned in the To, Cc, Bcc and headers in -the template, you should subclass RT::Action::SendEmail and override -either the SetRecipients method or the SetTo, SetCc, etc methods (see -the comments for the SetRecipients sub). - - =begin testing ok (require RT::Action::SendEmail); @@ -77,165 +93,127 @@ perl(1). # {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable) -# {{{ sub _Init -# We use _Init from RT::Action -# }}} # {{{ sub Commit -#Do what we need to do and send it out. + sub Commit { my $self = shift; - my $MIMEObj = $self->TemplateObj->MIMEObj; - my $msgid = $MIMEObj->head->get('Message-Id'); - chomp $msgid; - $RT::Logger->info($msgid." #".$self->TicketObj->id."/".$self->TransactionObj->id." - Scrip ". $self->ScripObj->id ." ".$self->ScripObj->Description); - #send the email + return($self->SendMessage($self->TemplateObj->MIMEObj)); +} - # Weed out any RT addresses. We really don't want to talk to ourselves! - @{$self->{'To'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'To'}}); - @{$self->{'Cc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Cc'}}); - @{$self->{'Bcc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Bcc'}}); - # If there are no recipients, don't try to send the message. - # If the transaction has content and has the header RT-Squelch-Replies-To +# }}} - if ( defined $self->TransactionObj->Attachments->First() ) { +# {{{ sub Prepare - my $squelch = $self->TransactionObj->Attachments->First->GetHeader( 'RT-Squelch-Replies-To'); +sub Prepare { + my $self = shift; - if ($squelch) { - my @blacklist = split ( /,/, $squelch ); - - # Cycle through the people we're sending to and pull out anyone on the - # system blacklist - - foreach my $person_to_yank (@blacklist) { - $person_to_yank =~ s/\s//g; - @{ $self->{'To'} } = - grep ( !/^$person_to_yank$/, @{ $self->{'To'} } ); - @{ $self->{'Cc'} } = - grep ( !/^$person_to_yank$/, @{ $self->{'Cc'} } ); - @{ $self->{'Bcc'} } = - grep ( !/^$person_to_yank$/, @{ $self->{'Bcc'} } ); - } - } + my ( $result, $message ) = $self->TemplateObj->Parse( + Argument => $self->Argument, + TicketObj => $self->TicketObj, + TransactionObj => $self->TransactionObj + ); + if ( !$result ) { + return (undef); } + my $MIMEObj = $self->TemplateObj->MIMEObj; + + # Header + $self->SetRTSpecialHeaders(); + + $self->RemoveInappropriateRecipients(); + # Go add all the Tos, Ccs and Bccs that we need to to the message to # make it happy, but only if we actually have values in those arrays. - $self->SetHeader( 'To', join ( ',', @{ $self->{'To'} } ) ) - if ( $self->{'To'} && @{ $self->{'To'} } ); - $self->SetHeader( 'Cc', join ( ',', @{ $self->{'Cc'} } ) ) - if ( $self->{'Cc'} && @{ $self->{'Cc'} } ); - $self->SetHeader( 'Bcc', join ( ',', @{ $self->{'Bcc'} } ) ) - if ( $self->{'Bcc'} && @{ $self->{'Bcc'} } ); + # TODO: We should be pulling the recipients out of the template and shove them into To, Cc and Bcc + + $self->SetHeader( 'To', join ( ', ', @{ $self->{'To'} } ) ) + if ( ! $MIMEObj->head->get('To') && $self->{'To'} && @{ $self->{'To'} } ); + $self->SetHeader( 'Cc', join ( ', ', @{ $self->{'Cc'} } ) ) + if ( !$MIMEObj->head->get('Cc') && $self->{'Cc'} && @{ $self->{'Cc'} } ); + $self->SetHeader( 'Bcc', join ( ', ', @{ $self->{'Bcc'} } ) ) + if ( !$MIMEObj->head->get('Bcc') && $self->{'Bcc'} && @{ $self->{'Bcc'} } ); + # PseudoTo (fake to headers) shouldn't get matched for message recipients. + # If we don't have any 'To' header (but do have other recipients), drop in + # the pseudo-to header. + $self->SetHeader( 'To', join ( ', ', @{ $self->{'PseudoTo'} } ) ) + if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } ) + and ( !$MIMEObj->head->get('To') ) ) and ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc')); - $self->SetHeader('MIME-Version', '1.0'); + # We should never have to set the MIME-Version header + $self->SetHeader( 'MIME-Version', '1.0' ); # try to convert message body from utf-8 to $RT::EmailOutputEncoding $self->SetHeader( 'Content-Type', 'text/plain; charset="utf-8"' ); - RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, 'mime_words_ok' ); + RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, + 'mime_words_ok' ); $self->SetHeader( 'Content-Type', 'text/plain; charset="' . $RT::EmailOutputEncoding . '"' ); - # Build up a MIME::Entity that looks like the original message. + $self->AddAttachments() if ( $MIMEObj->head->get('RT-Attach-Message') ); - my $do_attach = $self->TemplateObj->MIMEObj->head->get('RT-Attach-Message'); - - if ($do_attach) { - $self->TemplateObj->MIMEObj->head->delete('RT-Attach-Message'); - - my $attachments = RT::Attachments->new($RT::SystemUser); - $attachments->Limit( FIELD => 'TransactionId', - VALUE => $self->TransactionObj->Id ); - $attachments->OrderBy('id'); + return $result; - my $transaction_content_obj = $self->TransactionObj->ContentObj; +} - # attach any of this transaction's attachments - while ( my $attach = $attachments->Next ) { +# }}} - # Don't attach anything blank - next unless ( $attach->ContentLength ); +# }}} - # We want to make sure that we don't include the attachment that's being sued as the "Content" of this message" - next - if ( $transaction_content_obj - && $transaction_content_obj->Id == $attach->Id - && $transaction_content_obj->ContentType =~ qr{text/plain}i - ); - $MIMEObj->make_multipart('mixed'); - $MIMEObj->attach( Type => $attach->ContentType, - Charset => $attach->OriginalEncoding, - Data => $attach->OriginalContent, - Filename => $self->MIMEEncodeString( $attach->Filename, $RT::EmailOutputEncoding ), - Encoding => '-SUGGEST'); - } - } +=head2 To - my $retval = $self->SendMessage($MIMEObj); +Returns an array of Mail::Address objects containing all the To: recipients for this notification +=cut - return ($retval); +sub To { + my $self = shift; + return ($self->_AddressesFromHeader('To')); } -# }}} +=head2 Cc -# {{{ sub Prepare +Returns an array of Mail::Address objects containing all the Cc: recipients for this notification -sub Prepare { - my $self = shift; +=cut - # This actually populates the MIME::Entity fields in the Template Object +sub Cc { + my $self = shift; + return ($self->_AddressesFromHeader('Cc')); +} - unless ( $self->TemplateObj ) { - $RT::Logger->warning("No template object handed to $self\n"); - } +=head2 Bcc - unless ( $self->TransactionObj ) { - $RT::Logger->warning("No transaction object handed to $self\n"); +Returns an array of Mail::Address objects containing all the Bcc: recipients for this notification - } +=cut - unless ( $self->TicketObj ) { - $RT::Logger->warning("No ticket object handed to $self\n"); - } +sub Bcc { + my $self = shift; + return ($self->_AddressesFromHeader('Bcc')); - my ( $result, $message ) = $self->TemplateObj->Parse( - Argument => $self->Argument, - TicketObj => $self->TicketObj, - TransactionObj => $self->TransactionObj - ); - if ($result) { - - # Header - $self->SetSubject(); - $self->SetSubjectToken(); - $self->SetRecipients(); - $self->SetReturnAddress(); - $self->SetRTSpecialHeaders(); - if ($RT::EmailOutputEncoding) { - - # l10n related header - $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding ); - } - } +} - return $result; +sub _AddressesFromHeader { + my $self = shift; + my $field = shift; + my $header = $self->TemplateObj->MIMEObj->head->get($field); + my @addresses = Mail::Address->parse($header); + return (@addresses); } -# }}} - -# }}} # {{{ SendMessage + =head2 SendMessage MIMEObj sends the message using RT's preferred API. @@ -244,61 +222,72 @@ TODO: Break this out to a separate module =cut sub SendMessage { - my $self = shift; + my $self = shift; my $MIMEObj = shift; my $msgid = $MIMEObj->head->get('Message-Id'); + chomp $msgid; + $RT::Logger->info( $msgid . " #" + . $self->TicketObj->id . "/" + . $self->TransactionObj->id + . " - Scrip " + . $self->ScripObj->id . " " + . $self->ScripObj->Description ); #If we don't have any recipients to send to, don't send a message; - unless ( $MIMEObj->head->get('To') - || $MIMEObj->head->get('Cc') - || $MIMEObj->head->get('Bcc') ) { - $RT::Logger->info($msgid. " No recipients found. Not sending.\n"); + unless ( $MIMEObj->head->get('To') + || $MIMEObj->head->get('Cc') + || $MIMEObj->head->get('Bcc') ) + { + $RT::Logger->info( $msgid . " No recipients found. Not sending.\n" ); return (1); } - # PseudoTo (fake to headers) shouldn't get matched for message recipients. - # If we don't have any 'To' header, drop in the pseudo-to header. - $self->SetHeader( 'To', join ( ',', @{ $self->{'PseudoTo'} } ) ) - if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } ) - and ( !$MIMEObj->head->get('To') ) ); if ( $RT::MailCommand eq 'sendmailpipe' ) { eval { open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" ) || die $!; print MAIL $MIMEObj->as_string; close(MAIL); - }; - if ($@) { - $RT::Logger->crit($msgid. "Could not send mail. -".$@ ); + }; + if ($@) { + $RT::Logger->crit( $msgid . "Could not send mail. -" . $@ ); } } else { - my @mailer_args = ($RT::MailCommand); - local $ENV{MAILADDRESS}; + my @mailer_args = ($RT::MailCommand); + + local $ENV{MAILADDRESS}; if ( $RT::MailCommand eq 'sendmail' ) { - push @mailer_args, split(/\s+/, $RT::SendmailArguments); + push @mailer_args, split(/\s+/, $RT::SendmailArguments); } elsif ( $RT::MailCommand eq 'smtp' ) { - $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From'); - push @mailer_args, (Server => $RT::SMTPServer); - push @mailer_args, (Debug => $RT::SMTPDebug); + $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From'); + push @mailer_args, ( Server => $RT::SMTPServer ); + push @mailer_args, ( Debug => $RT::SMTPDebug ); + } + else { + push @mailer_args, $RT::MailParams; } - else { - push @mailer_args, $RT::MailParams; - } - unless ( $MIMEObj->send( @mailer_args ) ) { - $RT::Logger->crit($msgid. "Could not send mail." ); + unless ( $MIMEObj->send(@mailer_args) ) { + $RT::Logger->crit( $msgid . "Could not send mail." ); return (0); } } - - my $success = ($msgid. " sent To: ".$MIMEObj->head->get('To') . " Cc: ".$MIMEObj->head->get('Cc') . " Bcc: ".$MIMEObj->head->get('Bcc')); + my $success = + ( $msgid + . " sent To: " + . $MIMEObj->head->get('To') . " Cc: " + . $MIMEObj->head->get('Cc') . " Bcc: " + . $MIMEObj->head->get('Bcc') ); $success =~ s/\n//gi; + + $self->RecordOutgoingMailTransaction($MIMEObj) if ($RT::RecordOutgoingEmail); + $RT::Logger->info($success); return (1); @@ -306,51 +295,139 @@ sub SendMessage { # }}} -# {{{ Deal with message headers (Set* subs, designed for easy overriding) +# {{{ AddAttachments -# {{{ sub SetRTSpecialHeaders +=head2 AddAttachments -=head2 SetRTSpecialHeaders +Takes any attachments to this transaction and attaches them to the message +we're building. + +=cut -This routine adds all the random headers that RT wants in a mail message -that don't matter much to anybody else. + +sub AddAttachments { + my $self = shift; + + my $MIMEObj = $self->TemplateObj->MIMEObj; + + $MIMEObj->head->delete('RT-Attach-Message'); + + my $attachments = RT::Attachments->new($RT::SystemUser); + $attachments->Limit( + FIELD => 'TransactionId', + VALUE => $self->TransactionObj->Id + ); + $attachments->OrderBy('id'); + + my $transaction_content_obj = $self->TransactionObj->ContentObj; + + # attach any of this transaction's attachments + while ( my $attach = $attachments->Next ) { + + # Don't attach anything blank + next unless ( $attach->ContentLength ); + +# We want to make sure that we don't include the attachment that's being sued as the "Content" of this message" + next + if ( $transaction_content_obj + && $transaction_content_obj->Id == $attach->Id + && $transaction_content_obj->ContentType =~ qr{text/plain}i ); + $MIMEObj->make_multipart('mixed'); + $MIMEObj->attach( + Type => $attach->ContentType, + Charset => $attach->OriginalEncoding, + Data => $attach->OriginalContent, + Filename => $self->MIMEEncodeString( $attach->Filename, + $RT::EmailOutputEncoding ), + 'RT-Attachment:' => $self->TicketObj->Id."/".$self->TransactionObj->Id."/".$attach->id, + Encoding => '-SUGGEST' + ); + } + +} + +# }}} + +# {{{ RecordOutgoingMailTransaction + +=head2 RecordOutgoingMailTransaction MIMEObj + +Record a transaction in RT with this outgoing message for future record-keeping purposes =cut -sub SetRTSpecialHeaders { + + +sub RecordOutgoingMailTransaction { my $self = shift; + my $MIMEObj = shift; + + + my @parts = $MIMEObj->parts; + my @attachments; + my @keep; + foreach my $part (@parts) { + my $attach = $part->head->get('RT-Attachment'); + if ($attach) { + $RT::Logger->debug("We found an attachment. we want to not record it."); + push @attachments, $attach; + } else { + $RT::Logger->debug("We found a part. we want to record it."); + push @keep, $part; + } + } + $MIMEObj->parts(\@keep); + foreach my $attachment (@attachments) { + $MIMEObj->head->add('RT-Attachment', $attachment); + } - $self->SetReferences(); + RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, 'utf-8', 'mime_words_ok' ); - $self->SetMessageID(); + my $transaction = RT::Transaction->new($self->TransactionObj->CurrentUser); - $self->SetPrecedence(); + # XXX: TODO -> Record attachments as references to things in the attachments table, maybe. - $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname ); - $self->SetHeader( 'RT-Ticket', - $RT::rtname . " #" . $self->TicketObj->id() ); - $self->SetHeader( 'Managed-by', - "RT $RT::VERSION (http://www.bestpractical.com/rt/)" ); + my $type; + if ($self->TransactionObj->Type eq 'Comment') { + $type = 'CommentEmailRecord'; + } else { + $type = 'EmailRecord'; + } + + + + my ( $id, $msg ) = $transaction->Create( + Ticket => $self->TicketObj->Id, + Type => $type, + Data => $MIMEObj->head->get('Message-Id'), + MIMEObj => $MIMEObj, + ActivateScrips => 0 + ); - $self->SetHeader( 'RT-Originator', - $self->TransactionObj->CreatorObj->EmailAddress ); - return (); } -# {{{ sub SetReferences +# }}} +# + +# {{{ sub SetRTSpecialHeaders + +=head2 SetRTSpecialHeaders -=head2 SetReferences - - # This routine will set the References: and In-Reply-To headers, -# autopopulating it with all the correspondence on this ticket so -# far. This should make RT responses threadable. +This routine adds all the random headers that RT wants in a mail message +that don't matter much to anybody else. =cut -sub SetReferences { +sub SetRTSpecialHeaders { my $self = shift; + $self->SetSubject(); + $self->SetSubjectToken(); + $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding ) + if ($RT::EmailOutputEncoding); + $self->SetReturnAddress(); + # TODO: this one is broken. What is this email really a reply to? # If it's a reply to an incoming message, we'll need to use the # actual message-id from the appropriate Attachment object. For @@ -358,46 +435,93 @@ sub SetReferences { # References. $self->SetHeader( 'In-Reply-To', - "<rt-" . $self->TicketObj->id() . "\@" . $RT::rtname . ">" ); + "<rt-" . $self->TicketObj->id() . "\@" . $RT::rtname . ">" ); # TODO We should always add References headers for all message-ids # of previous messages related to this ticket. + + $self->SetHeader( 'Message-ID', + "<rt-" + . $RT::VERSION . "-" + . $self->TicketObj->id() . "-" + . $self->TransactionObj->id() . "-" + . $self->ScripObj->Id . "." + . rand(20) . "\@" + . $RT::Organization . ">" ) + unless $self->TemplateObj->MIMEObj->head->get('Message-ID'); + + $self->SetHeader( 'Precedence', "bulk" ) + unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ); + + $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname ); + $self->SetHeader( 'RT-Ticket', + $RT::rtname . " #" . $self->TicketObj->id() ); + $self->SetHeader( 'Managed-by', + "RT $RT::VERSION (http://www.bestpractical.com/rt/)" ); + + $self->SetHeader( 'RT-Originator', + $self->TransactionObj->CreatorObj->EmailAddress ); + } # }}} -# {{{ sub SetMessageID -=head2 SetMessageID +# }}} -Without this one, threading won't work very nice in email agents. -Anyway, I'm not really sure it's that healthy if we need to send -several separate/different emails about the same transaction. +# {{{ RemoveInappropriateRecipients + +=head2 RemoveInappropriateRecipients + +Remove addresses that are RT addresses or that are on this transaction's blacklist =cut -sub SetMessageID { +sub RemoveInappropriateRecipients { my $self = shift; - # TODO this one might be sort of broken. If we have several scrips +++ - # sending several emails to several different persons, we need to - # pull out different message-ids. I'd suggest message ids like - # "rt-ticket#-transaction#-scrip#-receipient#" + my @blacklist; - $self->SetHeader( 'Message-ID', - "<rt-" - . $RT::VERSION ."-" - . $self->TicketObj->id() . "-" - . $self->TransactionObj->id() . "." - . rand(20) . "\@" - . $RT::Organization . ">" ) - unless $self->TemplateObj->MIMEObj->head->get('Message-ID'); -} + # Weed out any RT addresses. We really don't want to talk to ourselves! + @{ $self->{'To'} } = + RT::EmailParser::CullRTAddresses( "", @{ $self->{'To'} } ); + @{ $self->{'Cc'} } = + RT::EmailParser::CullRTAddresses( "", @{ $self->{'Cc'} } ); + @{ $self->{'Bcc'} } = + RT::EmailParser::CullRTAddresses( "", @{ $self->{'Bcc'} } ); -# }}} + # If there are no recipients, don't try to send the message. + # If the transaction has content and has the header RT-Squelch-Replies-To -# }}} + if ( defined $self->TransactionObj->Attachments->First() ) { + my $squelch = + $self->TransactionObj->Attachments->First->GetHeader( + 'RT-Squelch-Replies-To'); + + if ($squelch) { + @blacklist = split ( /,/, $squelch ); + } + } + +# Let's grab the SquelchMailTo attribue and push those entries into the @blacklist + my @non_recipients = $self->TicketObj->SquelchMailTo; + foreach my $attribute (@non_recipients) { + push @blacklist, $attribute->Content; + } + + # Cycle through the people we're sending to and pull out anyone on the + # system blacklist + foreach my $person_to_yank (@blacklist) { + $person_to_yank =~ s/\s//g; + @{ $self->{'To'} } = grep ( !/^$person_to_yank$/, @{ $self->{'To'} } ); + @{ $self->{'Cc'} } = grep ( !/^$person_to_yank$/, @{ $self->{'Cc'} } ); + @{ $self->{'Bcc'} } = + grep ( !/^$person_to_yank$/, @{ $self->{'Bcc'} } ); + } +} + +# }}} # {{{ sub SetReturnAddress =head2 SetReturnAddress is_comment => BOOLEAN @@ -409,8 +533,10 @@ Calculate and set From and Reply-To headers based on the is_comment flag. sub SetReturnAddress { my $self = shift; - my %args = ( is_comment => 0, - @_ ); + my %args = ( + is_comment => 0, + @_ + ); # From and Reply-To # $args{is_comment} should be set if the comment address is to be used. @@ -426,21 +552,26 @@ sub SetReturnAddress { } unless ( $self->TemplateObj->MIMEObj->head->get('From') ) { - if ($RT::UseFriendlyFromLine) { - my $friendly_name = $self->TransactionObj->CreatorObj->RealName; - if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string - $friendly_name = $1; - } - - $friendly_name =~ s/"/\\"/g; - $self->SetHeader( 'From', - sprintf($RT::FriendlyFromLineFormat, - $self->MIMEEncodeString( $friendly_name, $RT::EmailOutputEncoding ), $replyto), - ); - } - else { - $self->SetHeader( 'From', $replyto ); - } + if ($RT::UseFriendlyFromLine) { + my $friendly_name = $self->TransactionObj->CreatorObj->RealName; + if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string + $friendly_name = $1; + } + + $friendly_name =~ s/"/\\"/g; + $self->SetHeader( + 'From', + sprintf( + $RT::FriendlyFromLineFormat, + $self->MIMEEncodeString( $friendly_name, + $RT::EmailOutputEncoding ), + $replyto + ), + ); + } + else { + $self->SetHeader( 'From', $replyto ); + } } unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) { @@ -473,82 +604,6 @@ sub SetHeader { # }}} -# {{{ sub SetRecipients - -=head2 SetRecipients - -Dummy method to be overriden by subclasses which want to set the recipients. - -=cut - -sub SetRecipients { - my $self = shift; - return (); -} - -# }}} - -# {{{ sub SetTo - -=head2 SetTo - -Takes a string that is the addresses you want to send mail to - -=cut - -sub SetTo { - my $self = shift; - my $addresses = shift; - return $self->SetHeader( 'To', $addresses ); -} - -# }}} - -# {{{ sub SetCc - -=head2 SetCc - -Takes a string that is the addresses you want to Cc - -=cut - -sub SetCc { - my $self = shift; - my $addresses = shift; - - return $self->SetHeader( 'Cc', $addresses ); -} - -# }}} - -# {{{ sub SetBcc - -=head2 SetBcc - -Takes a string that is the addresses you want to Bcc - -=cut - -sub SetBcc { - my $self = shift; - my $addresses = shift; - - return $self->SetHeader( 'Bcc', $addresses ); -} - -# }}} - -# {{{ sub SetPrecedence - -sub SetPrecedence { - my $self = shift; - - unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ) { - $self->SetHeader( 'Precedence', "bulk" ); - } -} - -# }}} # {{{ sub SetSubject @@ -564,36 +619,33 @@ sub SetSubject { my $self = shift; my $subject; - unless ( $self->TemplateObj->MIMEObj->head->get('Subject') ) { - my $message = $self->TransactionObj->Attachments; - my $ticket = $self->TicketObj->Id; - - if ( $self->{'Subject'} ) { - $subject = $self->{'Subject'}; - } - elsif ( ( $message->First() ) - && ( $message->First->Headers ) ) { - my $header = $message->First->Headers(); - $header =~ s/\n\s+/ /g; - if ( $header =~ /^Subject: (.*?)$/m ) { - $subject = $1; - } - else { - $subject = $self->TicketObj->Subject(); - } - + my $message = $self->TransactionObj->Attachments; + if ( $self->TemplateObj->MIMEObj->head->get('Subject') ) { + return (); + } + if ( $self->{'Subject'} ) { + $subject = $self->{'Subject'}; + } + elsif ( ( $message->First() ) && ( $message->First->Headers ) ) { + my $header = $message->First->Headers(); + $header =~ s/\n\s+/ /g; + if ( $header =~ /^Subject: (.*?)$/m ) { + $subject = $1; } else { $subject = $self->TicketObj->Subject(); } - $subject =~ s/(\r\n|\n|\s)/ /gi; + } + else { + $subject = $self->TicketObj->Subject(); + } + + $subject =~ s/(\r\n|\n|\s)/ /gi; - chomp $subject; - $self->SetHeader( 'Subject', $subject ); + chomp $subject; + $self->SetHeader( 'Subject', $subject ); - } - return ($subject); } # }}} @@ -621,7 +673,7 @@ sub SetSubjectToken { # }}} -# {{{ +# {{{ SetHeadingAsEncoding =head2 SetHeaderAsEncoding($field_name, $charset_encoding) @@ -652,7 +704,7 @@ sub SetHeaderAsEncoding { } # }}} -# {{{ MIMENcodeString +# {{{ MIMEEncodeString =head2 MIMEEncodeString STRING ENCODING diff --git a/rt/lib/RT/Action/SetPriority.pm b/rt/lib/RT/Action/SetPriority.pm index 515eeb58c..d3272a024 100644 --- a/rt/lib/RT/Action/SetPriority.pm +++ b/rt/lib/RT/Action/SetPriority.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK package RT::Action::SetPriority; require RT::Action::Generic; diff --git a/rt/lib/RT/Action/UserDefined.pm b/rt/lib/RT/Action/UserDefined.pm index e2e3d72ce..c298a7c7f 100644 --- a/rt/lib/RT/Action/UserDefined.pm +++ b/rt/lib/RT/Action/UserDefined.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# {{{ BEGIN BPS TAGGED BLOCK # -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# <jesse@bestpractical.com> # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # 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 @@ -14,13 +20,29 @@ # 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. +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. # -# END LICENSE BLOCK +# }}} END BPS TAGGED BLOCK package RT::Action::UserDefined; |