summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Action
diff options
context:
space:
mode:
authorivan <ivan>2012-02-19 22:45:24 +0000
committerivan <ivan>2012-02-19 22:45:24 +0000
commit5a5e999c1995dc74e90a5bec660e28692b81611e (patch)
tree872956f7ec1b3026d1c1bdb5f923aed96429225b /rt/lib/RT/Action
parent2c4e5aea8e1833e000716706d2a08ac9fdabf934 (diff)
fix ancient vendor branch CVS bs
Diffstat (limited to 'rt/lib/RT/Action')
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm126
-rwxr-xr-xrt/lib/RT/Action/Generic.pm218
-rwxr-xr-xrt/lib/RT/Action/Notify.pm149
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm82
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm64
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm1252
6 files changed, 1149 insertions, 742 deletions
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
index 81f7bdd..e3e792e 100755
--- a/rt/lib/RT/Action/Autoreply.pm
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -1,34 +1,71 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
package RT::Action::Autoreply;
-require RT::Action::SendEmail;
use strict;
-use vars qw/@ISA/;
-@ISA = qw(RT::Action::SendEmail);
+use warnings;
+
+use base 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
@@ -59,38 +96,37 @@ Set this message\'s return address to the apropriate queue address
sub SetReturnAddress {
my $self = shift;
- my %args = ( is_comment => 0,
- @_
- );
- my $replyto;
- if ($args{'is_comment'}) {
- $replyto = $self->TicketObj->QueueObj->CommentAddress ||
- $RT::CommentAddress;
- }
- else {
- $replyto = $self->TicketObj->QueueObj->CorrespondAddress ||
- $RT::CorrespondAddress;
- }
-
- unless ($self->TemplateObj->MIMEObj->head->get('From')) {
- my $friendly_name = $self->TicketObj->QueueObj->Description ||
- $self->TicketObj->QueueObj->Name;
- $friendly_name =~ s/"/\\"/g;
- $self->SetHeader('From', "\"$friendly_name\" <$replyto>");
- }
-
- unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
- $self->SetHeader('Reply-To', "$replyto");
- }
+ my $friendly_name;
+
+ if (RT->Config->Get('UseFriendlyFromLine')) {
+ $friendly_name = $self->TicketObj->QueueObj->Description ||
+ $self->TicketObj->QueueObj->Name;
+ }
+
+ $self->SUPER::SetReturnAddress( @_, friendly_name => $friendly_name );
}
# }}}
-eval "require RT::Action::Autoreply_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Vendor.pm});
-eval "require RT::Action::Autoreply_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Local.pm});
+# {{{{ sub SetRTSpecialHeaders
+
+=head2 SetRTSpecialHeaders
+
+Set the C<Auto-Generated> header to C<auto-replied>, in accordance
+with RFC3834.
+
+=cut
+
+sub SetRTSpecialHeaders {
+ my $self = shift;
+ $self->SUPER::SetRTSpecialHeaders(@_);
+ $self->SetHeader( 'Auto-Submitted', 'auto-replied' );
+}
+
+# }}}
+
+RT::Base->_ImportOverlays();
1;
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
index 007d299..d747813 100755
--- a/rt/lib/RT/Action/Generic.pm
+++ b/rt/lib/RT/Action/Generic.pm
@@ -1,29 +1,54 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
=head1 NAME
- RT::Action::Generic - a generic baseclass for RT Actions
+ RT::Action::Generic - deprecated, see RT::Action
=head1 SYNOPSIS
@@ -31,165 +56,18 @@
=head1 DESCRIPTION
-=head1 METHODS
-
-=begin testing
+This module is provided only for backwards compatibility.
-ok (require RT::Action::Generic);
+=head1 METHODS
-=end testing
=cut
-package RT::Action::Generic;
-
use strict;
+use warnings;
+package RT::Action::Generic;
+use base 'RT::Action';
-# {{{ sub new
-sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
- my $self = {};
- bless ($self, $class);
- $self->_Init(@_);
- return $self;
-}
-# }}}
-
-# {{{ sub new
-sub loc {
- my $self = shift;
- return $self->{'ScripObj'}->loc(@_);
-}
-# }}}
-
-# {{{ sub _Init
-sub _Init {
- my $self = shift;
- my %args = ( TransactionObj => undef,
- TicketObj => undef,
- ScripObj => undef,
- TemplateObj => undef,
- Argument => undef,
- Type => undef,
- @_ );
-
-
- $self->{'Argument'} = $args{'Argument'};
- $self->{'ScripObj'} = $args{'ScripObj'};
- $self->{'TicketObj'} = $args{'TicketObj'};
- $self->{'TransactionObj'} = $args{'TransactionObj'};
- $self->{'TemplateObj'} = $args{'TemplateObj'};
- $self->{'Type'} = $args{'Type'};
-}
-# }}}
-
-# Access Scripwide data
-
-# {{{ sub Argument
-sub Argument {
- my $self = shift;
- return($self->{'Argument'});
-}
-# }}}
-
-# {{{ sub TicketObj
-sub TicketObj {
- my $self = shift;
- return($self->{'TicketObj'});
-}
-# }}}
-
-# {{{ sub TransactionObj
-sub TransactionObj {
- my $self = shift;
- return($self->{'TransactionObj'});
-}
-# }}}
-
-# {{{ sub TemplateObj
-sub TemplateObj {
- my $self = shift;
- return($self->{'TemplateObj'});
-}
-# }}}
-
-# {{{ sub ScripObj
-sub ScripObj {
- my $self = shift;
- return($self->{'ScripObj'});
-}
-# }}}
-
-# {{{ sub Type
-sub Type {
- my $self = shift;
- return($self->{'Type'});
-}
-# }}}
-
-
-# Scrip methods
-
-#Do what we need to do and send it out.
-
-# {{{ sub Commit
-sub Commit {
- my $self = shift;
- return(0, $self->loc("Commit Stubbed"));
-}
-# }}}
-
-
-#What does this type of Action does
-
-# {{{ sub Describe
-sub Describe {
- my $self = shift;
- return $self->loc("No description for [_1]", ref $self);
-}
-# }}}
-
-
-#Parse the templates, get things ready to go.
-
-# {{{ sub Prepare
-sub Prepare {
- my $self = shift;
- return (0, $self->loc("Prepare Stubbed"));
-}
-# }}}
-
-
-#If this rule applies to this transaction, return true.
-
-# {{{ sub IsApplicable
-sub IsApplicable {
- my $self = shift;
- return(undef);
-}
-# }}}
-
-# {{{ sub DESTROY
-sub DESTROY {
- my $self = shift;
-
- # We need to clean up all the references that might maybe get
- # oddly circular
- $self->{'TemplateObj'} =undef
- $self->{'TicketObj'} = undef;
- $self->{'TransactionObj'} = undef;
- $self->{'ScripObj'} = undef;
-
-
-
-}
-
-# }}}
-
-eval "require RT::Action::Generic_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Vendor.pm});
-eval "require RT::Action::Generic_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Local.pm});
-
+RT::Base->_ImportOverlays();
1;
+
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
index 1e4e4c0..3f58bf5 100755
--- a/rt/lib/RT/Action/Notify.pm
+++ b/rt/lib/RT/Action/Notify.pm
@@ -1,34 +1,73 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
+#
package RT::Action::Notify;
-require RT::Action::SendEmail;
use strict;
-use vars qw/@ISA/;
-@ISA = qw(RT::Action::SendEmail);
+use warnings;
-# {{{ sub SetRecipients
+use base qw(RT::Action::SendEmail);
+
+use Email::Address;
+
+=head2 Prepare
+
+Set up the relevant recipients, then call our parent.
+
+=cut
+
+
+sub Prepare {
+ my $self = shift;
+ $self->SetRecipients();
+ $self->SUPER::Prepare();
+}
=head2 SetRecipients
@@ -40,93 +79,89 @@ Explicitly B<does not> notify the creator of the transaction by default
sub SetRecipients {
my $self = shift;
- my $arg = $self->Argument;
+ my $ticket = $self->TicketObj;
+ my $arg = $self->Argument;
$arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
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 ( my $attachment = $self->TransactionObj->Attachments->First ) {
+ push @Cc, map { $_->address } Email::Address->parse(
+ $attachment->GetHeader('RT-Send-Cc')
+ );
+ push @Bcc, map { $_->address } Email::Address->parse(
+ $attachment->GetHeader('RT-Send-Bcc')
+ );
}
}
if ( $arg =~ /\bRequestor\b/ ) {
- push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses );
+ push @To, $ticket->Requestors->MemberEmailAddresses;
}
-
-
if ( $arg =~ /\bCc\b/ ) {
#If we have a To, make the Ccs, Ccs, otherwise, promote them to To
if (@To) {
- push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
- push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $ticket->Cc->MemberEmailAddresses );
+ push ( @Cc, $ticket->QueueObj->Cc->MemberEmailAddresses );
}
else {
- push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
- push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $ticket->Cc->MemberEmailAddresses );
+ push ( @To, $ticket->QueueObj->Cc->MemberEmailAddresses );
}
}
- if ( ( $arg =~ /\bOwner\b/ )
- && ( $self->TicketObj->OwnerObj->id != $RT::Nobody->id ) )
- {
-
- # If we're not sending to Ccs or requestors,
+ if ( $arg =~ /\bOwner\b/ && $ticket->OwnerObj->id != $RT::Nobody->id ) {
+ # If we're not sending to Ccs or requestors,
# then the Owner can be the To.
if (@To) {
- push ( @Bcc, $self->TicketObj->OwnerObj->EmailAddress );
+ push ( @Bcc, $ticket->OwnerObj->EmailAddress );
}
else {
- push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
+ push ( @To, $ticket->OwnerObj->EmailAddress );
}
}
if ( $arg =~ /\bAdminCc\b/ ) {
- push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses );
- push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $ticket->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $ticket->QueueObj->AdminCc->MemberEmailAddresses );
}
- if ($RT::UseFriendlyToLine) {
+ if ( RT->Config->Get('UseFriendlyToLine') ) {
unless (@To) {
- push (
- @PseudoTo,
- sprintf($RT::FriendlyToLineFormat, $arg, $self->TicketObj->id),
- );
+ push @PseudoTo,
+ sprintf RT->Config->Get('FriendlyToLineFormat'), $arg, $ticket->id;
}
}
- my $creator = $self->TransactionObj->CreatorObj->EmailAddress();
+ my $creatorObj = $self->TransactionObj->CreatorObj;
+ my $creator = $creatorObj->EmailAddress();
#Strip the sender out of the To, Cc and AdminCc and set the
# recipients fields used to build the message by the superclass.
# unless a flag is set
- if ($RT::NotifyActor) {
+ my $TransactionCurrentUser = RT::CurrentUser->new;
+ $TransactionCurrentUser->LoadByName($creatorObj->Name);
+ if (RT->Config->Get('NotifyActor',$TransactionCurrentUser)) {
@{ $self->{'To'} } = @To;
@{ $self->{'Cc'} } = @Cc;
@{ $self->{'Bcc'} } = @Bcc;
}
else {
- @{ $self->{'To'} } = grep ( !/^$creator$/, @To );
- @{ $self->{'Cc'} } = grep ( !/^$creator$/, @Cc );
- @{ $self->{'Bcc'} } = grep ( !/^$creator$/, @Bcc );
+ @{ $self->{'To'} } = grep ( lc $_ ne lc $creator, @To );
+ @{ $self->{'Cc'} } = grep ( lc $_ ne lc $creator, @Cc );
+ @{ $self->{'Bcc'} } = grep ( lc $_ ne lc $creator, @Bcc );
}
@{ $self->{'PseudoTo'} } = @PseudoTo;
- return (1);
-}
-# }}}
+}
-eval "require RT::Action::Notify_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Vendor.pm});
-eval "require RT::Action::Notify_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Local.pm});
+RT::Base->_ImportOverlays();
1;
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
index 210e4ab..504d139 100755
--- a/rt/lib/RT/Action/NotifyAsComment.pm
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -1,55 +1,75 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
package RT::Action::NotifyAsComment;
-require RT::Action::Notify;
use strict;
-use vars qw/@ISA/;
-@ISA = qw(RT::Action::Notify);
+use warnings;
+use base qw(RT::Action::Notify);
=head2 SetReturnAddress
-Tell SendEmail that this message should come out as a comment.
+Tell SendEmail that this message should come out as a comment.
Calls SUPER::SetReturnAddress.
=cut
sub SetReturnAddress {
- my $self = shift;
-
- # Tell RT::Action::SendEmail that this should come
- # from the relevant comment email address.
- $self->{'comment'} = 1;
-
- return($self->SUPER::SetReturnAddress(is_comment => 1));
+ my $self = shift;
+
+ # Tell RT::Action::SendEmail that this should come
+ # from the relevant comment email address.
+ $self->{'comment'} = 1;
+
+ return $self->SUPER::SetReturnAddress( @_, is_comment => 1 );
}
-eval "require RT::Action::NotifyAsComment_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/NotifyAsComment_Vendor.pm});
-eval "require RT::Action::NotifyAsComment_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/NotifyAsComment_Local.pm});
+RT::Base->_ImportOverlays();
1;
-
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
index 02ff3a5..aff6365 100644
--- a/rt/lib/RT/Action/ResolveMembers.pm
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -1,35 +1,58 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
# This Action will resolve all members of a resolved group ticket
package RT::Action::ResolveMembers;
-require RT::Action::Generic;
+use base 'RT::Action';
require RT::Links;
use strict;
-use vars qw/@ISA/;
-@ISA=qw(RT::Action::Generic);
#Do what we need to do and send it out.
@@ -79,10 +102,7 @@ sub IsApplicable {
}
# }}}
-eval "require RT::Action::ResolveMembers_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/ResolveMembers_Vendor.pm});
-eval "require RT::Action::ResolveMembers_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/ResolveMembers_Local.pm});
+RT::Base->_ImportOverlays();
1;
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
index dac8fc8..9e93e4a 100755
--- a/rt/lib/RT/Action/SendEmail.pm
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -1,38 +1,65 @@
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# <sales@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.
-#
-# 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
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# 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 }}}
+
# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
package RT::Action::SendEmail;
-require RT::Action::Generic;
use strict;
-use vars qw/@ISA/;
-@ISA = qw(RT::Action::Generic);
+use warnings;
-use MIME::Words qw(encode_mimeword);
+use base qw(RT::Action);
use RT::EmailParser;
+use RT::Interface::Email;
+use Email::Address;
+our @EMAIL_RECIPIENT_HEADERS = qw(To Cc Bcc);
+
=head1 NAME
@@ -42,273 +69,495 @@ RT::Action::AutoReply is a good example subclass.
=head1 SYNOPSIS
- require RT::Action::SendEmail;
- @ISA = qw(RT::Action::SendEmail);
-
+ use base 'RT::Action::SendEmail';
=head1 DESCRIPTION
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).
+=head1 METHODS
+=head2 CleanSlate
-=begin testing
+Cleans class-wide options, like L</SquelchMailTo> or L</AttachTickets>.
-ok (require RT::Action::SendEmail);
+=cut
-=end testing
+sub CleanSlate {
+ my $self = shift;
+ $self->SquelchMailTo(undef);
+ $self->AttachTickets(undef);
+}
+=head2 Commit
-=head1 AUTHOR
+Sends the prepared message and writes outgoing record into DB if the feature is
+activated in the config.
-Jesse Vincent <jesse@bestpractical.com> and Tobias Brox <tobix@cpan.org>
+=cut
-=head1 SEE ALSO
+sub Commit {
+ my $self = shift;
-perl(1).
+ $self->DeferDigestRecipients() if RT->Config->Get('RecordOutgoingEmail');
+ my $message = $self->TemplateObj->MIMEObj;
+
+ my $orig_message;
+ if ( RT->Config->Get('RecordOutgoingEmail')
+ && RT->Config->Get('GnuPG')->{'Enable'} )
+ {
+
+ # it's hacky, but we should know if we're going to crypt things
+ my $attachment = $self->TransactionObj->Attachments->First;
+
+ my %crypt;
+ foreach my $argument (qw(Sign Encrypt)) {
+ if ( $attachment
+ && defined $attachment->GetHeader("X-RT-$argument") )
+ {
+ $crypt{$argument} = $attachment->GetHeader("X-RT-$argument");
+ } else {
+ $crypt{$argument} = $self->TicketObj->QueueObj->$argument();
+ }
+ }
+ if ( $crypt{'Sign'} || $crypt{'Encrypt'} ) {
+ $orig_message = $message->dup;
+ }
+ }
-=cut
+ my ($ret) = $self->SendMessage($message);
+ if ( $ret > 0 && RT->Config->Get('RecordOutgoingEmail') ) {
+ if ($orig_message) {
+ $message->attach(
+ Type => 'application/x-rt-original-message',
+ Disposition => 'inline',
+ Data => $orig_message->as_string,
+ );
+ }
+ $self->RecordOutgoingMailTransaction($message);
+ $self->RecordDeferredRecipients();
+ }
-# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
-# {{{ sub _Init
-# We use _Init from RT::Action
-# }}}
+ return ( abs $ret );
+}
-# {{{ sub Commit
-#Do what we need to do and send it out.
-sub Commit {
- my $self = shift;
+=head2 Prepare
- 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
+Builds an outgoing email we're going to send using scrip's template.
- # 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
+=cut
+
+sub Prepare {
+ my $self = shift;
- if ( defined $self->TransactionObj->Attachments->First() ) {
+ my ( $result, $message ) = $self->TemplateObj->Parse(
+ Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj
+ );
+ if ( !$result ) {
+ return (undef);
+ }
- my $squelch = $self->TransactionObj->Attachments->First->GetHeader( 'RT-Squelch-Replies-To');
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
- if ($squelch) {
- my @blacklist = split ( /,/, $squelch );
+ # Header
+ $self->SetRTSpecialHeaders();
- # Cycle through the people we're sending to and pull out anyone on the
- # system blacklist
+ $self->RemoveInappropriateRecipients();
- 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 %seen;
+ foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
+ @{ $self->{$type} }
+ = grep defined && length && !$seen{ lc $_ }++,
+ @{ $self->{$type} };
}
# 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->{'Cc'} && @{ $self->{'Bcc'} } );
+# TODO: We should be pulling the recipients out of the template and shove them into To, Cc and Bcc
+ for my $header (@EMAIL_RECIPIENT_HEADERS) {
- $self->SetHeader('MIME-Version', '1.0');
+ $self->SetHeader( $header, join( ', ', @{ $self->{$header} } ) )
+ if (!$MIMEObj->head->get($header)
+ && $self->{$header}
+ && @{ $self->{$header} } );
+ }
+ # 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'} }
+ && !$MIMEObj->head->get('To')
+ && ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc') );
+
+ # We should never have to set the MIME-Version header
+ $self->SetHeader( 'MIME-Version', '1.0' );
+
+ # fsck.com #5959: Since RT sends 8bit mail, we should say so.
+ $self->SetHeader( 'Content-Transfer-Encoding', '8bit' );
+
+ # For security reasons, we only send out textual mails.
+ foreach my $part ( grep !$_->is_multipart, $MIMEObj->parts_DFS ) {
+ my $type = $part->mime_type || 'text/plain';
+ $type = 'text/plain' unless RT::I18N::IsTextualContentType($type);
+ $part->head->mime_attr( "Content-Type" => $type );
+ # utf-8 here is for _FindOrGuessCharset in I18N.pm
+ # it's not the final charset/encoding sent
+ $part->head->mime_attr( "Content-Type.charset" => 'utf-8' );
+ }
- # 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->Config->Get('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')
+ && ( $MIMEObj->head->get('RT-Attach-Message') !~ /^(n|no|0|off|false)$/i ) );
+
+ $self->AddTickets;
+
+ my $attachment = $self->TransactionObj->Attachments->First;
+ if ($attachment
+ && !(
+ $attachment->GetHeader('X-RT-Encrypt')
+ || $self->TicketObj->QueueObj->Encrypt
+ )
+ )
+ {
+ $attachment->SetHeader( 'X-RT-Encrypt' => 1 )
+ if ( $attachment->GetHeader("X-RT-Incoming-Encryption") || '' ) eq
+ 'Success';
+ }
+ return $result;
+}
- # Build up a MIME::Entity that looks like the original message.
+=head2 To
- my $do_attach = $self->TemplateObj->MIMEObj->head->get('RT-Attach-Message');
+Returns an array of L<Email::Address> objects containing all the To: recipients for this notification
- if ($do_attach) {
- $self->TemplateObj->MIMEObj->head->delete('RT-Attach-Message');
+=cut
- my $attachments = RT::Attachments->new($RT::SystemUser);
- $attachments->Limit( FIELD => 'TransactionId',
- VALUE => $self->TransactionObj->Id );
- $attachments->OrderBy('id');
+sub To {
+ my $self = shift;
+ return ( $self->AddressesFromHeader('To') );
+}
- my $transaction_content_obj = $self->TransactionObj->ContentObj;
+=head2 Cc
- # attach any of this transaction's attachments
- while ( my $attach = $attachments->Next ) {
+Returns an array of L<Email::Address> objects containing all the Cc: recipients for this notification
- # Don't attach anything blank
- next unless ( $attach->ContentLength );
+=cut
- # 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');
- }
+sub Cc {
+ my $self = shift;
+ return ( $self->AddressesFromHeader('Cc') );
+}
- }
+=head2 Bcc
+Returns an array of L<Email::Address> objects containing all the Bcc: recipients for this notification
- my $retval = $self->SendMessage($MIMEObj);
+=cut
+sub Bcc {
+ my $self = shift;
+ return ( $self->AddressesFromHeader('Bcc') );
- return ($retval);
}
-# }}}
+sub AddressesFromHeader {
+ my $self = shift;
+ my $field = shift;
+ my $header = $self->TemplateObj->MIMEObj->head->get($field);
+ my @addresses = Email::Address->parse($header);
+
+ return (@addresses);
+}
-# {{{ sub Prepare
+=head2 SendMessage MIMEObj
-sub Prepare {
- my $self = shift;
+sends the message using RT's preferred API.
+TODO: Break this out to a separate module
- # This actually populates the MIME::Entity fields in the Template Object
+=cut
- unless ( $self->TemplateObj ) {
- $RT::Logger->warning("No template object handed to $self\n");
- }
+sub SendMessage {
- unless ( $self->TransactionObj ) {
- $RT::Logger->warning("No transaction object handed to $self\n");
+ # DO NOT SHIFT @_ in this subroutine. It breaks Hook::LexWrap's
+ # ability to pass @_ to a 'post' routine.
+ my ( $self, $MIMEObj ) = @_;
- }
+ my $msgid = $MIMEObj->head->get('Message-ID');
+ chomp $msgid;
+
+ $self->ScripActionObj->{_Message_ID}++;
+
+ $RT::Logger->info( $msgid . " #"
+ . $self->TicketObj->id . "/"
+ . $self->TransactionObj->id
+ . " - Scrip "
+ . ($self->ScripObj->id || '#rule'). " "
+ . ( $self->ScripObj->Description || '' ) );
- unless ( $self->TicketObj ) {
- $RT::Logger->warning("No ticket object handed to $self\n");
+ my $status = RT::Interface::Email::SendEmail(
+ Entity => $MIMEObj,
+ Ticket => $self->TicketObj,
+ Transaction => $self->TransactionObj,
+ );
+
+
+ return $status unless ($status > 0 || exists $self->{'Deferred'});
+ my $success = $msgid . " sent ";
+ foreach (@EMAIL_RECIPIENT_HEADERS) {
+ my $recipients = $MIMEObj->head->get($_);
+ $success .= " $_: " . $recipients if $recipients;
}
- my ( $result, $message ) = $self->TemplateObj->Parse(
- Argument => $self->Argument,
- TicketObj => $self->TicketObj,
- TransactionObj => $self->TransactionObj
+ if( exists $self->{'Deferred'} ) {
+ for (qw(daily weekly susp)) {
+ $success .= "\nBatched email $_ for: ". join(", ", keys %{ $self->{'Deferred'}{ $_ } } )
+ if exists $self->{'Deferred'}{ $_ };
+ }
+ }
+
+ $success =~ s/\n//g;
+
+ $RT::Logger->info($success);
+
+ return (1);
+}
+
+=head2 AddAttachments
+
+Takes any attachments to this transaction and attaches them to the message
+we're building.
+
+=cut
+
+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
);
- if ($result) {
-
- # Header
- $self->SetSubject();
- $self->SetSubjectToken();
- $self->SetRecipients();
- $self->SetReturnAddress();
- $self->SetRTSpecialHeaders();
- if ($RT::EmailOutputEncoding) {
-
- # l10n related header
- $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding );
+
+ # Don't attach anything blank
+ $attachments->LimitNotEmpty;
+ $attachments->OrderBy( FIELD => 'id' );
+
+ # We want to make sure that we don't include the attachment that's
+ # being used as the "Content" of this message" unless that attachment's
+ # content type is not like text/...
+ my $transaction_content_obj = $self->TransactionObj->ContentObj;
+
+ if ( $transaction_content_obj
+ && $transaction_content_obj->ContentType =~ m{text/}i )
+ {
+ # If this was part of a multipart/alternative, skip all of the kids
+ my $parent = $transaction_content_obj->ParentObj;
+ if ($parent and $parent->Id and $parent->ContentType eq "multipart/alternative") {
+ $attachments->Limit(
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'parent',
+ OPERATOR => '!=',
+ VALUE => $parent->Id,
+ );
+ } else {
+ $attachments->Limit(
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $transaction_content_obj->Id,
+ );
}
}
- return $result;
+ # attach any of this transaction's attachments
+ my $seen_attachment = 0;
+ while ( my $attach = $attachments->Next ) {
+ if ( !$seen_attachment ) {
+ $MIMEObj->make_multipart( 'mixed', Force => 1 );
+ $seen_attachment = 1;
+ }
+ $self->AddAttachment($attach);
+ }
+}
+
+=head2 AddAttachment $attachment
+
+Takes one attachment object of L<RT::Attachmment> class and attaches it to the message
+we're building.
+
+=cut
+sub AddAttachment {
+ my $self = shift;
+ my $attach = shift;
+ my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
+
+ $MIMEObj->attach(
+ Type => $attach->ContentType,
+ Charset => $attach->OriginalEncoding,
+ Data => $attach->OriginalContent,
+ Filename => $self->MIMEEncodeString( $attach->Filename ),
+ 'RT-Attachment:' => $self->TicketObj->Id . "/"
+ . $self->TransactionObj->Id . "/"
+ . $attach->id,
+ Encoding => '-SUGGEST',
+ );
}
-# }}}
+=head2 AttachTickets [@IDs]
-# }}}
+Returns or set list of ticket's IDs that should be attached to an outgoing message.
-# {{{ SendMessage
-=head2 SendMessage MIMEObj
+B<Note> this method works as a class method and setup things global, so you have to
+clean list by passing undef as argument.
-sends the message using RT's preferred API.
-TODO: Break this out to a seperate module
+=cut
+
+{
+ my $list = [];
+
+ sub AttachTickets {
+ my $self = shift;
+ $list = [ grep defined, @_ ] if @_;
+ return @$list;
+ }
+}
+
+=head2 AddTickets
+
+Attaches tickets to the current message, list of tickets' ids get from
+L</AttachTickets> method.
=cut
-sub SendMessage {
+sub AddTickets {
my $self = shift;
- my $MIMEObj = shift;
+ $self->AddTicket($_) foreach $self->AttachTickets;
+ return;
+}
- my $msgid = $MIMEObj->head->get('Message-Id');
+=head2 AddTicket $ID
+Attaches a ticket with ID to the message.
- #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");
- return (1);
- }
+Each ticket is attached as multipart entity and all its messages and attachments
+are attached as sub entities in order of creation, but only if transaction type
+is Create or Correspond.
- # 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.
+=cut
- $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" );
- print MAIL $MIMEObj->as_string;
- close(MAIL);
- };
- if ($@) {
- $RT::Logger->crit($msgid. "Could not send mail. -".$@ );
- }
+sub AddTicket {
+ my $self = shift;
+ my $tid = shift;
+
+ # XXX: we need a current user here, but who is current user?
+ my $attachs = RT::Attachments->new($RT::SystemUser);
+ my $txn_alias = $attachs->TransactionAlias;
+ $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
+ $attachs->Limit(
+ ALIAS => $txn_alias,
+ FIELD => 'Type',
+ VALUE => 'Correspond'
+ );
+ $attachs->LimitByTicket($tid);
+ $attachs->LimitNotEmpty;
+ $attachs->OrderBy( FIELD => 'Created' );
+
+ my $ticket_mime = MIME::Entity->build(
+ Type => 'multipart/mixed',
+ Top => 0,
+ Description => "ticket #$tid",
+ );
+ while ( my $attachment = $attachs->Next ) {
+ $self->AddAttachment( $attachment, $ticket_mime );
}
- else {
- my @mailer_args = ($RT::MailCommand);
- local $ENV{MAILADDRESS};
+ if ( $ticket_mime->parts ) {
+ my $email_mime = $self->TemplateObj->MIMEObj;
+ $email_mime->make_multipart;
+ $email_mime->add_part($ticket_mime);
+ }
+ return;
+}
- if ( $RT::MailCommand eq 'sendmail' ) {
- push @mailer_args, $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);
- }
- else {
- push @mailer_args, $RT::MailParams;
- }
+=head2 RecordOutgoingMailTransaction MIMEObj
+
+Record a transaction in RT with this outgoing message for future record-keeping purposes
+
+=cut
+
+sub RecordOutgoingMailTransaction {
+ my $self = shift;
+ my $MIMEObj = shift;
- unless ( $MIMEObj->send( @mailer_args ) ) {
- $RT::Logger->crit($msgid. "Could not send mail." );
- return (0);
+ 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 );
+ }
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, 'utf-8', 'mime_words_ok' );
- my $success = ($msgid. " sent To: ".$MIMEObj->head->get('To') . " Cc: ".$MIMEObj->head->get('Cc') . " Bcc: ".$MIMEObj->head->get('Bcc'));
- $success =~ s/\n//gi;
- $RT::Logger->info($success);
+ my $transaction
+ = RT::Transaction->new( $self->TransactionObj->CurrentUser );
- return (1);
-}
+# XXX: TODO -> Record attachments as references to things in the attachments table, maybe.
+
+ my $type;
+ if ( $self->TransactionObj->Type eq 'Comment' ) {
+ $type = 'CommentEmailRecord';
+ } else {
+ $type = 'EmailRecord';
+ }
-# }}}
+ my $msgid = $MIMEObj->head->get('Message-ID');
+ chomp $msgid;
-# {{{ Deal with message headers (Set* subs, designed for easy overriding)
+ my ( $id, $msg ) = $transaction->Create(
+ Ticket => $self->TicketObj->Id,
+ Type => $type,
+ Data => $msgid,
+ MIMEObj => $MIMEObj,
+ ActivateScrips => 0
+ );
-# {{{ sub SetRTSpecialHeaders
+ if ($id) {
+ $self->{'OutgoingMailTransaction'} = $id;
+ } else {
+ $RT::Logger->warning(
+ "Could not record outgoing message transaction: $msg");
+ }
+ return $id;
+}
=head2 SetRTSpecialHeaders
@@ -320,85 +569,272 @@ that don't matter much to anybody else.
sub SetRTSpecialHeaders {
my $self = shift;
- $self->SetReferences();
+ $self->SetSubject();
+ $self->SetSubjectToken();
+ $self->SetHeaderAsEncoding( 'Subject',
+ RT->Config->Get('EmailOutputEncoding') )
+ if ( RT->Config->Get('EmailOutputEncoding') );
+ $self->SetReturnAddress();
+ $self->SetReferencesHeaders();
+
+ unless ( $self->TemplateObj->MIMEObj->head->get('Message-ID') ) {
+
+ # Get Message-ID for this txn
+ my $msgid = "";
+ if ( my $msg = $self->TransactionObj->Message->First ) {
+ $msgid = $msg->GetHeader("RT-Message-ID")
+ || $msg->GetHeader("Message-ID");
+ }
- $self->SetMessageID();
+ # If there is one, and we can parse it, then base our Message-ID on it
+ if ( $msgid
+ and $msgid
+ =~ s/<(rt-.*?-\d+-\d+)\.(\d+)-\d+-\d+\@\QRT->Config->Get('Organization')\E>$/
+ "<$1." . $self->TicketObj->id
+ . "-" . $self->ScripObj->id
+ . "-" . $self->ScripActionObj->{_Message_ID}
+ . "@" . RT->Config->Get('Organization') . ">"/eg
+ and $2 == $self->TicketObj->id
+ )
+ {
+ $self->SetHeader( "Message-ID" => $msgid );
+ } else {
+ $self->SetHeader(
+ 'Message-ID' => RT::Interface::Email::GenMessageId(
+ Ticket => $self->TicketObj,
+ Scrip => $self->ScripObj,
+ ScripAction => $self->ScripActionObj
+ ),
+ );
+ }
+ }
- $self->SetPrecedence();
+ if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
+ and !$self->TemplateObj->MIMEObj->head->get("Precedence")
+ ) {
+ $self->SetHeader( 'Precedence', $precedence );
+ }
- $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname );
+ $self->SetHeader( 'X-RT-Loop-Prevention', RT->Config->Get('rtname') );
$self->SetHeader( 'RT-Ticket',
- $RT::rtname . " #" . $self->TicketObj->id() );
+ RT->Config->Get('rtname') . " #" . $self->TicketObj->id() );
$self->SetHeader( 'Managed-by',
- "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
-
- $self->SetHeader( 'RT-Originator',
- $self->TransactionObj->CreatorObj->EmailAddress );
- return ();
+ "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
+
+# XXX, TODO: use /ShowUser/ShowUserEntry(or something like that) when it would be
+# refactored into user's method.
+ if ( my $email = $self->TransactionObj->CreatorObj->EmailAddress
+ and RT->Config->Get('UseOriginatorHeader')
+ ) {
+ $self->SetHeader( 'RT-Originator', $email );
+ }
}
-# {{{ sub SetReferences
-=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.
+sub DeferDigestRecipients {
+ my $self = shift;
+ $RT::Logger->debug( "Calling SetRecipientDigests for transaction " . $self->TransactionObj . ", id " . $self->TransactionObj->id );
+
+ # The digest attribute will be an array of notifications that need to
+ # be sent for this transaction. The array will have the following
+ # format for its objects.
+ # $digest_hash -> {daily|weekly|susp} -> address -> {To|Cc|Bcc}
+ # -> sent -> {true|false}
+ # The "sent" flag will be used by the cron job to indicate that it has
+ # run on this transaction.
+ # In a perfect world we might move this hash construction to the
+ # extension module itself.
+ my $digest_hash = {};
+
+ foreach my $mailfield (@EMAIL_RECIPIENT_HEADERS) {
+ # If we have a "PseudoTo", the "To" contains it, so we don't need to access it
+ next if ( ( $self->{'PseudoTo'} && @{ $self->{'PseudoTo'} } ) && ( $mailfield eq 'To' ) );
+ $RT::Logger->debug( "Working on mailfield $mailfield; recipients are " . join( ',', @{ $self->{$mailfield} } ) );
+
+ # Store the 'daily digest' folk in an array.
+ my ( @send_now, @daily_digest, @weekly_digest, @suspended );
+
+ # Have to get the list of addresses directly from the MIME header
+ # at this point.
+ $RT::Logger->debug( $self->TemplateObj->MIMEObj->head->as_string );
+ foreach my $rcpt ( map { $_->address } $self->AddressesFromHeader($mailfield) ) {
+ next unless $rcpt;
+ my $user_obj = RT::User->new($RT::SystemUser);
+ $user_obj->LoadByEmail($rcpt);
+ if ( ! $user_obj->id ) {
+ # If there's an email address in here without an associated
+ # RT user, pass it on through.
+ $RT::Logger->debug( "User $rcpt is not associated with an RT user object. Send mail.");
+ push( @send_now, $rcpt );
+ next;
+ }
-=cut
+ my $mailpref = RT->Config->Get( 'EmailFrequency', $user_obj ) || '';
+ $RT::Logger->debug( "Got user mail preference '$mailpref' for user $rcpt");
+
+ if ( $mailpref =~ /daily/i ) { push( @daily_digest, $rcpt ) }
+ elsif ( $mailpref =~ /weekly/i ) { push( @weekly_digest, $rcpt ) }
+ elsif ( $mailpref =~ /suspend/i ) { push( @suspended, $rcpt ) }
+ else { push( @send_now, $rcpt ) }
+ }
+
+ # Reset the relevant mail field.
+ $RT::Logger->debug( "Removing deferred recipients from $mailfield: line");
+ if (@send_now) {
+ $self->SetHeader( $mailfield, join( ', ', @send_now ) );
+ } else { # No recipients! Remove the header.
+ $self->TemplateObj->MIMEObj->head->delete($mailfield);
+ }
-sub SetReferences {
+ # Push the deferred addresses into the appropriate field in
+ # our attribute hash, with the appropriate mail header.
+ $RT::Logger->debug(
+ "Setting deferred recipients for attribute creation");
+ $digest_hash->{'daily'}->{$_} = {'header' => $mailfield , _sent => 0} for (@daily_digest);
+ $digest_hash->{'weekly'}->{$_} ={'header' => $mailfield, _sent => 0} for (@weekly_digest);
+ $digest_hash->{'susp'}->{$_} = {'header' => $mailfield, _sent =>0 } for (@suspended);
+ }
+
+ if ( scalar keys %$digest_hash ) {
+
+ # Save the hash so that we can add it as an attribute to the
+ # outgoing email transaction.
+ $self->{'Deferred'} = $digest_hash;
+ } else {
+ $RT::Logger->debug( "No recipients found for deferred delivery on "
+ . "transaction #"
+ . $self->TransactionObj->id );
+ }
+}
+
+
+
+sub RecordDeferredRecipients {
my $self = shift;
+ return unless exists $self->{'Deferred'};
- # 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
- # incoming mails, we would like to preserve the In-Reply-To and/or
- # References.
+ my $txn_id = $self->{'OutgoingMailTransaction'};
+ return unless $txn_id;
- $self->SetHeader( 'In-Reply-To',
- "<rt-" . $self->TicketObj->id() . "\@" . $RT::rtname . ">" );
+ my $txn_obj = RT::Transaction->new( $self->CurrentUser );
+ $txn_obj->Load( $txn_id );
+ my( $ret, $msg ) = $txn_obj->AddAttribute(
+ Name => 'DeferredRecipients',
+ Content => $self->{'Deferred'}
+ );
+ $RT::Logger->warning( "Unable to add deferred recipients to outgoing transaction: $msg" )
+ unless $ret;
- # TODO We should always add References headers for all message-ids
- # of previous messages related to this ticket.
+ return ($ret,$msg);
}
-# }}}
+=head2 SquelchMailTo [@ADDRESSES]
+
+Mark ADDRESSES to be removed from list of the recipients. Returns list of the addresses.
+To empty list pass undefined argument.
-# {{{ sub SetMessageID
+B<Note> that this method can be called as class method and works globaly. Don't forget to
+clean this list when blocking is not required anymore, pass undef to do this.
-=head2 SetMessageID
+=cut
+
+{
+ my $squelch = [];
+
+ sub SquelchMailTo {
+ my $self = shift;
+ if (@_) {
+ $squelch = [ grep defined, @_ ];
+ }
+ return @$squelch;
+ }
+}
-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.
+=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#"
-
- $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');
-}
+ my @blacklist = ();
-# }}}
+ # 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
+
+ my $msgid = $self->TemplateObj->MIMEObj->head->get('Message-Id');
+ if ( my $attachment = $self->TransactionObj->Attachments->First ) {
+
+ if ( $attachment->GetHeader('RT-DetectedAutoGenerated') ) {
+
+ # What do we want to do with this? It's probably (?) a bounce
+ # caused by one of the watcher addresses being broken.
+ # Default ("true") is to redistribute, for historical reasons.
+
+ if ( !RT->Config->Get('RedistributeAutoGeneratedMessages') ) {
+
+ # Don't send to any watchers.
+ @{ $self->{$_} } = () for (@EMAIL_RECIPIENT_HEADERS);
+ $RT::Logger->info( $msgid
+ . " The incoming message was autogenerated. "
+ . "Not redistributing this message based on site configuration."
+ );
+ } elsif ( RT->Config->Get('RedistributeAutoGeneratedMessages') eq
+ 'privileged' )
+ {
+
+ # Only send to "privileged" watchers.
+ foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
+ foreach my $addr ( @{ $self->{$type} } ) {
+ my $user = RT::User->new($RT::SystemUser);
+ $user->LoadByEmail($addr);
+ push @blacklist, $addr if ( !$user->Privileged );
+ }
+ }
+ $RT::Logger->info( $msgid
+ . " The incoming message was autogenerated. "
+ . "Not redistributing this message to unprivileged users based on site configuration."
+ );
+ }
+ }
+
+ if ( my $squelch = $attachment->GetHeader('RT-Squelch-Replies-To') ) {
+ push @blacklist, split( /,/, $squelch );
+ }
+ }
+
+# Let's grab the SquelchMailTo attribue and push those entries into the @blacklist
+ push @blacklist, map $_->Content, $self->TicketObj->SquelchMailTo;
+ push @blacklist, $self->SquelchMailTo;
+
+ # Cycle through the people we're sending to and pull out anyone on the
+ # system blacklist
-# }}}
+ # Trim leading and trailing spaces.
+ @blacklist = map { RT::User->CanonicalizeEmailAddress( $_->address ) } Email::Address->parse(join(', ', grep {defined} @blacklist));
-# {{{ sub SetReturnAddress
+ foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
+ my @addrs;
+ foreach my $addr ( @{ $self->{$type} } ) {
+
+ # Weed out any RT addresses. We really don't want to talk to ourselves!
+ # If we get a reply back, that means it's not an RT address
+ if ( !RT::EmailParser->CullRTAddresses($addr) ) {
+ $RT::Logger->info( $msgid . "$addr appears to point to this RT instance. Skipping" );
+ next;
+ }
+ if ( grep /^\Q$addr\E$/, @blacklist ) {
+ $RT::Logger->info( $msgid . "$addr was blacklisted for outbound mail on this transaction. Skipping");
+ next;
+ }
+ push @addrs, $addr;
+ }
+ @{ $self->{$type} } = @addrs;
+ }
+}
=head2 SetReturnAddress is_comment => BOOLEAN
@@ -409,8 +845,11 @@ 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,
+ friendly_name => undef,
+ @_
+ );
# From and Reply-To
# $args{is_comment} should be set if the comment address is to be used.
@@ -418,29 +857,37 @@ sub SetReturnAddress {
if ( $args{'is_comment'} ) {
$replyto = $self->TicketObj->QueueObj->CommentAddress
- || $RT::CommentAddress;
- }
- else {
+ || RT->Config->Get('CommentAddress');
+ } else {
$replyto = $self->TicketObj->QueueObj->CorrespondAddress
- || $RT::CorrespondAddress;
+ || RT->Config->Get('CorrespondAddress');
}
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->Config->Get('UseFriendlyFromLine') ) {
+ my $friendly_name = $args{friendly_name};
+
+ unless ( $friendly_name ) {
+ $friendly_name = $self->TransactionObj->CreatorObj->FriendlyName;
+ if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
+ $friendly_name = $1;
+ }
+ }
+
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader(
+ 'From',
+ sprintf(
+ RT->Config->Get('FriendlyFromLineFormat'),
+ $self->MIMEEncodeString(
+ $friendly_name, RT->Config->Get('EmailOutputEncoding')
+ ),
+ $replyto
+ ),
+ );
+ } else {
+ $self->SetHeader( 'From', $replyto );
+ }
}
unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) {
@@ -449,10 +896,6 @@ sub SetReturnAddress {
}
-# }}}
-
-# {{{ sub SetHeader
-
=head2 SetHeader FIELD, VALUE
Set the FIELD of the current MIME object into VALUE.
@@ -466,163 +909,157 @@ sub SetHeader {
chomp $val;
chomp $field;
- $self->TemplateObj->MIMEObj->head->fold_length( $field, 10000 );
- $self->TemplateObj->MIMEObj->head->replace( $field, $val );
- return $self->TemplateObj->MIMEObj->head->get($field);
+ my $head = $self->TemplateObj->MIMEObj->head;
+ $head->fold_length( $field, 10000 );
+ $head->replace( $field, $val );
+ return $head->get($field);
}
-# }}}
-
-# {{{ sub SetRecipients
-
-=head2 SetRecipients
+=head2 SetSubject
-Dummy method to be overriden by subclasses which want to set the recipients.
+This routine sets the subject. it does not add the rt tag. That gets done elsewhere
+If subject is already defined via template, it uses that. otherwise, it tries to get
+the transaction's subject.
-=cut
+=cut
-sub SetRecipients {
+sub SetSubject {
my $self = shift;
- return ();
-}
+ my $subject;
-# }}}
+ if ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
+ return ();
+ }
-# {{{ sub SetTo
+ # don't use Transaction->Attachments because it caches
+ # and anything which later calls ->Attachments will be hurt
+ # by our RowsPerPage() call. caching is hard.
+ my $message = RT::Attachments->new( $self->CurrentUser );
+ $message->Limit( FIELD => 'TransactionId', VALUE => $self->TransactionObj->id);
+ $message->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ $message->RowsPerPage(1);
+
+ if ( $self->{'Subject'} ) {
+ $subject = $self->{'Subject'};
+ } elsif ( my $first = $message->First ) {
+ my $tmp = $first->GetHeader('Subject');
+ $subject = defined $tmp ? $tmp : $self->TicketObj->Subject;
+ } else {
+ $subject = $self->TicketObj->Subject;
+ }
+ $subject = '' unless defined $subject;
+ chomp $subject;
-=head2 SetTo
+ $subject =~ s/(\r\n|\n|\s)/ /g;
-Takes a string that is the addresses you want to send mail to
+ $self->SetHeader( 'Subject', $subject );
-=cut
-
-sub SetTo {
- my $self = shift;
- my $addresses = shift;
- return $self->SetHeader( 'To', $addresses );
}
-# }}}
-
-# {{{ sub SetCc
-
-=head2 SetCc
+=head2 SetSubjectToken
-Takes a string that is the addresses you want to Cc
+This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
=cut
-sub SetCc {
- my $self = shift;
- my $addresses = shift;
+sub SetSubjectToken {
+ my $self = shift;
- return $self->SetHeader( 'Cc', $addresses );
+ my $head = $self->TemplateObj->MIMEObj->head;
+ $head->replace(
+ Subject => RT::Interface::Email::AddSubjectTag(
+ Encode::decode_utf8( $head->get('Subject') ),
+ $self->TicketObj,
+ ),
+ );
}
-# }}}
-
-# {{{ sub SetBcc
-
-=head2 SetBcc
+=head2 SetReferencesHeaders
-Takes a string that is the addresses you want to Bcc
+Set References and In-Reply-To headers for this message.
=cut
-sub SetBcc {
- my $self = shift;
- my $addresses = shift;
-
- return $self->SetHeader( 'Bcc', $addresses );
-}
-
-# }}}
-
-# {{{ sub SetPrecedence
-
-sub SetPrecedence {
+sub SetReferencesHeaders {
my $self = shift;
-
- unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ) {
- $self->SetHeader( 'Precedence', "bulk" );
+ my ( @in_reply_to, @references, @msgid );
+
+ if ( my $top = $self->TransactionObj->Message->First ) {
+ @in_reply_to = split( /\s+/m, $top->GetHeader('In-Reply-To') || '' );
+ @references = split( /\s+/m, $top->GetHeader('References') || '' );
+ @msgid = split( /\s+/m, $top->GetHeader('Message-ID') || '' );
+ } else {
+ return (undef);
}
-}
-# }}}
+ # There are two main cases -- this transaction was created with
+ # the RT Web UI, and hence we want to *not* append its Message-ID
+ # to the References and In-Reply-To. OR it came from an outside
+ # source, and we should treat it as per the RFC
+ my $org = RT->Config->Get('Organization');
+ if ( "@msgid" =~ /<(rt-.*?-\d+-\d+)\.(\d+)-0-0\@\Q$org\E>/ ) {
+
+ # Make all references which are internal be to version which we
+ # have sent out
+
+ for ( @references, @in_reply_to ) {
+ s/<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>$/
+ "<$1." . $self->TicketObj->id .
+ "-" . $self->ScripObj->id .
+ "-" . $self->ScripActionObj->{_Message_ID} .
+ "@" . $org . ">"/eg
+ }
-# {{{ sub SetSubject
+ # In reply to whatever the internal message was in reply to
+ $self->SetHeader( 'In-Reply-To', join( " ", (@in_reply_to) ) );
-=head2 SetSubject
+ # Default the references to whatever we're in reply to
+ @references = @in_reply_to unless @references;
-This routine sets the subject. it does not add the rt tag. that gets done elsewhere
-If $self->{'Subject'} is already defined, it uses that. otherwise, it tries to get
-the transaction's subject.
+ # References are unchanged from internal
+ } else {
-=cut
+ # In reply to that message
+ $self->SetHeader( 'In-Reply-To', join( " ", (@msgid) ) );
-sub SetSubject {
- my $self = shift;
- my $subject;
-
- unless ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
- my $message = $self->TransactionObj->Attachments;
- my $ticket = $self->TicketObj->Id;
+ # Default the references to whatever we're in reply to
+ @references = @in_reply_to unless @references;
- 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();
- }
+ # Push that message onto the end of the references
+ push @references, @msgid;
+ }
- }
- else {
- $subject = $self->TicketObj->Subject();
- }
+ # Push pseudo-ref to the front
+ my $pseudo_ref = $self->PseudoReference;
+ @references = ( $pseudo_ref, grep { $_ ne $pseudo_ref } @references );
- $subject =~ s/(\r\n|\n|\s)/ /gi;
+ # If there are more than 10 references headers, remove all but the
+ # first four and the last six (Gotta keep this from growing
+ # forever)
+ splice( @references, 4, -6 ) if ( $#references >= 10 );
- chomp $subject;
- $self->SetHeader( 'Subject', $subject );
+ # Add on the references
+ $self->SetHeader( 'References', join( " ", @references ) );
+ $self->TemplateObj->MIMEObj->head->fold_length( 'References', 80 );
- }
- return ($subject);
}
-# }}}
-
-# {{{ sub SetSubjectToken
+=head2 PseudoReference
-=head2 SetSubjectToken
-
-This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
+Returns a fake Message-ID: header for the ticket to allow a base level of threading
=cut
-sub SetSubjectToken {
+sub PseudoReference {
+
my $self = shift;
- my $tag = "[$RT::rtname #" . $self->TicketObj->id . "]";
- my $sub = $self->TemplateObj->MIMEObj->head->get('Subject');
- unless ( $sub =~ /\Q$tag\E/ ) {
- $sub =~ s/(\r\n|\n|\s)/ /gi;
- chomp $sub;
- $self->TemplateObj->MIMEObj->head->replace( 'Subject', "$tag $sub" );
- }
+ my $pseudo_ref
+ = '<RT-Ticket-'
+ . $self->TicketObj->id . '@'
+ . RT->Config->Get('Organization') . '>';
+ return $pseudo_ref;
}
-# }}}
-
-# }}}
-
-# {{{
-
=head2 SetHeaderAsEncoding($field_name, $charset_encoding)
This routine converts the field into specified charset encoding.
@@ -633,53 +1070,34 @@ sub SetHeaderAsEncoding {
my $self = shift;
my ( $field, $enc ) = ( shift, shift );
- if ($field eq 'From' and $RT::SMTPFrom) {
- $self->TemplateObj->MIMEObj->head->replace( $field, $RT::SMTPFrom );
- return;
- }
-
- my $value = $self->TemplateObj->MIMEObj->head->get($field);
-
- # don't bother if it's us-ascii
+ my $head = $self->TemplateObj->MIMEObj->head;
- # See RT::I18N, 'NOTES: Why Encode::_utf8_off before Encode::from_to'
-
- $value = $self->MIMEEncodeString($value, $enc);
-
- $self->TemplateObj->MIMEObj->head->replace( $field, $value );
+ if ( lc($field) eq 'from' and RT->Config->Get('SMTPFrom') ) {
+ $head->replace( $field, RT->Config->Get('SMTPFrom') );
+ return;
+ }
+ my $value = $head->get( $field );
+ $value = $self->MIMEEncodeString( $value, $enc );
+ $head->replace( $field, $value );
-}
-# }}}
+}
-# {{{ MIMENcodeString
+=head2 MIMEEncodeString
-=head2 MIMEEncodeString STRING ENCODING
+Takes a perl string and optional encoding pass it over
+L<RT::Interface::Email/EncodeToMIME>.
-Takes a string and a possible encoding and returns the string wrapped in MIME goo.
+Basicly encode a string using B encoding according to RFC2047.
=cut
sub MIMEEncodeString {
- my $self = shift;
- my $value = shift;
- my $enc = shift;
-
- chomp $value;
- return ($value) unless $value =~ /[^\x20-\x7e]/;
-
- $value =~ s/\s*$//;
- Encode::_utf8_off($value);
- my $res = Encode::from_to( $value, "utf-8", $enc );
- $value = encode_mimeword( $value, 'B', $enc );
+ my $self = shift;
+ return RT::Interface::Email::EncodeToMIME( String => $_[0], Charset => $_[1] );
}
-# }}}
-
-eval "require RT::Action::SendEmail_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm});
-eval "require RT::Action::SendEmail_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm});
+RT::Base->_ImportOverlays();
1;