summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Action
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Action')
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm64
-rwxr-xr-xrt/lib/RT/Action/Generic.pm155
-rwxr-xr-xrt/lib/RT/Action/Notify.pm99
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm25
-rw-r--r--rt/lib/RT/Action/OpenDependent.pm55
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm57
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm468
-rwxr-xr-xrt/lib/RT/Action/SendPasswordEmail.pm170
-rw-r--r--rt/lib/RT/Action/StallDependent.pm68
9 files changed, 1161 insertions, 0 deletions
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
new file mode 100755
index 000000000..624888e94
--- /dev/null
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -0,0 +1,64 @@
+#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Autoreply.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+
+package RT::Action::Autoreply;
+require RT::Action::SendEmail;
+@ISA = qw(RT::Action::SendEmail);
+
+
+# {{{ sub SetRecipients
+
+=head2 SetRecipients
+
+Sets the recipients of this message to this ticket's Requestor.
+
+=cut
+
+
+sub SetRecipients {
+ my $self=shift;
+
+ push(@{$self->{'To'}}, @{$self->TicketObj->Requestors->Emails});
+
+ return(1);
+}
+
+# }}}
+
+
+# {{{ sub SetReturnAddress
+
+=head2 SetReturnAddress
+
+Set this message\'s return address to the apropriate queue address
+
+=cut
+
+sub SetReturnAddress {
+ my $self = shift;
+ my %args = ( is_comment => 0,
+ @_
+ );
+
+ 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->Name;
+ $self->SetHeader('From', "\"$friendly_name\" <$replyto>");
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To', "$replyto");
+ }
+
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
new file mode 100755
index 000000000..ecfd4ab1a
--- /dev/null
+++ b/rt/lib/RT/Action/Generic.pm
@@ -0,0 +1,155 @@
+# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Generic.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# (c) 1996-2000 Jesse Vincent <jesse@fsck.com>
+# This software is redistributable under the terms of the GNU GPL
+
+=head1 NAME
+
+ RT::Action::Generic - a generic baseclass for RT Actions
+
+=head1 SYNOPSIS
+
+ use RT::Action::Generic;
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::TestHarness);
+ok (require RT::Action::Generic);
+
+=end testing
+
+=cut
+
+package RT::Action::Generic;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ 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 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,"Commit Stubbed");
+}
+# }}}
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ("No description for " . ref $self);
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ return (0,"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;
+
+
+
+}
+
+# }}}
+1;
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
new file mode 100755
index 000000000..6dca4fd41
--- /dev/null
+++ b/rt/lib/RT/Action/Notify.pm
@@ -0,0 +1,99 @@
+#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Notify.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+
+package RT::Action::Notify;
+require RT::Action::SendEmail;
+@ISA = qw(RT::Action::SendEmail);
+
+# {{{ sub SetRecipients
+
+=head2 SetRecipients
+
+Sets the recipients of this meesage to Owner, Requestor, AdminCc, Cc or All.
+Explicitly B<does not> notify the creator of the transaction.
+
+=cut
+
+sub SetRecipients {
+ my $self = shift;
+
+ $arg = $self->Argument;
+
+ $arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
+
+ my ( @To, @PseudoTo, @Cc, @Bcc );
+
+
+ if ($arg =~ /\bOtherRecipients\b/) {
+ if ($self->TransactionObj->Message->First) {
+ push (@Cc, $self->TransactionObj->Message->First->GetHeader('RT-Send-Cc'));
+ push (@Bcc, $self->TransactionObj->Message->First->GetHeader('RT-Send-Bcc'));
+ }
+ }
+
+ if ( $arg =~ /\bRequestor\b/ ) {
+ push ( @To, @{ $self->TicketObj->Requestors->Emails } );
+ }
+
+
+
+ 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->Emails } );
+ push ( @Cc, @{ $self->TicketObj->QueueObj->Cc->Emails } );
+ }
+ else {
+ push ( @Cc, @{ $self->TicketObj->Cc->Emails } );
+ push ( @To, @{ $self->TicketObj->QueueObj->Cc->Emails } );
+ }
+ }
+
+ if ( ( $arg =~ /\bOwner\b/ )
+ && ( $self->TicketObj->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 );
+ }
+ else {
+ push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
+ }
+
+ }
+
+ if ( $arg =~ /\bAdminCc\b/ ) {
+ push ( @Bcc, @{ $self->TicketObj->AdminCc->Emails } );
+ push ( @Bcc, @{ $self->TicketObj->QueueObj->AdminCc->Emails } );
+ }
+
+ if ($RT::UseFriendlyToLine) {
+ unless (@To) {
+ push ( @PseudoTo,
+ "\"$arg of $RT::rtname Ticket #"
+ . $self->TicketObj->id . "\":;" );
+ }
+ }
+
+ my $creator = $self->TransactionObj->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.
+
+ $RT::Logger->debug("$self: To is ".join(",",@To));
+ $RT::Logger->debug("$self: Cc is ".join(",",@Cc));
+ $RT::Logger->debug("$self: Bcc is ".join(",",@Bcc));
+
+ @{ $self->{'To'} } = grep ( !/^$creator$/, @To );
+ @{ $self->{'Cc'} } = grep ( !/^$creator$/, @Cc );
+ @{ $self->{'Bcc'} } = grep ( !/^$creator$/, @Bcc );
+ @{ $self->{'PseudoTo'} } = @PseudoTo;
+ return (1);
+
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
new file mode 100755
index 000000000..c72bfff13
--- /dev/null
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -0,0 +1,25 @@
+#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/NotifyAsComment.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+
+package RT::Action::NotifyAsComment;
+require RT::Action::Notify;
+@ISA = qw(RT::Action::Notify);
+
+
+=head2 SetReturnAddress
+
+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));
+}
+1;
+
diff --git a/rt/lib/RT/Action/OpenDependent.pm b/rt/lib/RT/Action/OpenDependent.pm
new file mode 100644
index 000000000..b271e4709
--- /dev/null
+++ b/rt/lib/RT/Action/OpenDependent.pm
@@ -0,0 +1,55 @@
+# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Attic/OpenDependent.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# This Action will open the BASE if a dependent is resolved.
+
+package RT::Action::OpenDependent;
+require RT::Action::Generic;
+require RT::Links;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self . " will stall a [local] BASE if it's open and a dependency link is created.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+
+ my $Links=RT::Links->new($RT::SystemUser);
+ $Links->Limit(FIELD => 'Type', VALUE => 'DependsOn');
+ $Links->Limit(FIELD => 'Target', VALUE => $self->TicketObj->id);
+
+ while (my $Link=$Links->Next()) {
+ next unless $Link->BaseIsLocal;
+ my $base=RT::Ticket->new($self->TicketObj->CurrentUser);
+ # Todo: Only work if Base is a plain ticket num:
+ $base->Load($Link->Base);
+ $base->Open if $base->Status eq 'stalled';
+ }
+}
+
+
+# Applicability checked in Commit.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ 1;
+ return 1;
+}
+# }}}
+
+1;
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
new file mode 100644
index 000000000..00547ebe8
--- /dev/null
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -0,0 +1,57 @@
+# This Action will resolve all members of a resolved group ticket
+
+package RT::Action::ResolveMembers;
+require RT::Action::Generic;
+require RT::Links;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self . " will resolve all members of a resolved group ticket.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+
+ my $Links=RT::Links->new($RT::SystemUser);
+ $Links->Limit(FIELD => 'Type', VALUE => 'MemberOf');
+ $Links->Limit(FIELD => 'Target', VALUE => $self->TicketObj->id);
+
+ while (my $Link=$Links->Next()) {
+ # Todo: Try to deal with remote URIs as well
+ next unless $Link->BaseIsLocal;
+ my $base=RT::Ticket->new($self->TicketObj->CurrentUser);
+ # Todo: Only work if Base is a plain ticket num:
+ $base->Load($Link->Base);
+ # I'm afraid this might be a major bottleneck if ResolveGroupTicket is on.
+ $base->Resolve;
+ }
+}
+
+
+# Applicability checked in Commit.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ 1;
+ return 1;
+}
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
new file mode 100755
index 000000000..e3abb1154
--- /dev/null
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -0,0 +1,468 @@
+# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/SendEmail.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# Copyright 1996-2002 Jesse Vincent <jesse@bestpractical.com>
+# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
+# Released under the terms of version 2 of the GNU Public License
+
+package RT::Action::SendEmail;
+require RT::Action::Generic;
+
+@ISA = qw(RT::Action::Generic);
+
+
+=head1 NAME
+
+ RT::Action::SendEmail - An Action which users can use to send mail
+ or can subclassed for more specialized mail sending behavior.
+ RT::Action::AutoReply is a good example subclass.
+
+
+=head1 SYNOPSIS
+
+ require RT::Action::SendEmail;
+ @ISA = qw(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).
+
+
+=begin testing
+
+ok (require RT::TestHarness);
+ok (require RT::Action::SendEmail);
+
+=end testing
+
+
+=head1 AUTHOR
+
+Jesse Vincent <jesse@bestpractical.com> and Tobias Brox <tobix@cpan.org>
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
+
+# {{{ 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;
+ #send the email
+
+ # 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->Message->First()) {
+ my $headers = $self->TransactionObj->Message->First->Headers();
+
+ if ($headers =~ /^RT-Squelch-Replies-To: (.*?)$/si) {
+ my @blacklist = split(/,/,$1);
+
+ # 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'}});
+ }
+ }
+ }
+
+ # 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->SetHeader('Cc', join(',' , @{$self->{'Cc'}}))
+ if (@{$self->{'Cc'}});
+ $self->SetHeader('Bcc', join(',', @{$self->{'Bcc'}}))
+ if (@{$self->{'Bcc'}});;
+
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+
+
+ $MIMEObj->make_singlepart;
+
+
+ #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->debug("$self: 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'}}) and (! $MIMEObj->head->get('To')));
+
+ if ($RT::MailCommand eq 'sendmailpipe') {
+ open (MAIL, "|$RT::SendmailPath $RT::SendmailArguments") || return(0);
+ print MAIL $MIMEObj->as_string;
+ close(MAIL);
+ }
+ else {
+ unless ($MIMEObj->send($RT::MailCommand, $RT::MailParams)) {
+ $RT::Logger->crit("$self: Could not send mail for ".
+ $self->TransactionObj . "\n");
+ return(0);
+ }
+ }
+
+ return (1);
+
+}
+# }}}
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+
+ # This actually populates the MIME::Entity fields in the Template Object
+
+ 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->TemplateObj->Parse(Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj);
+
+ # Header
+
+ $self->SetSubject();
+
+ $self->SetSubjectToken();
+
+ $self->SetRecipients();
+
+ $self->SetReturnAddress();
+
+ $self->SetRTSpecialHeaders();
+
+ return 1;
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ Deal with message headers (Set* subs, designed for easy overriding)
+
+# {{{ sub SetRTSpecialHeaders
+
+# This routine adds all the random headers that RT wants in a mail message
+# that don't matter much to anybody else.
+
+sub SetRTSpecialHeaders {
+ my $self = shift;
+
+ $self->SetReferences();
+
+ $self->SetMessageID();
+
+ $self->SetPrecedence();
+
+ $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://bestpractical.com/rt/)");
+
+ $self->SetHeader('RT-Originator', $self->TransactionObj->CreatorObj->EmailAddress);
+ return();
+
+}
+
+
+
+# {{{ 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.
+
+=cut
+
+sub SetReferences {
+ my $self = shift;
+
+ # 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.
+
+ $self->SetHeader
+ ('In-Reply-To', "<rt-".$self->TicketObj->id().
+ "\@".$RT::rtname.">");
+
+
+ # TODO We should always add References headers for all message-ids
+ # of previous messages related to this ticket.
+}
+
+# }}}
+
+# {{{ sub 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.
+
+sub SetMessageID {
+ 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-".$self->TicketObj->id().
+ "-".
+ $self->TransactionObj->id()."." .rand(20) . "\@".$RT::Organization.">")
+ unless $self->TemplateObj->MIMEObj->head->get('Message-ID');
+}
+
+
+# }}}
+
+# }}}
+
+# {{{ sub SetReturnAddress
+
+sub SetReturnAddress {
+
+ my $self = shift;
+ my %args = ( is_comment => 0,
+ @_ );
+
+ # From and Reply-To
+ # $args{is_comment} should be set if the comment address is to be used.
+ 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->TransactionObj->CreatorObj->RealName;
+
+ if ($friendly_name =~ /^\S+\@\S+$/) { # A "bare" mail address
+ $friendly_name =~ s/"/\\"/g;
+ $friendly_name = qq|"$friendly_name"|;
+ }
+
+
+ # TODO: this "via RT" should really be site-configurable.
+ $self->SetHeader('From', "\"$friendly_name via RT\" <$replyto>");
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To', "$replyto");
+ }
+
+}
+
+# }}}
+
+# {{{ sub SetHeader
+
+sub SetHeader {
+ my $self = shift;
+ my $field = shift;
+ my $val = shift;
+
+ chomp $val;
+ chomp $field;
+ $self->TemplateObj->MIMEObj->head->fold_length($field,10000);
+ $self->TemplateObj->MIMEObj->head->add($field, $val);
+ return $self->TemplateObj->MIMEObj->head->get($field);
+}
+
+# }}}
+
+# {{{ 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
+
+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
+
+=head2 SetSubject
+
+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.
+
+=cut
+
+sub SetSubject {
+ my $self = shift;
+ unless ($self->TemplateObj->MIMEObj->head->get('Subject')) {
+ my $message=$self->TransactionObj->Message;
+ my $ticket=$self->TicketObj->Id;
+
+ my $subject;
+
+ if ($self->{'Subject'}) {
+ $subject = $self->{'Subject'};
+ }
+ elsif (($message->First()) &&
+ ($message->First->Headers)) {
+ $header = $message->First->Headers();
+ $header =~ s/\n\s+/ /g;
+ if ( $header =~ /^Subject: (.*?)$/m ) {
+ $subject = $1;
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ $subject =~ s/(\r\n|\n|\s)/ /gi;
+
+ chomp $subject;
+ $self->SetHeader('Subject',$subject);
+
+ }
+ return($subject);
+}
+# }}}
+
+# {{{ sub SetSubjectToken
+
+=head2 SetSubjectToken
+
+ This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
+
+=cut
+
+sub SetSubjectToken {
+ 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");
+ }
+}
+
+# }}}
+
+# }}}
+
+__END__
+
+# {{{ POD
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Action/SendPasswordEmail.pm b/rt/lib/RT/Action/SendPasswordEmail.pm
new file mode 100755
index 000000000..91bb3c1cb
--- /dev/null
+++ b/rt/lib/RT/Action/SendPasswordEmail.pm
@@ -0,0 +1,170 @@
+# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Attic/SendPasswordEmail.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# Copyright 2001 Jesse Vincent <jesse@fsck.com>
+# Released under the terms of the GNU Public License
+
+package RT::Action::SendPasswordEmail;
+require RT::Action::Generic;
+
+@ISA = qw(RT::Action::Generic);
+
+
+=head1 NAME
+
+ RT::Action::SendGenericEmail - An Action which users can use to send mail
+ or can subclassed for more specialized mail sending behavior.
+
+
+
+=head1 SYNOPSIS
+
+ require RT::Action::SendPasswordEmail;
+
+
+=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).
+
+
+=begin testing
+
+ok (require RT::TestHarness);
+ok (require RT::Action::SendPasswordEmail);
+
+=end testing
+
+
+=head1 AUTHOR
+
+Jesse Vincent <jesse@bestpractical.com>
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
+
+# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
+
+# {{{ sub Commit
+
+#Do what we need to do and send it out.
+
+sub Commit {
+ my $self = shift;
+ #send the email
+
+
+
+
+
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+
+
+ $MIMEObj->make_singlepart;
+
+ #If we don\'t have any recipients to send to, don\'t send a message;
+ unless ($MIMEObj->head->get('To')) {
+ $RT::Logger->debug("$self: No recipients found. Not sending.\n");
+ return(1);
+ }
+
+ if ($RT::MailCommand eq 'sendmailpipe') {
+ open (MAIL, "|$RT::SendmailPath $RT::SendmailArguments") || return(0);
+ print MAIL $MIMEObj->as_string;
+ close(MAIL);
+ }
+ else {
+ unless ($MIMEObj->send($RT::MailCommand, $RT::MailParams)) {
+ $RT::Logger->crit("$self: Could not send mail for ".
+ $self->TransactionObj . "\n");
+ return(0);
+ }
+ }
+
+ return (1);
+
+}
+# }}}
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+
+ # This actually populates the MIME::Entity fields in the Template Object
+
+ unless ($self->TemplateObj) {
+ $RT::Logger->warning("No template object handed to $self\n");
+ }
+
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To',$RT::CorrespondAddress );
+ }
+
+
+ $self->SetHeader('Precedence', "bulk");
+ $self->SetHeader('X-RT-Loop-Prevention', $RT::rtname);
+ $self->SetHeader
+ ('Managed-by',"Request Tracker $RT::VERSION (http://www.fsck.com/projects/rt/)");
+
+ $self->TemplateObj->Parse(Argument => $self->Argument);
+
+
+ return 1;
+}
+
+# }}}
+
+# }}}
+
+
+# {{{ sub SetTo
+
+=head2 SetTo EMAIL
+
+Sets this message's "To" field to EMAIL
+
+=cut
+
+sub SetTo {
+ my $self = shift;
+ my $to = shift;
+ $self->SetHeader('To',$to);
+}
+
+# }}}
+
+# {{{ sub SetHeader
+
+sub SetHeader {
+ my $self = shift;
+ my $field = shift;
+ my $val = shift;
+
+ chomp $val;
+ chomp $field;
+ $self->TemplateObj->MIMEObj->head->fold_length($field,10000);
+ $self->TemplateObj->MIMEObj->head->add($field, $val);
+ return $self->TemplateObj->MIMEObj->head->get($field);
+}
+
+# }}}
+
+
+
+__END__
+
+# {{{ POD
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Action/StallDependent.pm b/rt/lib/RT/Action/StallDependent.pm
new file mode 100644
index 000000000..09d3448a8
--- /dev/null
+++ b/rt/lib/RT/Action/StallDependent.pm
@@ -0,0 +1,68 @@
+# This Action will stall the BASE if a dependency or membership link
+# (according to argument) is created and if BASE is open.
+
+# TODO: Rename this .pm
+
+package RT::Action::StallDependent;
+require RT::Action::Generic;
+@ISA=qw|RT::Action::Generic|;
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self . " will stall a [local] BASE if it's dependent [or member] of a linked up request.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ # Find all Dependent
+ my $arg=$self->Argument || "DependsOn";
+ unless ($self->TransactionObj->Data =~ /^([^ ]+) $arg /) {
+ warn; return 0;
+ }
+ my $base_id=$1;
+ my $base;
+ if ($1 eq "THIS") {
+ $base=$self->TicketObj;
+ } else {
+ $base_id=&RT::Link::_IsLocal(undef, $base_id) || return 0;
+ $base=RT::Ticket->new($self->TicketObj->CurrentUser);
+ $base->Load($base_id);
+ }
+ $base->Stall if $base->Status eq 'open';
+ return 0;
+}
+
+
+# {{{ sub IsApplicable
+
+# Only applicable if:
+# 1. the link action is a dependency
+# 2. BASE is a local ticket
+
+sub IsApplicable {
+ my $self = shift;
+
+ my $arg=$self->Argument || "DependsOn";
+
+ # 1:
+ $self->TransactionObj->Data =~ /^([^ ]*) $arg / || return 0;
+
+ # 2:
+ # (dirty!)
+ &RT::Link::_IsLocal(undef,$1) || return 0;
+
+ return 1;
+}
+# }}}
+
+1;