summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Action
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Action')
-rw-r--r--rt/lib/RT/Action/AutoOpen.pm86
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm40
-rw-r--r--rt/lib/RT/Action/CreateTickets.pm564
-rw-r--r--rt/lib/RT/Action/EscalatePriority.pm142
-rwxr-xr-xrt/lib/RT/Action/Generic.pm56
-rwxr-xr-xrt/lib/RT/Action/Notify.pm83
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm34
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm35
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm699
-rw-r--r--rt/lib/RT/Action/SetPriority.pm61
-rw-r--r--rt/lib/RT/Action/UserDefined.pm71
11 files changed, 1589 insertions, 282 deletions
diff --git a/rt/lib/RT/Action/AutoOpen.pm b/rt/lib/RT/Action/AutoOpen.pm
new file mode 100644
index 000000000..ea6da1952
--- /dev/null
+++ b/rt/lib/RT/Action/AutoOpen.pm
@@ -0,0 +1,86 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+# This Action will open the BASE if a dependent is resolved.
+
+package RT::Action::AutoOpen;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@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 );
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ # if the ticket is already open or the ticket is new and the message is more mail from the
+ # requestor, don't reopen it.
+
+ if ( ( $self->TicketObj->Status eq 'open' )
+ || ( ( $self->TicketObj->Status eq 'new' )
+ && $self->TransactionObj->IsInbound )
+ ) {
+
+ return undef;
+ }
+ else {
+ return (1);
+ }
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ my $oldstatus = $self->TicketObj->Status();
+ $self->TicketObj->__Set( Field => 'Status', Value => 'open' );
+ $self->TicketObj->_NewTransaction(
+ Type => 'Set',
+ Field => 'Status',
+ OldValue => $oldstatus,
+ NewValue => 'open',
+ Data => 'Ticket auto-opened on incoming correspondence'
+ );
+
+
+ return(1);
+}
+
+eval "require RT::Action::AutoOpen_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/AutoOpen_Vendor.pm});
+eval "require RT::Action::AutoOpen_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/AutoOpen_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
index 624888e94..81f7bddfa 100755
--- a/rt/lib/RT/Action/Autoreply.pm
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -1,7 +1,31 @@
-#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Autoreply.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
-
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
package RT::Action::Autoreply;
require RT::Action::SendEmail;
+
+use strict;
+use vars qw/@ISA/;
@ISA = qw(RT::Action::SendEmail);
@@ -17,7 +41,7 @@ Sets the recipients of this message to this ticket's Requestor.
sub SetRecipients {
my $self=shift;
- push(@{$self->{'To'}}, @{$self->TicketObj->Requestors->Emails});
+ push(@{$self->{'To'}}, $self->TicketObj->Requestors->MemberEmailAddresses);
return(1);
}
@@ -39,6 +63,7 @@ sub SetReturnAddress {
@_
);
+ my $replyto;
if ($args{'is_comment'}) {
$replyto = $self->TicketObj->QueueObj->CommentAddress ||
$RT::CommentAddress;
@@ -49,7 +74,9 @@ sub SetReturnAddress {
}
unless ($self->TemplateObj->MIMEObj->head->get('From')) {
- my $friendly_name=$self->TicketObj->QueueObj->Name;
+ my $friendly_name = $self->TicketObj->QueueObj->Description ||
+ $self->TicketObj->QueueObj->Name;
+ $friendly_name =~ s/"/\\"/g;
$self->SetHeader('From', "\"$friendly_name\" <$replyto>");
}
@@ -61,4 +88,9 @@ sub SetReturnAddress {
# }}}
+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});
+
1;
diff --git a/rt/lib/RT/Action/CreateTickets.pm b/rt/lib/RT/Action/CreateTickets.pm
new file mode 100644
index 000000000..0ab206771
--- /dev/null
+++ b/rt/lib/RT/Action/CreateTickets.pm
@@ -0,0 +1,564 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+package RT::Action::CreateTickets;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+use MIME::Entity;
+
+=head1 NAME
+
+ RT::Action::CreateTickets
+
+Create one or more tickets according to an externally supplied template.
+
+
+=head1 SYNOPSIS
+
+ ===Create-Ticket: codereview
+ Subject: Code review for {$Tickets{'TOP'}->Subject}
+ Depended-On-By: {$Tickets{'TOP'}->Id}
+ Content: Someone has created a ticket. you should review and approve it,
+ so they can finish their work
+ ENDOFCONTENT
+
+=head1 DESCRIPTION
+
+
+Using the "CreateTickets" ScripAction and mandatory dependencies, RT now has
+the ability to model complex workflow. When a ticket is created in a queue
+that has a "CreateTickets" scripaction, that ScripAction parses its "Template"
+
+
+
+=head2 FORMAT
+
+CreateTickets uses the template as a template for an ordered set of tickets
+to create. The basic format is as follows:
+
+
+ ===Create-Ticket: identifier
+ Param: Value
+ Param2: Value
+ Param3: Value
+ Content: Blah
+ blah
+ blah
+ ENDOFCONTENT
+ ===Create-Ticket: id2
+ Param: Value
+ Content: Blah
+ ENDOFCONTENT
+
+
+Each ===Create-Ticket: section is evaluated as its own
+Text::Template object, which means that you can embed snippets
+of perl inside the Text::Template using {} delimiters, but that
+such sections absolutely can not span a ===Create-Ticket boundary.
+
+After each ticket is created, it's stuffed into a hash called %Tickets
+so as to be available during the creation of other tickets during the same
+ScripAction. The hash is prepopulated with the ticket which triggered the
+ScripAction as $Tickets{'TOP'}; you can also access that ticket using the
+shorthand $TOP.
+
+A simple example:
+
+ ===Create-Ticket: codereview
+ Subject: Code review for {$Tickets{'TOP'}->Subject}
+ Depended-On-By: {$Tickets{'TOP'}->Id}
+ Content: Someone has created a ticket. you should review and approve it,
+ so they can finish their work
+ ENDOFCONTENT
+
+
+
+A convoluted example
+
+ ===Create-Ticket: approval
+ { # Find out who the administrators of the group called "HR"
+ # of which the creator of this ticket is a member
+ my $name = "HR";
+
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->LimitToUserDefinedGroups();
+ $groups->Limit(FIELD => "Name", OPERATOR => "=", VALUE => "$name");
+ $groups->WithMember($TransactionObj->CreatorObj->Id);
+
+ my $groupid = $groups->First->Id;
+
+ my $adminccs = RT::Users->new($RT::SystemUser);
+ $adminccs->WhoHaveRight(
+ Right => "AdminGroup",
+ Object =>$groups->First,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => 0,
+ IncludeSubgroupMembers => 0,
+ );
+
+ my @admins;
+ while (my $admin = $adminccs->Next) {
+ push (@admins, $admin->EmailAddress);
+ }
+ }
+ Queue: Approvals
+ Type: Approval
+ AdminCc: {join ("\nAdminCc: ",@admins) }
+ Depended-On-By: {$Tickets{"TOP"}->Id}
+ Refers-To: {$Tickets{"TOP"}->Id}
+ Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
+ Due: {time + 86400}
+ Content-Type: text/plain
+ Content: Your approval is requested for the ticket {$Tickets{"TOP"}->Id}: {$Tickets{"TOP"}->Subject}
+ Blah
+ Blah
+ ENDOFCONTENT
+ ===Create-Ticket: two
+ Subject: Manager approval
+ Depended-On-By: {$Tickets{"TOP"}->Id}
+ Refers-On: {$Tickets{"approval"}->Id}
+ Queue: Approvals
+ Content-Type: text/plain
+ Content:
+ Your approval is requred for this ticket, too.
+ ENDOFCONTENT
+
+=head2 Acceptable fields
+
+A complete list of acceptable fields for this beastie:
+
+
+ * Queue => Name or id# of a queue
+ Subject => A text string
+ ! Status => A valid status. defaults to 'new'
+ Due => Dates can be specified in seconds since the epoch
+ to be handled literally or in a semi-free textual
+ format which RT will attempt to parse.
+
+
+
+ Starts =>
+ Started =>
+ Resolved =>
+ Owner => Username or id of an RT user who can and should own
+ this ticket
+ + Requestor => Email address
+ + Cc => Email address
+ + AdminCc => Email address
+ TimeWorked =>
+ TimeEstimated =>
+ TimeLeft =>
+ InitialPriority =>
+ FinalPriority =>
+ Type =>
+ +! DependsOn =>
+ +! DependedOnBy =>
+ +! RefersTo =>
+ +! ReferredToBy =>
+ +! Members =>
+ +! MemberOf =>
+ Content => content. Can extend to multiple lines. Everything
+ within a template after a Content: header is treated
+ as content until we hit a line containing only
+ ENDOFCONTENT
+ ContentType => the content-type of the Content field
+ CustomField-<id#> => custom field value
+
+Fields marked with an * are required.
+
+Fields marked with a + man have multiple values, simply
+by repeating the fieldname on a new line with an additional value.
+
+Fields marked with a ! are postponed to be processed after all
+tickets in the same actions are created. Except for 'Status', those
+field can also take a ticket name within the same action (i.e.
+the identifiers after ==Create-Ticket), instead of raw Ticket ID
+numbers.
+
+When parsed, field names are converted to lowercase and have -s stripped.
+Refers-To, RefersTo, refersto, refers-to and r-e-f-er-s-tO will all
+be treated as the same thing.
+
+
+=begin testing
+
+ok (require RT::Action::CreateTickets);
+use_ok(RT::Scrip);
+use_ok(RT::Template);
+use_ok(RT::ScripAction);
+use_ok(RT::ScripCondition);
+use_ok(RT::Ticket);
+
+my $approvalsq = RT::Queue->new($RT::SystemUser);
+$approvalsq->Create(Name => 'Approvals');
+ok ($approvalsq->Id, "Created Approvals test queue");
+
+
+my $approvals =
+'===Create-Ticket: approval
+{ my $name = "HR";
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->LimitToUserDefinedGroups();
+ $groups->Limit(FIELD => "Name", OPERATOR => "=", VALUE => "$name");
+ $groups->WithMember($Transaction->CreatorObj->Id);
+
+ my $groupid = $groups->First->Id;
+
+ my $adminccs = RT::Users->new($RT::SystemUser);
+ $adminccs->WhoHaveRight(Right => "AdminGroup", IncludeSystemRights => undef, IncludeSuperusers => 0, IncludeSubgroupMembers => 0, Object => $groups->First);
+
+ my @admins;
+ while (my $admin = $adminccs->Next) {
+ push (@admins, $admin->EmailAddress);
+ }
+}
+Queue: Approvals
+Type: Approval
+AdminCc: {join ("\nAdminCc: ",@admins) }
+Depended-On-By: {$Tickets{"TOP"}->Id}
+Refers-To: {$Tickets{"TOP"}->Id}
+Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
+Due: {time + 86400}
+Content-Type: text/plain
+Content: Your approval is requested for the ticket {$Tickets{"TOP"}->Id}: {$Tickets{"TOP"}->Subject}
+Blah
+Blah
+ENDOFCONTENT
+===Create-Ticket: two
+Subject: Manager approval.
+Depends-On: {$Tickets{"approval"}->Id}
+Queue: Approvals
+Content-Type: text/plain
+Content:
+Your minion approved this ticket. you ok with that?
+ENDOFCONTENT
+';
+
+ok ($approvals =~ /Content/, "Read in the approvals template");
+
+my $apptemp = RT::Template->new($RT::SystemUser);
+$apptemp->Create( Content => $approvals, Name => "Approvals", Queue => "0");
+
+ok ($apptemp->Id);
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name => 'WorkflowTest');
+ok ($q->Id, "Created workflow test queue");
+
+my $scrip = RT::Scrip->new($RT::SystemUser);
+my ($sval, $smsg) =$scrip->Create( ScripCondition => 'On Transaction',
+ ScripAction => 'Create Tickets',
+ Template => 'Approvals',
+ Queue => $q->Id);
+ok ($sval, $smsg);
+ok ($scrip->Id, "Created the scrip");
+ok ($scrip->TemplateObj->Id, "Created the scrip template");
+ok ($scrip->ConditionObj->Id, "Created the scrip condition");
+ok ($scrip->ActionObj->Id, "Created the scrip action");
+
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Create(Subject => "Sample workflow test",
+ Owner => "root",
+ Queue => $q->Id);
+
+
+=end testing
+
+
+=head1 AUTHOR
+
+Jesse Vincent <jesse@bestpractical.com>
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
+
+my %LINKTYPEMAP = (
+ MemberOf => { Type => 'MemberOf',
+ Mode => 'Target', },
+ Members => { Type => 'MemberOf',
+ Mode => 'Base', },
+ HasMember => { Type => 'MemberOf',
+ Mode => 'Base', },
+ RefersTo => { Type => 'RefersTo',
+ Mode => 'Target', },
+ ReferredToBy => { Type => 'RefersTo',
+ Mode => 'Base', },
+ DependsOn => { Type => 'DependsOn',
+ Mode => 'Target', },
+ DependedOnBy => { Type => 'DependsOn',
+ Mode => 'Base', },
+
+);
+
+# {{{ Scrip methods (Commit, Prepare)
+
+# {{{ sub Commit
+#Do what we need to do and send it out.
+sub Commit {
+ my $self = shift;
+ my (@links, @postponed);
+
+ # XXX: cargo cult programming that works. i'll be back.
+ use bytes;
+
+ # Create all the tickets we care about
+ return(1) unless $self->TicketObj->Type eq 'ticket';
+
+ %T::Tickets = ();
+
+ foreach my $template_id ( @{ $self->{'template_order'} } ) {
+ $T::Tickets{'TOP'} = $T::TOP = $self->TicketObj;
+ $RT::Logger->debug("Workflow: processing $template_id of $T::TOP");
+
+ $T::ID = $template_id;
+ @T::AllID = @{ $self->{'template_order'} };
+
+ my $template = Text::Template->new(
+ TYPE => 'STRING',
+ SOURCE => $self->{'templates'}->{$template_id}
+ );
+
+ $RT::Logger->debug("Workflow: evaluating\n$self->{templates}{$template_id}");
+
+ my $err;
+ my $filled_in = $template->fill_in( PACKAGE => 'T', BROKEN => sub {
+ $err = { @_ }->{error};
+ } );
+
+ $RT::Logger->debug("Workflow: yielding\n$filled_in");
+
+ if ($err) {
+ $RT::Logger->error("Ticket creation failed for ".$self->TicketObj->Id." ".$err);
+ while (my ($k, $v) = each %T::X) {
+ $RT::Logger->debug("Eliminating $template_id from ${k}'s parents.");
+ delete $v->{$template_id};
+ }
+ next;
+ }
+
+ my %args;
+ my @lines = ( split ( /\n/, $filled_in ) );
+ while ( defined(my $line = shift @lines) ) {
+ if ( $line =~ /^(.*?):(?:\s+(.*))?$/ ) {
+ my $value = $2;
+ my $tag = lc ($1);
+ $tag =~ s/-//g;
+
+ if (ref($args{$tag})) { #If it's an array, we want to push the value
+ push @{$args{$tag}}, $value;
+ }
+ elsif (defined ($args{$tag})) { #if we're about to get a second value, make it an array
+ $args{$tag} = [$args{$tag}, $value];
+ }
+ else { #if there's nothing there, just set the value
+ $args{ $tag } = $value;
+ }
+
+ if ( $tag eq 'content' ) { #just build up the content
+ # convert it to an array
+ $args{$tag} = defined($value) ? [ $value."\n" ] : [];
+ while ( defined(my $l = shift @lines) ) {
+ last if ($l =~ /^ENDOFCONTENT\s*$/) ;
+ push @{$args{'content'}}, $l."\n";
+ }
+ }
+ }
+ }
+
+ foreach my $date qw(due starts started resolved) {
+ my $dateobj = RT::Date->new($RT::SystemUser);
+ next unless $args{$date};
+ if ($args{$date} =~ /^\d+$/) {
+ $dateobj->Set(Format => 'unix', Value => $args{$date});
+ } else {
+ $dateobj->Set(Format => 'unknown', Value => $args{$date});
+ }
+ $args{$date} = $dateobj->ISO;
+ }
+ my $mimeobj = MIME::Entity->new();
+ $mimeobj->build(Type => $args{'contenttype'},
+ Data => $args{'content'});
+ # Now we have a %args to work with.
+ # Make sure we have at least the minimum set of
+ # reasonable data and do our thang
+ $T::Tickets{$template_id} ||= RT::Ticket->new($RT::SystemUser);
+
+ # Deferred processing
+ push @links, (
+ $T::Tickets{$template_id}, {
+ DependsOn => $args{'dependson'},
+ DependedOnBy => $args{'dependedonby'},
+ RefersTo => $args{'refersto'},
+ ReferredToBy => $args{'referredtoby'},
+ Members => $args{'members'},
+ MemberOf => $args{'memberof'},
+ }
+ );
+
+ push @postponed, (
+ # Status is postponed so we don't violate dependencies
+ $T::Tickets{$template_id}, {
+ Status => $args{'status'},
+ }
+ );
+
+ $args{'requestor'} ||= $self->TicketObj->Requestors->MemberEmailAddresses;
+
+ my %ticketargs = ( Queue => $args{'queue'},
+ Subject=> $args{'subject'},
+ Status => 'new',
+ Due => $args{'due'},
+ Starts => $args{'starts'},
+ Started => $args{'started'},
+ Resolved => $args{'resolved'},
+ Owner => $args{'owner'},
+ Requestor => $args{'requestor'},
+ Cc => $args{'cc'},
+ AdminCc=> $args{'admincc'},
+ TimeWorked =>$args{'timeworked'},
+ TimeEstimated =>$args{'timeestimated'},
+ TimeLeft =>$args{'timeleft'},
+ InitialPriority => $args{'initialpriority'},
+ FinalPriority => $args{'finalpriority'},
+ Type => $args{'type'},
+ MIMEObj => $mimeobj);
+
+
+ foreach my $key (keys(%args)) {
+ $key =~ /^customfield-(\d+)$/ or next;
+ $ticketargs{ "CustomField-" . $1 } = $args{$key};
+ }
+
+ my ($id, $transid, $msg) = $T::Tickets{$template_id}->Create(%ticketargs);
+ if (!$id) {
+ $RT::Logger->error(
+ "Couldn't create related ticket $template_id for ".
+ $self->TicketObj->Id." ".$msg
+ );
+ next;
+ }
+
+ $RT::Logger->debug("Assigned $template_id with $id");
+ $T::Tickets{$template_id}->SetOriginObj($self->TicketObj)
+ if $T::Tickets{$template_id}->can('SetOriginObj');
+ }
+
+ # postprocessing: add links
+
+ while (my $ticket = shift(@links)) {
+ $RT::Logger->debug("Handling links for " . $ticket->Id);
+ my %args = %{shift(@links)};
+
+ foreach my $type ( keys %LINKTYPEMAP ) {
+ next unless (defined $args{$type});
+ foreach my $link (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ if (!exists $T::Tickets{$link}) {
+ $RT::Logger->debug("Skipping $type link for $link (non-existent)");
+ next;
+ }
+ $RT::Logger->debug("Building $type link for $link: " . $T::Tickets{$link}->Id);
+ $link = $T::Tickets{$link}->Id;
+
+ my ( $wval, $wmsg ) = $ticket->AddLink(
+ Type => $LINKTYPEMAP{$type}->{'Type'},
+ $LINKTYPEMAP{$type}->{'Mode'} => $link,
+ Silent => 1
+ );
+
+ $RT::Logger->warning("AddLink thru $link failed: $wmsg") unless $wval;
+ # push @non_fatal_errors, $wmsg unless ($wval);
+ }
+
+ }
+ }
+
+ # postponed actions -- Status only, currently
+ while (my $ticket = shift(@postponed)) {
+ $RT::Logger->debug("Handling postponed actions for $ticket");
+ my %args = %{shift(@postponed)};
+
+ $ticket->SetStatus($args{Status}) if defined $args{Status};
+ }
+
+ return(1);
+}
+# }}}
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+
+ unless ($self->TemplateObj) {
+ $RT::Logger->warning("No template object handed to $self\n");
+ }
+
+ unless ($self->TransactionObj) {
+ $RT::Logger->warning("No transaction object handed to $self\n");
+
+ }
+
+ unless ($self->TicketObj) {
+ $RT::Logger->warning("No ticket object handed to $self\n");
+
+ }
+
+
+
+
+my $template_id;
+foreach my $line (split(/\n/,$self->TemplateObj->Content)) {
+ if ($line =~ /^===Create-Ticket: (.*)$/) {
+ $template_id = $1;
+ push @{$self->{'template_order'}},$template_id;
+ } else {
+ $self->{'templates'}->{$template_id} .= $line."\n";
+ }
+
+
+}
+
+ return 1;
+
+}
+
+# }}}
+
+# }}}
+
+eval "require RT::Action::CreateTickets_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Vendor.pm});
+eval "require RT::Action::CreateTickets_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Action/EscalatePriority.pm b/rt/lib/RT/Action/EscalatePriority.pm
new file mode 100644
index 000000000..7ed63ae01
--- /dev/null
+++ b/rt/lib/RT/Action/EscalatePriority.pm
@@ -0,0 +1,142 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+=head1 NAME
+
+ RT::Action::EscalatePriority
+
+=head1 DESCRIPTION
+
+EscalatePriority is a ScripAction which is NOT intended to be called per
+transaction. It's intended to be called by an RT escalation daemon.
+(The daemon is called escalator).
+
+EsclatePriority uses the following formula to change a ticket's priority:
+
+ Priority = Priority + (( FinalPriority - Priority ) / ( DueDate-Today))
+
+Unless the duedate is past, in which case priority gets bumped straight
+to final priority.
+
+In this way, priority is either increased or decreased toward the final priority
+as the ticket heads toward its due date.
+
+
+=cut
+
+
+package RT::Action::EscalatePriority;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@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 move a ticket's priority toward its final priority.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ if ($self->TicketObj->Priority() == $self->TicketObj->FinalPriority()) {
+ # no update necessary.
+ return 0;
+ }
+
+ #compute the number of days until the ticket is due
+ my $due = $self->TicketObj->DueObj();
+
+
+ # If we don't have a due date, adjust the priority by one
+ # until we hit the final priority
+ if ($due->Unix() < 1) {
+ if ( $self->TicketObj->Priority > $self->TicketObj->FinalPriority ){
+ $self->{'prio'} = ($self->TicketObj->Priority - 1);
+ return 1;
+ }
+ elsif ( $self->TicketObj->Priority < $self->TicketObj->FinalPriority ){
+ $self->{'prio'} = ($self->TicketObj->Priority + 1);
+ return 1;
+ }
+ # otherwise the priority is at the final priority. we don't need to
+ # Continue
+ else {
+ return 0;
+ }
+ }
+
+ # we've got a due date. now there are other things we should do
+ else {
+ my $diff_in_seconds = $due->Diff(time());
+ my $diff_in_days = int( $diff_in_seconds / 86400);
+
+ #if we haven't hit the due date yet
+ if ($diff_in_days > 0 ) {
+
+ # compute the difference between the current priority and the
+ # final priority
+
+ my $prio_delta =
+ $self->TicketObj->FinalPriority() - $self->TicketObj->Priority;
+
+ my $inc_priority_by = int( $prio_delta / $diff_in_days );
+
+ #set the ticket's priority to that amount
+ $self->{'prio'} = $self->TicketObj->Priority + $inc_priority_by;
+
+ }
+ #if $days is less than 1, set priority to final_priority
+ else {
+ $self->{'prio'} = $self->TicketObj->FinalPriority();
+ }
+
+ }
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ my ($val, $msg) = $self->TicketObj->SetPriority($self->{'prio'});
+
+ unless ($val) {
+ $RT::Logger->debug($self . " $msg\n");
+ }
+}
+
+eval "require RT::Action::EscalatePriority_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/EscalatePriority_Vendor.pm});
+eval "require RT::Action::EscalatePriority_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/EscalatePriority_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
index ecfd4ab1a..007d299c7 100755
--- a/rt/lib/RT/Action/Generic.pm
+++ b/rt/lib/RT/Action/Generic.pm
@@ -1,7 +1,26 @@
-# $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
-
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
=head1 NAME
RT::Action::Generic - a generic baseclass for RT Actions
@@ -16,7 +35,6 @@
=begin testing
-ok (require RT::TestHarness);
ok (require RT::Action::Generic);
=end testing
@@ -25,6 +43,8 @@ ok (require RT::Action::Generic);
package RT::Action::Generic;
+use strict;
+
# {{{ sub new
sub new {
my $proto = shift;
@@ -36,6 +56,13 @@ sub new {
}
# }}}
+# {{{ sub new
+sub loc {
+ my $self = shift;
+ return $self->{'ScripObj'}->loc(@_);
+}
+# }}}
+
# {{{ sub _Init
sub _Init {
my $self = shift;
@@ -87,6 +114,13 @@ sub TemplateObj {
}
# }}}
+# {{{ sub ScripObj
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+
# {{{ sub Type
sub Type {
my $self = shift;
@@ -102,7 +136,7 @@ sub Type {
# {{{ sub Commit
sub Commit {
my $self = shift;
- return(0,"Commit Stubbed");
+ return(0, $self->loc("Commit Stubbed"));
}
# }}}
@@ -112,7 +146,7 @@ sub Commit {
# {{{ sub Describe
sub Describe {
my $self = shift;
- return ("No description for " . ref $self);
+ return $self->loc("No description for [_1]", ref $self);
}
# }}}
@@ -122,7 +156,7 @@ sub Describe {
# {{{ sub Prepare
sub Prepare {
my $self = shift;
- return (0,"Prepare Stubbed");
+ return (0, $self->loc("Prepare Stubbed"));
}
# }}}
@@ -152,4 +186,10 @@ sub DESTROY {
}
# }}}
+
+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});
+
1;
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
index 6dca4fd41..1e4e4c073 100755
--- a/rt/lib/RT/Action/Notify.pm
+++ b/rt/lib/RT/Action/Notify.pm
@@ -1,7 +1,31 @@
-#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/Notify.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
-
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
package RT::Action::Notify;
require RT::Action::SendEmail;
+
+use strict;
+use vars qw/@ISA/;
@ISA = qw(RT::Action::SendEmail);
# {{{ sub SetRecipients
@@ -9,14 +33,14 @@ require RT::Action::SendEmail;
=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.
+Explicitly B<does not> notify the creator of the transaction by default
=cut
sub SetRecipients {
my $self = shift;
- $arg = $self->Argument;
+ my $arg = $self->Argument;
$arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
@@ -24,14 +48,14 @@ sub SetRecipients {
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 ($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 =~ /\bRequestor\b/ ) {
- push ( @To, @{ $self->TicketObj->Requestors->Emails } );
+ push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses );
}
@@ -40,12 +64,12 @@ sub SetRecipients {
#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 } );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
else {
- push ( @Cc, @{ $self->TicketObj->Cc->Emails } );
- push ( @To, @{ $self->TicketObj->QueueObj->Cc->Emails } );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
}
@@ -65,15 +89,16 @@ sub SetRecipients {
}
if ( $arg =~ /\bAdminCc\b/ ) {
- push ( @Bcc, @{ $self->TicketObj->AdminCc->Emails } );
- push ( @Bcc, @{ $self->TicketObj->QueueObj->AdminCc->Emails } );
+ push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses );
}
if ($RT::UseFriendlyToLine) {
unless (@To) {
- push ( @PseudoTo,
- "\"$arg of $RT::rtname Ticket #"
- . $self->TicketObj->id . "\":;" );
+ push (
+ @PseudoTo,
+ sprintf($RT::FriendlyToLineFormat, $arg, $self->TicketObj->id),
+ );
}
}
@@ -81,14 +106,17 @@ sub SetRecipients {
#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 );
+ # unless a flag is set
+ if ($RT::NotifyActor) {
+ @{ $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->{'PseudoTo'} } = @PseudoTo;
return (1);
@@ -96,4 +124,9 @@ sub SetRecipients {
# }}}
+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});
+
1;
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
index c72bfff13..210e4ab15 100755
--- a/rt/lib/RT/Action/NotifyAsComment.pm
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -1,7 +1,31 @@
-#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Action/NotifyAsComment.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
-
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
package RT::Action::NotifyAsComment;
require RT::Action::Notify;
+
+use strict;
+use vars qw/@ISA/;
@ISA = qw(RT::Action::Notify);
@@ -21,5 +45,11 @@ sub SetReturnAddress {
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});
+
1;
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
index 00547ebe8..02ff3a58c 100644
--- a/rt/lib/RT/Action/ResolveMembers.pm
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -1,8 +1,34 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
# This Action will resolve all members of a resolved group ticket
package RT::Action::ResolveMembers;
require RT::Action::Generic;
require RT::Links;
+
+use strict;
+use vars qw/@ISA/;
@ISA=qw(RT::Action::Generic);
#Do what we need to do and send it out.
@@ -12,7 +38,7 @@ require RT::Links;
# {{{ sub Describe
sub Describe {
my $self = shift;
- return (ref $self . " will resolve all members of a resolved group ticket.");
+ return $self->loc("[_1] will resolve all members of a resolved group ticket.", ref $self);
}
# }}}
@@ -33,7 +59,7 @@ sub Commit {
while (my $Link=$Links->Next()) {
# Todo: Try to deal with remote URIs as well
- next unless $Link->BaseIsLocal;
+ next unless $Link->BaseURI->IsLocal;
my $base=RT::Ticket->new($self->TicketObj->CurrentUser);
# Todo: Only work if Base is a plain ticket num:
$base->Load($Link->Base);
@@ -53,5 +79,10 @@ 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});
+
1;
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
index e3abb1154..dac8fc8e7 100755
--- a/rt/lib/RT/Action/SendEmail.pm
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -1,20 +1,44 @@
-# $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>
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
# 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;
+use strict;
+use vars qw/@ISA/;
@ISA = qw(RT::Action::Generic);
+use MIME::Words qw(encode_mimeword);
-=head1 NAME
+use RT::EmailParser;
- 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 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
@@ -36,7 +60,6 @@ the comments for the SetRecipients sub).
=begin testing
-ok (require RT::TestHarness);
ok (require RT::Action::SendEmail);
=end testing
@@ -54,158 +77,266 @@ perl(1).
# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
-# {{{ sub _Init
+# {{{ sub _Init
# We use _Init from RT::Action
# }}}
-# {{{ sub Commit
+# {{{ sub Commit
#Do what we need to do and send it out.
-sub Commit {
+sub Commit {
my $self = shift;
+
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+ my $msgid = $MIMEObj->head->get('Message-Id');
+ chomp $msgid;
+ $RT::Logger->info($msgid." #".$self->TicketObj->id."/".$self->TransactionObj->id." - Scrip ". $self->ScripObj->id ." ".$self->ScripObj->Description);
#send the email
-
+
+ # Weed out any RT addresses. We really don't want to talk to ourselves!
+ @{$self->{'To'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'To'}});
+ @{$self->{'Cc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Cc'}});
+ @{$self->{'Bcc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Bcc'}});
# If there are no recipients, don't try to send the message.
# If the transaction has content and has the header RT-Squelch-Replies-To
-
- if (defined $self->TransactionObj->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'}});
- }
- }
+
+ if ( defined $self->TransactionObj->Attachments->First() ) {
+
+ my $squelch = $self->TransactionObj->Attachments->First->GetHeader( 'RT-Squelch-Replies-To');
+
+ if ($squelch) {
+ my @blacklist = split ( /,/, $squelch );
+
+ # Cycle through the people we're sending to and pull out anyone on the
+ # system blacklist
+
+ foreach my $person_to_yank (@blacklist) {
+ $person_to_yank =~ s/\s//g;
+ @{ $self->{'To'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'To'} } );
+ @{ $self->{'Cc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Cc'} } );
+ @{ $self->{'Bcc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Bcc'} } );
+ }
+ }
}
-
- # Go add all the Tos, Ccs and Bccs that we need to to the message to
+
+ # 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;
-
-
+ $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'} } );
+
+
+ $self->SetHeader('MIME-Version', '1.0');
+
+ # try to convert message body from utf-8 to $RT::EmailOutputEncoding
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="utf-8"' );
+
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, 'mime_words_ok' );
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="' . $RT::EmailOutputEncoding . '"' );
+
+
+ # Build up a MIME::Entity that looks like the original message.
+
+ my $do_attach = $self->TemplateObj->MIMEObj->head->get('RT-Attach-Message');
+
+ if ($do_attach) {
+ $self->TemplateObj->MIMEObj->head->delete('RT-Attach-Message');
+
+ my $attachments = RT::Attachments->new($RT::SystemUser);
+ $attachments->Limit( FIELD => 'TransactionId',
+ VALUE => $self->TransactionObj->Id );
+ $attachments->OrderBy('id');
+
+ my $transaction_content_obj = $self->TransactionObj->ContentObj;
+
+ # attach any of this transaction's attachments
+ while ( my $attach = $attachments->Next ) {
+
+ # Don't attach anything blank
+ next unless ( $attach->ContentLength );
+
+ # We want to make sure that we don't include the attachment that's being sued as the "Content" of this message"
+ next
+ if ( $transaction_content_obj
+ && $transaction_content_obj->Id == $attach->Id
+ && $transaction_content_obj->ContentType =~ qr{text/plain}i
+ );
+ $MIMEObj->make_multipart('mixed');
+ $MIMEObj->attach( Type => $attach->ContentType,
+ Charset => $attach->OriginalEncoding,
+ Data => $attach->OriginalContent,
+ Filename => $self->MIMEEncodeString( $attach->Filename, $RT::EmailOutputEncoding ),
+ Encoding => '-SUGGEST');
+ }
+
+ }
+
+
+ my $retval = $self->SendMessage($MIMEObj);
+
+
+ return ($retval);
+}
+
+# }}}
+
+# {{{ 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");
+
+ }
+
+ my ( $result, $message ) = $self->TemplateObj->Parse(
+ Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj
+ );
+ if ($result) {
+
+ # Header
+ $self->SetSubject();
+ $self->SetSubjectToken();
+ $self->SetRecipients();
+ $self->SetReturnAddress();
+ $self->SetRTSpecialHeaders();
+ if ($RT::EmailOutputEncoding) {
+
+ # l10n related header
+ $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding );
+ }
+ }
+
+ return $result;
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ SendMessage
+=head2 SendMessage MIMEObj
+
+sends the message using RT's preferred API.
+TODO: Break this out to a seperate module
+
+=cut
+
+sub SendMessage {
+ my $self = shift;
+ my $MIMEObj = shift;
+
+ my $msgid = $MIMEObj->head->get('Message-Id');
+
+
#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);
+ unless ( $MIMEObj->head->get('To')
+ || $MIMEObj->head->get('Cc')
+ || $MIMEObj->head->get('Bcc') ) {
+ $RT::Logger->info($msgid. " No recipients found. Not sending.\n");
+ return (1);
}
# PseudoTo (fake to headers) shouldn't get matched for message recipients.
# If we don't have any 'To' header, drop in the pseudo-to header.
- $self->SetHeader('To', join(',', @{$self->{'PseudoTo'}}))
- if ( (@{$self->{'PseudoTo'}}) 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);
+ $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. -".$@ );
+ }
}
else {
- unless ($MIMEObj->send($RT::MailCommand, $RT::MailParams)) {
- $RT::Logger->crit("$self: Could not send mail for ".
- $self->TransactionObj . "\n");
- return(0);
+ my @mailer_args = ($RT::MailCommand);
+ local $ENV{MAILADDRESS};
+
+ 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;
}
+
+ unless ( $MIMEObj->send( @mailer_args ) ) {
+ $RT::Logger->crit($msgid. "Could not send mail." );
+ 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();
+ 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);
- $self->SetRTSpecialHeaders();
-
- return 1;
-
+ 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.
+=head2 SetRTSpecialHeaders
+
+This routine adds all the random headers that RT wants in a mail message
+that don't matter much to anybody else.
+
+=cut
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();
-
-}
+ $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname );
+ $self->SetHeader( 'RT-Ticket',
+ $RT::rtname . " #" . $self->TicketObj->id() );
+ $self->SetHeader( 'Managed-by',
+ "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
+ $self->SetHeader( 'RT-Originator',
+ $self->TransactionObj->CreatorObj->EmailAddress );
+ return ();
+}
# {{{ sub SetReferences
@@ -218,105 +349,126 @@ sub SetRTSpecialHeaders {
=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.
+ my $self = shift;
- $self->SetHeader
- ('In-Reply-To', "<rt-".$self->TicketObj->id().
- "\@".$RT::rtname.">");
+ # 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.
+ # 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.
+=head2 SetMessageID
-sub SetMessageID {
- my $self = shift;
+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.
- # 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#"
+=cut
+
+sub SetMessageID {
+ my $self = shift;
- $self->SetHeader
- ('Message-ID', "<rt-".$self->TicketObj->id().
- "-".
- $self->TransactionObj->id()."." .rand(20) . "\@".$RT::Organization.">")
+ # 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');
}
-
# }}}
# }}}
-# {{{ sub SetReturnAddress
+# {{{ sub SetReturnAddress
+
+=head2 SetReturnAddress is_comment => BOOLEAN
+
+Calculate and set From and Reply-To headers based on the is_comment flag.
+
+=cut
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");
- }
-
+ 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') ) {
+ if ($RT::UseFriendlyFromLine) {
+ my $friendly_name = $self->TransactionObj->CreatorObj->RealName;
+ if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
+ $friendly_name = $1;
+ }
+
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader( 'From',
+ sprintf($RT::FriendlyFromLineFormat,
+ $self->MIMEEncodeString( $friendly_name, $RT::EmailOutputEncoding ), $replyto),
+ );
+ }
+ else {
+ $self->SetHeader( 'From', $replyto );
+ }
+ }
+
+ unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) {
+ $self->SetHeader( 'Reply-To', "$replyto" );
+ }
+
}
# }}}
# {{{ sub SetHeader
+=head2 SetHeader FIELD, VALUE
+
+Set the FIELD of the current MIME object into VALUE.
+
+=cut
+
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);
+ 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->replace( $field, $val );
+ return $self->TemplateObj->MIMEObj->head->get($field);
}
# }}}
@@ -331,21 +483,29 @@ Dummy method to be overriden by subclasses which want to set the recipients.
sub SetRecipients {
my $self = shift;
- return();
+ return ();
}
# }}}
# {{{ sub SetTo
+=head2 SetTo
+
+Takes a string that is the addresses you want to send mail to
+
+=cut
+
sub SetTo {
- my $self=shift;
+ my $self = shift;
my $addresses = shift;
- return $self->SetHeader('To',$addresses);
+ return $self->SetHeader( 'To', $addresses );
}
+
# }}}
# {{{ sub SetCc
+
=head2 SetCc
Takes a string that is the addresses you want to Cc
@@ -353,11 +513,12 @@ Takes a string that is the addresses you want to Cc
=cut
sub SetCc {
- my $self=shift;
+ my $self = shift;
my $addresses = shift;
- return $self->SetHeader('Cc', $addresses);
+ return $self->SetHeader( 'Cc', $addresses );
}
+
# }}}
# {{{ sub SetBcc
@@ -367,23 +528,24 @@ sub SetCc {
Takes a string that is the addresses you want to Bcc
=cut
+
sub SetBcc {
- my $self=shift;
+ my $self = shift;
my $addresses = shift;
- return $self->SetHeader('Bcc', $addresses);
+ return $self->SetHeader( 'Bcc', $addresses );
}
# }}}
-# {{{ sub SetPrecedence
+# {{{ sub SetPrecedence
sub SetPrecedence {
- my $self = shift;
+ my $self = shift;
- unless ($self->TemplateObj->MIMEObj->head->get("Precedence")) {
- $self->SetHeader('Precedence', "bulk");
- }
+ unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ) {
+ $self->SetHeader( 'Precedence', "bulk" );
+ }
}
# }}}
@@ -399,70 +561,125 @@ 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 $self = shift;
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);
-
+ unless ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
+ my $message = $self->TransactionObj->Attachments;
+ my $ticket = $self->TicketObj->Id;
+
+ if ( $self->{'Subject'} ) {
+ $subject = $self->{'Subject'};
+ }
+ elsif ( ( $message->First() )
+ && ( $message->First->Headers ) ) {
+ my $header = $message->First->Headers();
+ $header =~ s/\n\s+/ /g;
+ if ( $header =~ /^Subject: (.*?)$/m ) {
+ $subject = $1;
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ $subject =~ s/(\r\n|\n|\s)/ /gi;
+
+ chomp $subject;
+ $self->SetHeader( 'Subject', $subject );
+
}
- return($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.
+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");
- }
+ 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__
+# {{{
+
+=head2 SetHeaderAsEncoding($field_name, $charset_encoding)
+
+This routine converts the field into specified charset encoding.
+
+=cut
+
+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
+
+ # See RT::I18N, 'NOTES: Why Encode::_utf8_off before Encode::from_to'
+
+ $value = $self->MIMEEncodeString($value, $enc);
+
+ $self->TemplateObj->MIMEObj->head->replace( $field, $value );
+
+
+}
+# }}}
+
+# {{{ MIMENcodeString
+
+=head2 MIMEEncodeString STRING ENCODING
+
+Takes a string and a possible encoding and returns the string wrapped in MIME goo.
+
+=cut
+
+sub MIMEEncodeString {
+ my $self = shift;
+ my $value = shift;
+ my $enc = shift;
-# {{{ POD
+ 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 );
+}
# }}}
+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});
+
1;
diff --git a/rt/lib/RT/Action/SetPriority.pm b/rt/lib/RT/Action/SetPriority.pm
new file mode 100644
index 000000000..515eeb58c
--- /dev/null
+++ b/rt/lib/RT/Action/SetPriority.pm
@@ -0,0 +1,61 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+package RT::Action::SetPriority;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@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 set a ticket's priority to the argument provided.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ $self->TicketObj->SetPriority($self->Argument);
+
+}
+
+eval "require RT::Action::SetPriority_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SetPriority_Vendor.pm});
+eval "require RT::Action::SetPriority_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SetPriority_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/UserDefined.pm b/rt/lib/RT/Action/UserDefined.pm
new file mode 100644
index 000000000..e2e3d72ce
--- /dev/null
+++ b/rt/lib/RT/Action/UserDefined.pm
@@ -0,0 +1,71 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+
+
+package RT::Action::UserDefined;
+use RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+=head2 Prepare
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub Prepare {
+ my $self = shift;
+ my $retval = eval $self->ScripObj->CustomPrepareCode;
+ if ($@) {
+ $RT::Logger->error("Scrip ".$self->ScripObj->Id. " Prepare failed: ".$@);
+ return (undef);
+ }
+ return ($retval);
+}
+
+=head2 Commit
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ my $retval = eval $self->ScripObj->CustomCommitCode;
+ if ($@) {
+ $RT::Logger->error("Scrip ".$self->ScripObj->Id. " Commit failed: ".$@);
+ return (undef);
+ }
+ return ($retval);
+}
+
+eval "require RT::Action::UserDefined_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/UserDefined_Vendor.pm});
+eval "require RT::Action::UserDefined_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/UserDefined_Local.pm});
+
+1;
+