summaryrefslogtreecommitdiff
path: root/rt/lib/RT
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT')
-rwxr-xr-xrt/lib/RT/ACE.pm5
-rwxr-xr-xrt/lib/RT/ACL.pm2
-rwxr-xr-xrt/lib/RT/Action.pm2
-rw-r--r--rt/lib/RT/Action/AutoOpen.pm2
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm4
-rw-r--r--rt/lib/RT/Action/CreateTickets.pm173
-rw-r--r--rt/lib/RT/Action/EscalatePriority.pm3
-rw-r--r--rt/lib/RT/Action/ExtractSubjectTag.pm41
-rwxr-xr-xrt/lib/RT/Action/LinearEscalate.pm2
-rwxr-xr-xrt/lib/RT/Action/Notify.pm2
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm2
-rw-r--r--rt/lib/RT/Action/NotifyGroup.pm2
-rw-r--r--rt/lib/RT/Action/NotifyGroupAsComment.pm2
-rw-r--r--rt/lib/RT/Action/RecordComment.pm3
-rw-r--r--rt/lib/RT/Action/RecordCorrespondence.pm5
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm2
-rw-r--r--rt/lib/RT/Action/SetPriority.pm3
-rw-r--r--rt/lib/RT/Action/SetStatus.pm2
-rw-r--r--rt/lib/RT/Action/UserDefined.pm3
-rw-r--r--rt/lib/RT/Approval.pm2
-rw-r--r--rt/lib/RT/Approval/Rule.pm2
-rw-r--r--rt/lib/RT/Approval/Rule/Created.pm2
-rw-r--r--rt/lib/RT/Approval/Rule/NewPending.pm2
-rw-r--r--rt/lib/RT/Approval/Rule/Passed.pm2
-rw-r--r--rt/lib/RT/Approval/Rule/Rejected.pm2
-rw-r--r--rt/lib/RT/Article.pm16
-rw-r--r--rt/lib/RT/Articles.pm2
-rwxr-xr-xrt/lib/RT/Attachment.pm8
-rwxr-xr-xrt/lib/RT/Attachments.pm2
-rw-r--r--rt/lib/RT/Attribute.pm2
-rw-r--r--rt/lib/RT/Attributes.pm2
-rw-r--r--rt/lib/RT/Base.pm2
-rw-r--r--rt/lib/RT/CachedGroupMember.pm2
-rw-r--r--rt/lib/RT/CachedGroupMembers.pm2
-rw-r--r--rt/lib/RT/Class.pm6
-rw-r--r--rt/lib/RT/Classes.pm2
-rwxr-xr-xrt/lib/RT/Condition.pm2
-rw-r--r--rt/lib/RT/Condition/AnyTransaction.pm3
-rw-r--r--rt/lib/RT/Condition/BeforeDue.pm3
-rw-r--r--rt/lib/RT/Condition/CloseTicket.pm2
-rw-r--r--rt/lib/RT/Condition/Overdue.pm3
-rw-r--r--rt/lib/RT/Condition/OwnerChange.pm3
-rw-r--r--rt/lib/RT/Condition/PriorityChange.pm3
-rw-r--r--rt/lib/RT/Condition/PriorityExceeds.pm3
-rw-r--r--rt/lib/RT/Condition/QueueChange.pm3
-rw-r--r--rt/lib/RT/Condition/ReopenTicket.pm2
-rw-r--r--rt/lib/RT/Condition/StatusChange.pm3
-rw-r--r--rt/lib/RT/Condition/UserDefined.pm3
-rw-r--r--rt/lib/RT/Config.pm30
-rw-r--r--rt/lib/RT/Crypt/GnuPG.pm22
-rwxr-xr-xrt/lib/RT/CurrentUser.pm11
-rw-r--r--rt/lib/RT/CustomField.pm17
-rw-r--r--rt/lib/RT/CustomFieldValue.pm2
-rw-r--r--rt/lib/RT/CustomFieldValues.pm2
-rw-r--r--rt/lib/RT/CustomFieldValues/External.pm4
-rw-r--r--rt/lib/RT/CustomFieldValues/Groups.pm2
-rw-r--r--rt/lib/RT/CustomFields.pm2
-rw-r--r--rt/lib/RT/Dashboard.pm4
-rw-r--r--rt/lib/RT/Dashboard/Mailer.pm45
-rw-r--r--rt/lib/RT/Dashboards.pm3
-rw-r--r--rt/lib/RT/Date.pm2
-rw-r--r--rt/lib/RT/EmailParser.pm112
-rw-r--r--rt/lib/RT/Generated.pm4
-rw-r--r--rt/lib/RT/Generated.pm.in2
-rw-r--r--rt/lib/RT/Graph/Tickets.pm2
-rwxr-xr-xrt/lib/RT/Group.pm9
-rwxr-xr-xrt/lib/RT/GroupMember.pm2
-rwxr-xr-xrt/lib/RT/GroupMembers.pm2
-rwxr-xr-xrt/lib/RT/Groups.pm6
-rw-r--r--rt/lib/RT/Handle.pm37
-rw-r--r--rt/lib/RT/I18N.pm58
-rw-r--r--rt/lib/RT/I18N/cs.pm2
-rw-r--r--rt/lib/RT/I18N/i_default.pm2
-rwxr-xr-xrt/lib/RT/I18N/ru.pm2
-rw-r--r--rt/lib/RT/Installer.pm9
-rw-r--r--rt/lib/RT/Interface/CLI.pm5
-rwxr-xr-xrt/lib/RT/Interface/Email.pm45
-rwxr-xr-xrt/lib/RT/Interface/Email/Auth/GnuPG.pm2
-rw-r--r--rt/lib/RT/Interface/Email/Auth/MailFrom.pm9
-rw-r--r--rt/lib/RT/Interface/REST.pm5
-rw-r--r--rt/lib/RT/Interface/Web.pm236
-rw-r--r--rt/lib/RT/Interface/Web/Handler.pm44
-rw-r--r--rt/lib/RT/Interface/Web/Menu.pm57
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder.pm2
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder/Tree.pm2
-rw-r--r--rt/lib/RT/Interface/Web/Request.pm23
-rw-r--r--rt/lib/RT/Interface/Web/Session.pm10
-rw-r--r--rt/lib/RT/Lifecycle.pm142
-rw-r--r--rt/lib/RT/Link.pm45
-rw-r--r--rt/lib/RT/Links.pm2
-rw-r--r--rt/lib/RT/ObjectClass.pm2
-rw-r--r--rt/lib/RT/ObjectClasses.pm2
-rw-r--r--rt/lib/RT/ObjectCustomField.pm2
-rw-r--r--rt/lib/RT/ObjectCustomFieldValue.pm2
-rw-r--r--rt/lib/RT/ObjectCustomFieldValues.pm6
-rw-r--r--rt/lib/RT/ObjectCustomFields.pm2
-rw-r--r--rt/lib/RT/ObjectTopic.pm15
-rw-r--r--rt/lib/RT/ObjectTopics.pm2
-rw-r--r--rt/lib/RT/Plugin.pm2
-rw-r--r--rt/lib/RT/Pod/HTML.pm104
-rw-r--r--rt/lib/RT/Pod/HTMLBatch.pm48
-rw-r--r--rt/lib/RT/Pod/Search.pm48
-rw-r--r--rt/lib/RT/Principal.pm2
-rw-r--r--rt/lib/RT/Principals.pm2
-rwxr-xr-xrt/lib/RT/Queue.pm40
-rwxr-xr-xrt/lib/RT/Queues.pm2
-rwxr-xr-xrt/lib/RT/Record.pm65
-rw-r--r--rt/lib/RT/Reminders.pm12
-rw-r--r--rt/lib/RT/Report/Tickets.pm39
-rw-r--r--rt/lib/RT/Report/Tickets/Entry.pm2
-rw-r--r--rt/lib/RT/Rule.pm2
-rw-r--r--rt/lib/RT/Ruleset.pm2
-rw-r--r--rt/lib/RT/SQL.pm4
-rw-r--r--rt/lib/RT/SavedSearch.pm26
-rw-r--r--rt/lib/RT/SavedSearches.pm3
-rwxr-xr-xrt/lib/RT/Scrip.pm10
-rwxr-xr-xrt/lib/RT/ScripAction.pm2
-rwxr-xr-xrt/lib/RT/ScripActions.pm2
-rwxr-xr-xrt/lib/RT/ScripCondition.pm6
-rwxr-xr-xrt/lib/RT/ScripConditions.pm2
-rwxr-xr-xrt/lib/RT/Scrips.pm2
-rwxr-xr-xrt/lib/RT/Search.pm2
-rw-r--r--rt/lib/RT/Search/ActiveTicketsInQueue.pm3
-rw-r--r--rt/lib/RT/Search/FromSQL.pm3
-rw-r--r--rt/lib/RT/Search/Googleish.pm4
-rw-r--r--rt/lib/RT/SearchBuilder.pm4
-rw-r--r--rt/lib/RT/SharedSetting.pm6
-rw-r--r--rt/lib/RT/SharedSettings.pm3
-rw-r--r--rt/lib/RT/Shredder.pm4
-rw-r--r--rt/lib/RT/Shredder/ACE.pm2
-rw-r--r--rt/lib/RT/Shredder/Attachment.pm2
-rw-r--r--rt/lib/RT/Shredder/CachedGroupMember.pm2
-rw-r--r--rt/lib/RT/Shredder/Constants.pm2
-rw-r--r--rt/lib/RT/Shredder/CustomField.pm2
-rw-r--r--rt/lib/RT/Shredder/CustomFieldValue.pm2
-rw-r--r--rt/lib/RT/Shredder/Dependencies.pm3
-rw-r--r--rt/lib/RT/Shredder/Dependency.pm3
-rw-r--r--rt/lib/RT/Shredder/Exceptions.pm2
-rw-r--r--rt/lib/RT/Shredder/Group.pm2
-rw-r--r--rt/lib/RT/Shredder/GroupMember.pm2
-rw-r--r--rt/lib/RT/Shredder/Link.pm2
-rw-r--r--rt/lib/RT/Shredder/ObjectCustomFieldValue.pm2
-rw-r--r--rt/lib/RT/Shredder/POD.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Attachments.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base/Dump.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base/Search.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Objects.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/SQLDump.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Summary.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Tickets.pm2
-rw-r--r--rt/lib/RT/Shredder/Plugin/Users.pm2
-rw-r--r--rt/lib/RT/Shredder/Principal.pm2
-rw-r--r--rt/lib/RT/Shredder/Queue.pm2
-rw-r--r--rt/lib/RT/Shredder/Record.pm2
-rw-r--r--rt/lib/RT/Shredder/Scrip.pm2
-rw-r--r--rt/lib/RT/Shredder/ScripAction.pm2
-rw-r--r--rt/lib/RT/Shredder/ScripCondition.pm2
-rw-r--r--rt/lib/RT/Shredder/Template.pm2
-rw-r--r--rt/lib/RT/Shredder/Ticket.pm2
-rw-r--r--rt/lib/RT/Shredder/Transaction.pm2
-rw-r--r--rt/lib/RT/Shredder/User.pm2
-rw-r--r--rt/lib/RT/Squish.pm2
-rw-r--r--rt/lib/RT/Squish/CSS.pm2
-rw-r--r--rt/lib/RT/Squish/JS.pm2
-rw-r--r--rt/lib/RT/System.pm2
-rwxr-xr-xrt/lib/RT/Template.pm6
-rwxr-xr-xrt/lib/RT/Templates.pm2
-rw-r--r--rt/lib/RT/Test.pm110
-rw-r--r--rt/lib/RT/Test/Apache.pm2
-rw-r--r--rt/lib/RT/Test/Email.pm2
-rw-r--r--rt/lib/RT/Test/GnuPG.pm7
-rw-r--r--rt/lib/RT/Test/Web.pm2
-rwxr-xr-xrt/lib/RT/Ticket.pm119
-rwxr-xr-xrt/lib/RT/Tickets.pm93
-rw-r--r--rt/lib/RT/Tickets_SQL.pm11
-rw-r--r--rt/lib/RT/Topic.pm2
-rw-r--r--rt/lib/RT/Topics.pm3
-rwxr-xr-xrt/lib/RT/Transaction.pm75
-rwxr-xr-xrt/lib/RT/Transactions.pm2
-rw-r--r--rt/lib/RT/URI.pm2
-rw-r--r--rt/lib/RT/URI/a.pm2
-rw-r--r--rt/lib/RT/URI/base.pm3
-rw-r--r--rt/lib/RT/URI/fsck_com_article.pm2
-rw-r--r--rt/lib/RT/URI/fsck_com_rt.pm2
-rw-r--r--rt/lib/RT/URI/t.pm2
-rwxr-xr-xrt/lib/RT/User.pm63
-rwxr-xr-xrt/lib/RT/Users.pm4
-rw-r--r--rt/lib/RT/Util.pm2
190 files changed, 1693 insertions, 767 deletions
diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm
index ae3eda4..c752aa2 100755
--- a/rt/lib/RT/ACE.pm
+++ b/rt/lib/RT/ACE.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -285,7 +285,8 @@ sub Create {
ObjectId => $args{'ObjectId'},
);
if ( $self->Id ) {
- return ( 0, $self->loc('That principal already has that right') );
+ return ( 0, $self->loc('[_1] already has that right',
+ $princ_obj->Object->Name) );
}
my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id,
diff --git a/rt/lib/RT/ACL.pm b/rt/lib/RT/ACL.pm
index 49a7f1d..d1e0df5 100755
--- a/rt/lib/RT/ACL.pm
+++ b/rt/lib/RT/ACL.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action.pm b/rt/lib/RT/Action.pm
index 23ff82d..dc10d0d 100755
--- a/rt/lib/RT/Action.pm
+++ b/rt/lib/RT/Action.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/AutoOpen.pm b/rt/lib/RT/Action/AutoOpen.pm
index 5f96e06..8566c62 100644
--- a/rt/lib/RT/Action/AutoOpen.pm
+++ b/rt/lib/RT/Action/AutoOpen.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
index cde874e..89b7536 100755
--- a/rt/lib/RT/Action/Autoreply.pm
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -87,7 +87,7 @@ sub SetRecipients {
=head2 SetReturnAddress
-Set this message\'s return address to the apropriate queue address
+Set this message's return address to the apropriate queue address
=cut
diff --git a/rt/lib/RT/Action/CreateTickets.pm b/rt/lib/RT/Action/CreateTickets.pm
index efd2bda..8030802 100644
--- a/rt/lib/RT/Action/CreateTickets.pm
+++ b/rt/lib/RT/Action/CreateTickets.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -56,14 +56,11 @@ use MIME::Entity;
=head1 NAME
- RT::Action::CreateTickets
-
-Create one or more tickets according to an externally supplied template.
-
+RT::Action::CreateTickets - Create one or more tickets according to an externally supplied template
=head1 SYNOPSIS
- ===Create-Ticket codereview
+ ===Create-Ticket: codereview
Subject: Code review for {$Tickets{'TOP'}->Subject}
Depended-On-By: TOP
Content: Someone has created a ticket. you should review and approve it,
@@ -72,18 +69,14 @@ Create one or more tickets according to an externally supplied template.
=head1 DESCRIPTION
+The CreateTickets ScripAction allows you to create automated workflows in RT,
+creating new tickets in response to actions and conditions from other
+tickets.
-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:
+=head2 Format
+CreateTickets uses the RT template configured in the scrip as a template
+for an ordered set of tickets to create. The basic format is as follows:
===Create-Ticket: identifier
Param: Value
@@ -98,19 +91,24 @@ to create. The basic format is as follows:
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, using the key 'create-identifier', where
-C<identifier> is the id you put after C<===Create-Ticket:>. The hash
+As shown, you can put one or more C<===Create-Ticket:> sections in
+a template. Each C<===Create-Ticket:> section is evaluated as its own
+L<Text::Template> object, which means that you can embed snippets
+of Perl inside the L<Text::Template> using C<{}> delimiters, but that
+such sections absolutely can not span a C<===Create-Ticket:> boundary.
+
+Note that each C<Value> must come right after the C<Param> on the same
+line. The C<Content:> param can extend over multiple lines, but the text
+of the first line must start right after C<Content:>. Don't try to start
+your C<Content:> section with a newline.
+
+After each ticket is created, it's stuffed into a hash called C<%Tickets>
+making it available during the creation of other tickets during the
+same ScripAction. The hash key for each ticket is C<create-[identifier]>,
+where C<[identifier]> is the value you put after C<===Create-Ticket:>. The hash
is prepopulated with the ticket which triggered the ScripAction as
-$Tickets{'TOP'}; you can also access that ticket using the shorthand
-TOP.
+C<$Tickets{'TOP'}>. You can also access that ticket using the shorthand
+C<TOP>.
A simple example:
@@ -121,22 +119,20 @@ A simple example:
so they can finish their work
ENDOFCONTENT
-
-
-A convoluted example
+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",
@@ -145,10 +141,10 @@ A convoluted example
IncludeSuperusers => 0,
IncludeSubgroupMembers => 0,
);
-
- my @admins;
+
+ our @admins;
while (my $admin = $adminccs->Next) {
- push (@admins, $admin->EmailAddress);
+ push (@admins, $admin->EmailAddress);
}
}
Queue: ___Approvals
@@ -170,50 +166,51 @@ A convoluted example
Refers-To: {$Tickets{"create-approval"}->Id}
Queue: ___Approvals
Content-Type: text/plain
- Content:
- Your approval is requred for this ticket, too.
+ Content: Your approval is requred for this ticket, too.
ENDOFCONTENT
-
-=head2 Acceptable fields
-A complete list of acceptable fields for this beastie:
+As shown above, you can include a block with Perl code to set up some
+values for the new tickets. If you want to access a variable in the
+template section after the block, you must scope it with C<our> rather
+than C<my>. Just as with other RT templates, you can also include
+Perl code in the template sections using C<{}>.
+=head2 Acceptable Fields
+
+A complete list of acceptable fields:
* Queue => Name or id# of a queue
Subject => A text string
- ! Status => A valid status. defaults to 'new'
+ ! 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
+ Starts =>
+ Started =>
+ Resolved =>
+ Owner => Username or id of an RT user who can and should own
this ticket; forces the owner if necessary
+ Requestor => Email address
- + Cc => Email address
- + AdminCc => Email address
+ + Cc => Email address
+ + AdminCc => Email address
+ RequestorGroup => Group name
+ CcGroup => Group name
+ AdminCcGroup => Group name
- TimeWorked =>
- TimeEstimated =>
- TimeLeft =>
- InitialPriority =>
- FinalPriority =>
- Type =>
- +! DependsOn =>
+ TimeWorked =>
+ TimeEstimated =>
+ TimeLeft =>
+ InitialPriority =>
+ FinalPriority =>
+ Type =>
+ +! DependsOn =>
+! DependedOnBy =>
+! RefersTo =>
- +! ReferredToBy =>
+ +! ReferredToBy =>
+! Members =>
- +! MemberOf =>
- Content => content. Can extend to multiple lines. Everything
+ +! 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
+ as content until we hit a line containing only
ENDOFCONTENT
ContentType => the content-type of the Content field. Defaults to
'text/plain'
@@ -225,31 +222,22 @@ A complete list of acceptable fields for this beastie:
CF-name => custom field value
CustomField-name => custom field value
-Fields marked with an * are required.
+Fields marked with an C<*> are required.
-Fields marked with a + may have multiple values, simply
+Fields marked with a C<+> may 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
+Fields marked with a C<!> have processing postponed until after all
+tickets in the same actions are created. Except for C<Status>, those
+fields can also take a ticket name within the same action (i.e.
+the identifiers after C<===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.
-
-
+When parsed, field names are converted to lowercase and have hyphens stripped.
+C<Refers-To>, C<RefersTo>, C<refersto>, C<refers-to> and C<r-e-f-er-s-tO> will
+all be treated as the same thing.
-
-=head1 AUTHOR
-
-Jesse Vincent <jesse@bestpractical.com>
-
-=head1 SEE ALSO
-
-perl(1).
+=head1 METHODS
=cut
@@ -537,12 +525,16 @@ sub UpdateByTemplate {
return @results;
}
-=head2 Parse TEMPLATE_CONTENT, DEFAULT_QUEUE, DEFAULT_REQEUESTOR ACTIVE
+=head2 Parse
+
+Takes (in order) template content, a default queue, a default requestor, and
+active (a boolean flag).
-Parse a template from TEMPLATE_CONTENT
+Parses a template in the template content, defaulting queue and requestor if
+unspecified in the template to the values provided as arguments.
-If $active is set to true, then we'll use Text::Template to parse the templates,
-allowing you to embed active perl in your templates.
+If the active flag is true, then we'll use L<Text::Template> to parse the
+templates, allowing you to embed active Perl in your templates.
=cut
@@ -576,9 +568,9 @@ sub Parse {
Parses mulitline templates. Things like:
- ===Create-Ticket ...
+ ===Create-Ticket: ...
-Takes the same arguments as Parse
+Takes the same arguments as L</Parse>.
=cut
@@ -834,9 +826,10 @@ sub ParseLines {
}
-=head2 _ParseXSVTemplate
+=head2 _ParseXSVTemplate
-Parses a tab or comma delimited template. Should only ever be called by Parse
+Parses a tab or comma delimited template. Should only ever be called by
+L</Parse>.
=cut
diff --git a/rt/lib/RT/Action/EscalatePriority.pm b/rt/lib/RT/Action/EscalatePriority.pm
index cb19b4f..1300b4f 100644
--- a/rt/lib/RT/Action/EscalatePriority.pm
+++ b/rt/lib/RT/Action/EscalatePriority.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -78,6 +78,7 @@ package RT::Action::EscalatePriority;
use base 'RT::Action';
use strict;
+use warnings;
#Do what we need to do and send it out.
diff --git a/rt/lib/RT/Action/ExtractSubjectTag.pm b/rt/lib/RT/Action/ExtractSubjectTag.pm
index a4d6458..6a3898e 100644
--- a/rt/lib/RT/Action/ExtractSubjectTag.pm
+++ b/rt/lib/RT/Action/ExtractSubjectTag.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -46,9 +46,48 @@
#
# END BPS TAGGED BLOCK }}}
+=head1 NAME
+
+ RT::Action::ExtractSubjectTag
+
+=head1 DESCRIPTION
+
+ExtractSubjectTag is a ScripAction which allows ticket bonding between
+two RT instances or between RT and other Ticket systems like Siebel
+or Remedy.
+
+By default this ScripAction is set up to run on every transaction on
+every Correspondence.
+
+One can configure this ScripActions behaviour by changing the
+global C<$ExtractSubjectTagMatch> in C<RT_Config.pm>.
+
+If a transaction's subject matches this regexp, we append the match
+tag to the ticket's current subject. This helps ensure that
+further communication on the ticket will include the remote
+system's subject tag.
+
+If you modify this code, be careful not to remove the code where it
+ensures that it only examines remote systems' tags.
+
+=head1 EXAMPLE
+
+As an example, Siebel will set their subject tag to something
+like:
+
+ B<[SR ID:1-554]>
+
+To record this tag in the local ticket's subject, we need to change
+ExtractSubjectTagMatch to something like:
+
+ Set($ExtractSubjectTagMatch, qr/\[[^\]]+[#:][0-9-]+\]/);
+
+=cut
+
package RT::Action::ExtractSubjectTag;
use base 'RT::Action';
use strict;
+use warnings;
sub Describe {
my $self = shift;
diff --git a/rt/lib/RT/Action/LinearEscalate.pm b/rt/lib/RT/Action/LinearEscalate.pm
index 0a0825e..13913e6 100755
--- a/rt/lib/RT/Action/LinearEscalate.pm
+++ b/rt/lib/RT/Action/LinearEscalate.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
index f1aef40..3553cbc 100755
--- a/rt/lib/RT/Action/Notify.pm
+++ b/rt/lib/RT/Action/Notify.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
index b62f555..0016a36 100755
--- a/rt/lib/RT/Action/NotifyAsComment.pm
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/NotifyGroup.pm b/rt/lib/RT/Action/NotifyGroup.pm
index 5e7076f..1dece60 100644
--- a/rt/lib/RT/Action/NotifyGroup.pm
+++ b/rt/lib/RT/Action/NotifyGroup.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/NotifyGroupAsComment.pm b/rt/lib/RT/Action/NotifyGroupAsComment.pm
index 1511890..cf6952a 100644
--- a/rt/lib/RT/Action/NotifyGroupAsComment.pm
+++ b/rt/lib/RT/Action/NotifyGroupAsComment.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/RecordComment.pm b/rt/lib/RT/Action/RecordComment.pm
index 62832a5..a384af3 100644
--- a/rt/lib/RT/Action/RecordComment.pm
+++ b/rt/lib/RT/Action/RecordComment.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Action::RecordComment;
use base 'RT::Action';
use strict;
+use warnings;
=head1 NAME
diff --git a/rt/lib/RT/Action/RecordCorrespondence.pm b/rt/lib/RT/Action/RecordCorrespondence.pm
index 2faa560..cc21503 100644
--- a/rt/lib/RT/Action/RecordCorrespondence.pm
+++ b/rt/lib/RT/Action/RecordCorrespondence.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,12 +49,13 @@
package RT::Action::RecordCorrespondence;
use base 'RT::Action';
use strict;
+use warnings;
=head1 NAME
RT::Action::RecordCorrespondence - An Action which can be used from an
external tool, or in any situation where a ticket transaction has not
-been started, to make a comment on the ticket.
+been started, to create a correspondence on the ticket.
=head1 SYNOPSIS
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
index 1e6607e..0a52904 100755
--- a/rt/lib/RT/Action/SendEmail.pm
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/SetPriority.pm b/rt/lib/RT/Action/SetPriority.pm
index 783d57d..2043532 100644
--- a/rt/lib/RT/Action/SetPriority.pm
+++ b/rt/lib/RT/Action/SetPriority.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -50,6 +50,7 @@ package RT::Action::SetPriority;
use base 'RT::Action';
use strict;
+use warnings;
#Do what we need to do and send it out.
diff --git a/rt/lib/RT/Action/SetStatus.pm b/rt/lib/RT/Action/SetStatus.pm
index f52d401..be00396 100644
--- a/rt/lib/RT/Action/SetStatus.pm
+++ b/rt/lib/RT/Action/SetStatus.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Action/UserDefined.pm b/rt/lib/RT/Action/UserDefined.pm
index 1bad2be..b259323 100644
--- a/rt/lib/RT/Action/UserDefined.pm
+++ b/rt/lib/RT/Action/UserDefined.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -50,6 +50,7 @@ package RT::Action::UserDefined;
use base 'RT::Action';
use strict;
+use warnings;
=head2 Prepare
diff --git a/rt/lib/RT/Approval.pm b/rt/lib/RT/Approval.pm
index 6a519c1..dc60222 100644
--- a/rt/lib/RT/Approval.pm
+++ b/rt/lib/RT/Approval.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Approval/Rule.pm b/rt/lib/RT/Approval/Rule.pm
index 85526c1..6892f41 100644
--- a/rt/lib/RT/Approval/Rule.pm
+++ b/rt/lib/RT/Approval/Rule.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Approval/Rule/Created.pm b/rt/lib/RT/Approval/Rule/Created.pm
index cd4519c..8fcaeb2 100644
--- a/rt/lib/RT/Approval/Rule/Created.pm
+++ b/rt/lib/RT/Approval/Rule/Created.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Approval/Rule/NewPending.pm b/rt/lib/RT/Approval/Rule/NewPending.pm
index d2ba3ac..97d3cfb 100644
--- a/rt/lib/RT/Approval/Rule/NewPending.pm
+++ b/rt/lib/RT/Approval/Rule/NewPending.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Approval/Rule/Passed.pm b/rt/lib/RT/Approval/Rule/Passed.pm
index 000a8dc..acc4916 100644
--- a/rt/lib/RT/Approval/Rule/Passed.pm
+++ b/rt/lib/RT/Approval/Rule/Passed.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Approval/Rule/Rejected.pm b/rt/lib/RT/Approval/Rule/Rejected.pm
index b22df5c..0a02568 100644
--- a/rt/lib/RT/Approval/Rule/Rejected.pm
+++ b/rt/lib/RT/Approval/Rule/Rejected.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Article.pm b/rt/lib/RT/Article.pm
index 678aa11..ec1ae3c 100644
--- a/rt/lib/RT/Article.pm
+++ b/rt/lib/RT/Article.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -399,9 +399,8 @@ sub AddLink {
# Check that we're actually getting a valid URI
my $uri_obj = RT::URI->new( $self->CurrentUser );
- $uri_obj->FromURI( $args{'Target'}||$args{'Base'} );
- unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
- my $msg = $self->loc( "Couldn't resolve '[_1]' into a Link.", $args{'Target'} );
+ unless ( $uri_obj->FromURI( $args{'Target'}||$args{'Base'} )) {
+ my $msg = $self->loc( "Couldn't resolve '[_1]' into a Link.", $args{'Target'} || $args{'Base'} );
$RT::Logger->warning( $msg );
return( 0, $msg );
}
@@ -611,15 +610,6 @@ sub CustomFieldLookupType {
"RT::Class-RT::Article";
}
-# _LookupId is the id of the toplevel type object the customfield is joined to
-# in this case, that's an RT::Class.
-
-sub _LookupId {
- my $self = shift;
- return $self->ClassObj->id;
-
-}
-
=head2 LoadByInclude Field Value
Takes the name of a form field from "Include Article"
diff --git a/rt/lib/RT/Articles.pm b/rt/lib/RT/Articles.pm
index 47d0ebe..d69eabf 100644
--- a/rt/lib/RT/Articles.pm
+++ b/rt/lib/RT/Articles.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Attachment.pm b/rt/lib/RT/Attachment.pm
index f1d9a63..54217b3 100755
--- a/rt/lib/RT/Attachment.pm
+++ b/rt/lib/RT/Attachment.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -266,7 +266,7 @@ sub ParentObj {
=head2 Children
Returns an L<RT::Attachments> object which is preloaded with
-all attachments objects with this attachment\'s Id as their
+all attachments objects with this attachment's Id as their
C<Parent>.
=cut
@@ -499,12 +499,14 @@ L<Email::Address> objects.
=cut
+our @ADDRESS_HEADERS = qw(From To Cc Bcc RT-Send-Cc RT-Send-Bcc);
+
sub Addresses {
my $self = shift;
my %data = ();
my $current_user_address = lc $self->CurrentUser->EmailAddress;
- foreach my $hdr (qw(From To Cc Bcc RT-Send-Cc RT-Send-Bcc)) {
+ foreach my $hdr (@ADDRESS_HEADERS) {
my @Addresses;
my $line = $self->GetHeader($hdr);
diff --git a/rt/lib/RT/Attachments.pm b/rt/lib/RT/Attachments.pm
index 2bdbc24..5b087a4 100755
--- a/rt/lib/RT/Attachments.pm
+++ b/rt/lib/RT/Attachments.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Attribute.pm b/rt/lib/RT/Attribute.pm
index cd0b54e..10971a2 100644
--- a/rt/lib/RT/Attribute.pm
+++ b/rt/lib/RT/Attribute.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Attributes.pm b/rt/lib/RT/Attributes.pm
index fcbd0b1..9c18c1a 100644
--- a/rt/lib/RT/Attributes.pm
+++ b/rt/lib/RT/Attributes.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Base.pm b/rt/lib/RT/Base.pm
index 2aae60e..403c318 100644
--- a/rt/lib/RT/Base.pm
+++ b/rt/lib/RT/Base.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/CachedGroupMember.pm b/rt/lib/RT/CachedGroupMember.pm
index 4c05852..b334d4d 100644
--- a/rt/lib/RT/CachedGroupMember.pm
+++ b/rt/lib/RT/CachedGroupMember.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/CachedGroupMembers.pm b/rt/lib/RT/CachedGroupMembers.pm
index f76fc5d..4d8f356 100644
--- a/rt/lib/RT/CachedGroupMembers.pm
+++ b/rt/lib/RT/CachedGroupMembers.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Class.pm b/rt/lib/RT/Class.pm
index 3906b9f..dfe8eb3 100644
--- a/rt/lib/RT/Class.pm
+++ b/rt/lib/RT/Class.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -94,7 +94,7 @@ $RIGHTS = {
AdminClass => 'Modify metadata and custom fields for this class', #loc_pair
AdminTopics => 'Modify topic hierarchy associated with this class', #loc_pair
ShowACL => 'Display Access Control List', #loc_pair
- ModifyACL => 'Modify Access Control List', #loc_pair
+ ModifyACL => 'Create, modify and delete Access Control List entries', #loc_pair
DeleteArticle => 'Delete articles in this class', #loc_pair
};
@@ -218,7 +218,7 @@ sub ValidateName {
return undef unless ($newval);
my $obj = RT::Class->new($RT::SystemUser);
$obj->Load($newval);
- return undef if ( $obj->Id );
+ return undef if $obj->id && ( !$self->id || $self->id != $obj->id );
return $self->SUPER::ValidateName($newval);
}
diff --git a/rt/lib/RT/Classes.pm b/rt/lib/RT/Classes.pm
index 37dc411..60122c7 100644
--- a/rt/lib/RT/Classes.pm
+++ b/rt/lib/RT/Classes.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Condition.pm b/rt/lib/RT/Condition.pm
index f50d64d..0751815 100755
--- a/rt/lib/RT/Condition.pm
+++ b/rt/lib/RT/Condition.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Condition/AnyTransaction.pm b/rt/lib/RT/Condition/AnyTransaction.pm
index 73eea2b..2c9129c 100644
--- a/rt/lib/RT/Condition/AnyTransaction.pm
+++ b/rt/lib/RT/Condition/AnyTransaction.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -50,6 +50,7 @@ package RT::Condition::AnyTransaction;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/BeforeDue.pm b/rt/lib/RT/Condition/BeforeDue.pm
index 11c40e6..8df73ca 100644
--- a/rt/lib/RT/Condition/BeforeDue.pm
+++ b/rt/lib/RT/Condition/BeforeDue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -52,6 +52,7 @@ use base 'RT::Condition';
use RT::Date;
use strict;
+use warnings;
sub IsApplicable {
my $self = shift;
diff --git a/rt/lib/RT/Condition/CloseTicket.pm b/rt/lib/RT/Condition/CloseTicket.pm
index 60d5bbe..bdeaf2d 100644
--- a/rt/lib/RT/Condition/CloseTicket.pm
+++ b/rt/lib/RT/Condition/CloseTicket.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Condition/Overdue.pm b/rt/lib/RT/Condition/Overdue.pm
index 3bf79a1..547aea2 100644
--- a/rt/lib/RT/Condition/Overdue.pm
+++ b/rt/lib/RT/Condition/Overdue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -59,6 +59,7 @@ Returns true if the ticket we're operating on is overdue
package RT::Condition::Overdue;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/OwnerChange.pm b/rt/lib/RT/Condition/OwnerChange.pm
index 4643791..8500548 100644
--- a/rt/lib/RT/Condition/OwnerChange.pm
+++ b/rt/lib/RT/Condition/OwnerChange.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::OwnerChange;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/PriorityChange.pm b/rt/lib/RT/Condition/PriorityChange.pm
index aa00e60..a600453 100644
--- a/rt/lib/RT/Condition/PriorityChange.pm
+++ b/rt/lib/RT/Condition/PriorityChange.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::PriorityChange;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/PriorityExceeds.pm b/rt/lib/RT/Condition/PriorityExceeds.pm
index 17943cf..a28d6df 100644
--- a/rt/lib/RT/Condition/PriorityExceeds.pm
+++ b/rt/lib/RT/Condition/PriorityExceeds.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::PriorityExceeds;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/QueueChange.pm b/rt/lib/RT/Condition/QueueChange.pm
index 69eea18..ba7a8a4 100644
--- a/rt/lib/RT/Condition/QueueChange.pm
+++ b/rt/lib/RT/Condition/QueueChange.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::QueueChange;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/ReopenTicket.pm b/rt/lib/RT/Condition/ReopenTicket.pm
index 7072221..a057e40 100644
--- a/rt/lib/RT/Condition/ReopenTicket.pm
+++ b/rt/lib/RT/Condition/ReopenTicket.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Condition/StatusChange.pm b/rt/lib/RT/Condition/StatusChange.pm
index 10d882e..e84915d 100644
--- a/rt/lib/RT/Condition/StatusChange.pm
+++ b/rt/lib/RT/Condition/StatusChange.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::StatusChange;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 DESCRIPTION
diff --git a/rt/lib/RT/Condition/UserDefined.pm b/rt/lib/RT/Condition/UserDefined.pm
index 8ed5e5c..1abee67 100644
--- a/rt/lib/RT/Condition/UserDefined.pm
+++ b/rt/lib/RT/Condition/UserDefined.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Condition::UserDefined;
use base 'RT::Condition';
use strict;
+use warnings;
=head2 IsApplicable
diff --git a/rt/lib/RT/Config.pm b/rt/lib/RT/Config.pm
index 014c764..0c04b91 100644
--- a/rt/lib/RT/Config.pm
+++ b/rt/lib/RT/Config.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -303,15 +303,6 @@ our %META = (
},
# User overridable options for RT at a glance
- DefaultSummaryRows => {
- Section => 'RT at a glance', #loc
- Overridable => 1,
- SortOrder => 1,
- Widget => '/Widgets/Form/Integer',
- WidgetArguments => {
- Description => 'Number of search results', #loc
- },
- },
HomePageRefreshInterval => {
Section => 'RT at a glance', #loc
Overridable => 1,
@@ -446,10 +437,13 @@ our %META = (
Description => 'Date format', #loc
Callback => sub { my $ret = { Values => [], ValuesLabel => {}};
my $date = RT::Date->new($HTML::Mason::Commands::session{'CurrentUser'});
- $date->Set;
+ $date->SetToNow;
foreach my $value ($date->Formatters) {
push @{$ret->{Values}}, $value;
- $ret->{ValuesLabel}{$value} = $date->$value();
+ $ret->{ValuesLabel}{$value} = $date->Get(
+ Format => $value,
+ Timezone => 'user',
+ );
}
return $ret;
},
@@ -1215,7 +1209,7 @@ sub SetFromConfig {
# if the entry has a trailing '::' then
# it is a link to another name space
if ( substr( $k, -2 ) eq '::') {
- $name = $self->__GetNameByRef( $ref, $k );
+ $name = $self->__GetNameByRef( $ref, $pack eq 'main::'? $k : $pack.$k );
return $name if $name;
}
@@ -1230,13 +1224,19 @@ sub SetFromConfig {
# Otherwie 5.10 goes boom. maybe we should skip any
# reference
next if ref($entry) eq 'SCALAR' || ref($entry) eq 'REF';
- my $entry_ref = *{$entry}{ ref($ref) };
+
+ my $ref_type = ref($ref);
+
+ # regex/arrayref/hashref/coderef are stored in SCALAR glob
+ $ref_type = 'SCALAR' if $ref_type eq 'REF';
+
+ my $entry_ref = *{$entry}{ $ref_type };
next unless $entry_ref;
# if references are equal then we've found
if ( $entry_ref == $ref ) {
$last_pack = $pack;
- return ( $REF_SYMBOLS{ ref($ref) } || '*' ) . $pack . $k;
+ return ( $REF_SYMBOLS{ $ref_type } || '*' ) . $pack . $k;
}
}
return '';
diff --git a/rt/lib/RT/Crypt/GnuPG.pm b/rt/lib/RT/Crypt/GnuPG.pm
index 2330478..6164a42 100644
--- a/rt/lib/RT/Crypt/GnuPG.pm
+++ b/rt/lib/RT/Crypt/GnuPG.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -905,11 +905,25 @@ sub FindProtectedParts {
# sense) unnecessarily applies a base64 transfer encoding to PGP
# mail (whose content is already base64-encoded).
if ( $entity->bodyhandle->is_encoded and $entity->head->mime_encoding ) {
- pipe( my ($read_decoded, $write_decoded) );
my $decoder = MIME::Decoder->new( $entity->head->mime_encoding );
if ($decoder) {
- eval { $decoder->decode($io, $write_decoded) };
- $io = $read_decoded;
+ local $@;
+ eval {
+ my $buf = '';
+ open my $fh, '>', \$buf
+ or die "Couldn't open scalar for writing: $!";
+ binmode $fh, ":raw";
+ $decoder->decode($io, $fh);
+ close $fh or die "Couldn't close scalar: $!";
+
+ open $fh, '<', \$buf
+ or die "Couldn't re-open scalar for reading: $!";
+ binmode $fh, ":raw";
+ $io = $fh;
+ 1;
+ } or do {
+ $RT::Logger->error("Couldn't decode body: $@");
+ }
}
}
diff --git a/rt/lib/RT/CurrentUser.pm b/rt/lib/RT/CurrentUser.pm
index 7d24779..fa0d4ca 100755
--- a/rt/lib/RT/CurrentUser.pm
+++ b/rt/lib/RT/CurrentUser.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -240,9 +240,12 @@ sub loc {
my $handle = $self->LanguageHandle;
if (@_ == 1) {
- # pre-scan the lexicon hashes to return _AUTO keys verbatim,
- # to keep locstrings containing '[' and '~' from tripping over Maketext
- return $_[0] unless grep exists $_->{$_[0]}, @{ $handle->_lex_refs };
+ # If we have no [_1] replacements, and the key does not appear
+ # in the lexicon, unescape (using ~) and return it verbatim, as
+ # an optimization.
+ my $unescaped = $_[0];
+ $unescaped =~ s!~(.)!$1!g;
+ return $unescaped unless grep exists $_->{$_[0]}, @{ $handle->_lex_refs };
}
return $handle->maketext(@_);
diff --git a/rt/lib/RT/CustomField.pm b/rt/lib/RT/CustomField.pm
index 8d16c1f..01b4970 100644
--- a/rt/lib/RT/CustomField.pm
+++ b/rt/lib/RT/CustomField.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -710,7 +710,7 @@ sub ValidateValuesClass {
my $self = shift;
my $class = shift;
- return 1 if !defined $class || $class eq 'RT::CustomFieldValues';
+ return 1 if !$class || $class eq 'RT::CustomFieldValues';
return 1 if grep $class eq $_, RT->Config->Get('CustomFieldValuesSources');
return undef;
}
@@ -1556,9 +1556,8 @@ sub _CanonicalizeValueDate {
my $DateObj = RT::Date->new( $self->CurrentUser );
$DateObj->Set( Format => 'unknown',
Value => $args->{'Content'},
- Timezone => 'UTC',
);
- $args->{'Content'} = $DateObj->Date( Timezone => 'UTC' );
+ $args->{'Content'} = $DateObj->Date( Timezone => 'user' );
}
=head2 MatchPattern STRING
@@ -1666,14 +1665,13 @@ sub ValuesForObject {
my $object = shift;
my $values = RT::ObjectCustomFieldValues->new($self->CurrentUser);
- unless ($self->CurrentUserHasRight('SeeCustomField')) {
+ unless ($self->id and $self->CurrentUserHasRight('SeeCustomField')) {
# Return an empty object if they have no rights to see
+ $values->Limit( FIELD => "id", VALUE => 0, SUBCLAUSE => "ACL" );
return ($values);
}
-
-
+
$values->LimitToCustomField($self->Id);
- $values->LimitToEnabled();
$values->LimitToObject($object);
return ($values);
@@ -1690,6 +1688,7 @@ Examples:
'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc
'RT::User' => "Users", # loc
'RT::Group' => "Groups", # loc
+ 'RT::Queue' => "Queues", # loc
This is a class method.
@@ -2103,6 +2102,8 @@ sub _CoreAccessible {
{read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
Repeated =>
{read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ ValuesClass =>
+ {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
BasedOn =>
{read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
Description =>
diff --git a/rt/lib/RT/CustomFieldValue.pm b/rt/lib/RT/CustomFieldValue.pm
index 26df55a..6dffc34 100644
--- a/rt/lib/RT/CustomFieldValue.pm
+++ b/rt/lib/RT/CustomFieldValue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/CustomFieldValues.pm b/rt/lib/RT/CustomFieldValues.pm
index 90a1637..e3380b7 100644
--- a/rt/lib/RT/CustomFieldValues.pm
+++ b/rt/lib/RT/CustomFieldValues.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/CustomFieldValues/External.pm b/rt/lib/RT/CustomFieldValues/External.pm
index 6112514..e6bf2f8 100644
--- a/rt/lib/RT/CustomFieldValues/External.pm
+++ b/rt/lib/RT/CustomFieldValues/External.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -82,7 +82,7 @@ C<sortorder>.
=head1 SEE ALSO
-L<docs/extending/external_custom_fields.pod>
+F<docs/extending/external_custom_fields.pod>
=cut
diff --git a/rt/lib/RT/CustomFieldValues/Groups.pm b/rt/lib/RT/CustomFieldValues/Groups.pm
index 2519e29..feeeadb 100644
--- a/rt/lib/RT/CustomFieldValues/Groups.pm
+++ b/rt/lib/RT/CustomFieldValues/Groups.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/CustomFields.pm b/rt/lib/RT/CustomFields.pm
index d4a5bc7..017018e 100644
--- a/rt/lib/RT/CustomFields.pm
+++ b/rt/lib/RT/CustomFields.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Dashboard.pm b/rt/lib/RT/Dashboard.pm
index 2e2bbc4..349864e 100644
--- a/rt/lib/RT/Dashboard.pm
+++ b/rt/lib/RT/Dashboard.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -109,7 +109,7 @@ An object of this class is called "dashboard"
=cut
-sub ObjectName { "dashboard" }
+sub ObjectName { "dashboard" } # loc
sub SaveAttribute {
my $self = shift;
diff --git a/rt/lib/RT/Dashboard/Mailer.pm b/rt/lib/RT/Dashboard/Mailer.pm
index 40b53b1..9d28c49 100644
--- a/rt/lib/RT/Dashboard/Mailer.pm
+++ b/rt/lib/RT/Dashboard/Mailer.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -59,6 +59,7 @@ use RT::Dashboard;
use RT::Interface::Web::Handler;
use RT::Interface::Web;
use File::Temp 'tempdir';
+use HTML::Scrubber;
sub MailDashboards {
my $self = shift;
@@ -248,6 +249,8 @@ SUMMARY
}
}
+ $content = ScrubContent($content);
+
$RT::Logger->debug("Got ".length($content)." characters of output.");
$content = HTML::RewriteAttributes::Links->rewrite(
@@ -393,7 +396,7 @@ sub BuildEmail {
Type => $mimetype,
Encoding => $encoding,
Disposition => 'inline',
- Name => $filename,
+ Name => RT::Interface::Email::EncodeToMIME( String => $filename ),
'Content-Id' => $cid_of{$uri},
);
@@ -408,9 +411,9 @@ sub BuildEmail {
);
my $entity = MIME::Entity->build(
- From => $args{From},
- To => $args{To},
- Subject => $args{Subject},
+ From => Encode::encode_utf8($args{From}),
+ To => Encode::encode_utf8($args{To}),
+ Subject => RT::Interface::Email::EncodeToMIME( String => $args{Subject} ),
Type => "multipart/mixed",
);
@@ -463,6 +466,33 @@ sub BuildEmail {
}
{
+ my $scrubber;
+
+ sub _scrubber {
+ unless ($scrubber) {
+ $scrubber = HTML::Scrubber->new;
+ # Allow everything by default, except JS attributes ...
+ $scrubber->default(
+ 1 => {
+ '*' => 1,
+ map { ("on$_" => 0) }
+ qw(blur change click dblclick error focus keydown keypress keyup load
+ mousedown mousemove mouseout mouseover mouseup reset select submit unload)
+ }
+ );
+ # ... and <script>s
+ $scrubber->deny('script');
+ }
+ return $scrubber;
+ }
+
+ sub ScrubContent {
+ my $content = shift;
+ return _scrubber->scrub($content);
+ }
+}
+
+{
my %cache;
sub HourDowDomIn {
@@ -560,8 +590,9 @@ sub GetResource {
{
package RT::Dashboard::FakeRequest;
sub new { bless {}, shift }
- sub header_out { shift }
- sub headers_out { shift }
+ sub header_out { return undef }
+ sub headers_out { wantarray ? () : {} }
+ sub err_headers_out { wantarray ? () : {} }
sub content_type {
my $self = shift;
$self->{content_type} = shift if @_;
diff --git a/rt/lib/RT/Dashboards.pm b/rt/lib/RT/Dashboards.pm
index 5d10205..f9cbbe8 100644
--- a/rt/lib/RT/Dashboards.pm
+++ b/rt/lib/RT/Dashboards.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -70,6 +70,7 @@ package RT::Dashboards;
use RT::Dashboard;
use strict;
+use warnings;
use base 'RT::SharedSettings';
sub RecordClass {
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm
index 442c770..031f9c8 100644
--- a/rt/lib/RT/Date.pm
+++ b/rt/lib/RT/Date.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/EmailParser.pm b/rt/lib/RT/EmailParser.pm
index dd73d90..19dc2c9 100644
--- a/rt/lib/RT/EmailParser.pm
+++ b/rt/lib/RT/EmailParser.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -131,8 +131,6 @@ sub SmartParseMIMEEntityFromScalar {
}
};
- $self->RescueOutlook;
-
#If for some reason we weren't able to parse the message using a temp file
# try it with a scalar
if ( $@ || !$self->Entity ) {
@@ -286,7 +284,7 @@ sub _PostProcessNewEntity {
Takes a hashref object containing QueueObj, Head and CurrentUser objects.
Returns a list of all email addresses in the To and Cc
-headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+headers b<except> the current Queue's email addresses, the CurrentUser's
email address and anything that the RT->Config->Get('RTAddressRegexp') matches.
=cut
@@ -568,50 +566,90 @@ return 1 if it does find the problem in the entity and get it fixed.
sub RescueOutlook {
my $self = shift;
my $mime = $self->Entity();
- return unless $mime;
-
- my $mailer = $mime->head->get('X-Mailer');
- # 12.0 is outlook 2007, 14.0 is 2010
- if ( $mailer && $mailer =~ /Microsoft(?:.*?)Outlook 1[2-4]\./ ) {
- my $text_part;
- if ( $mime->head->get('Content-Type') =~ m{multipart/mixed} ) {
- my $first = $mime->parts(0);
- if ( $first && $first->head->get('Content-Type') =~ m{multipart/alternative} )
+ return unless $mime && $self->LooksLikeMSEmail($mime);
+
+ my $text_part;
+ if ( $mime->head->get('Content-Type') =~ m{multipart/mixed} ) {
+ my $first = $mime->parts(0);
+ if ( $first && $first->head->get('Content-Type') =~ m{multipart/alternative} )
+ {
+ my $inner_first = $first->parts(0);
+ if ( $inner_first && $inner_first->head->get('Content-Type') =~ m{text/plain} )
{
- my $inner_first = $first->parts(0);
- if ( $inner_first && $inner_first->head->get('Content-Type') =~ m{text/plain} )
- {
- $text_part = $inner_first;
- }
+ $text_part = $inner_first;
}
}
- elsif ( $mime->head->get('Content-Type') =~ m{multipart/alternative} ) {
- my $first = $mime->parts(0);
- if ( $first && $first->head->get('Content-Type') =~ m{text/plain} ) {
- $text_part = $first;
- }
+ }
+ elsif ( $mime->head->get('Content-Type') =~ m{multipart/alternative} ) {
+ my $first = $mime->parts(0);
+ if ( $first && $first->head->get('Content-Type') =~ m{text/plain} ) {
+ $text_part = $first;
}
+ }
- if ($text_part) {
-
- # use the unencoded string
- my $content = $text_part->bodyhandle->as_string;
- if ( $content =~ s/\n\n/\n/g ) {
- # only write only if we did change the content
- if ( my $io = $text_part->open("w") ) {
- $io->print($content);
- $io->close;
- return 1;
- }
- else {
- $RT::Logger->error("can't write to body");
- }
+ # Add base64 since we've seen examples of double newlines with
+ # this type too. Need an example of a multi-part base64 to
+ # handle that permutation if it exists.
+ elsif ( $mime->head->get('Content-Transfer-Encoding') =~ m{base64} ) {
+ $text_part = $mime; # Assuming single part, already decoded.
+ }
+
+ if ($text_part) {
+
+ # use the unencoded string
+ my $content = $text_part->bodyhandle->as_string;
+ if ( $content =~ s/\n\n/\n/g ) {
+
+ # Outlook puts a space on extra newlines, remove it
+ $content =~ s/\ +$//mg;
+
+ # only write only if we did change the content
+ if ( my $io = $text_part->open("w") ) {
+ $io->print($content);
+ $io->close;
+ $RT::Logger->debug(
+ "Removed extra newlines from MS Outlook message.");
+ return 1;
+ }
+ else {
+ $RT::Logger->error("Can't write to body to fix newlines");
}
}
}
+
return;
}
+=head1 LooksLikeMSEmail
+
+Try to determine if the current email may have
+come from MS Outlook or gone through Exchange, and therefore
+may have extra newlines added.
+
+=cut
+
+sub LooksLikeMSEmail {
+ my $self = shift;
+ my $mime = shift;
+
+ my $mailer = $mime->head->get('X-Mailer');
+
+ # 12.0 is outlook 2007, 14.0 is 2010
+ return 1 if ( $mailer && $mailer =~ /Microsoft(?:.*?)Outlook 1[2-4]\./ );
+
+ if ( RT->Config->Get('CheckMoreMSMailHeaders') ) {
+
+ # Check for additional headers that might
+ # indicate this came from Outlook or through Exchange.
+ # A sample we received had the headers X-MS-Has-Attach: and
+ # X-MS-Tnef-Correlator: and both had no value.
+
+ my @tags = $mime->head->tags();
+ return 1 if grep { /^X-MS-/ } @tags;
+ }
+
+ return 0; # Doesn't look like MS email.
+}
sub DESTROY {
my $self = shift;
diff --git a/rt/lib/RT/Generated.pm b/rt/lib/RT/Generated.pm
index 907ea77..4f74ea9 100644
--- a/rt/lib/RT/Generated.pm
+++ b/rt/lib/RT/Generated.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -50,7 +50,7 @@ package RT;
use warnings;
use strict;
-our $VERSION = '4.0.8';
+our $VERSION = '4.0.13';
diff --git a/rt/lib/RT/Generated.pm.in b/rt/lib/RT/Generated.pm.in
index ac15bde..91aa840 100644
--- a/rt/lib/RT/Generated.pm.in
+++ b/rt/lib/RT/Generated.pm.in
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Graph/Tickets.pm b/rt/lib/RT/Graph/Tickets.pm
index b839824..753ff20 100644
--- a/rt/lib/RT/Graph/Tickets.pm
+++ b/rt/lib/RT/Graph/Tickets.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Group.pm b/rt/lib/RT/Group.pm
index b367b2f..d4d2802 100755
--- a/rt/lib/RT/Group.pm
+++ b/rt/lib/RT/Group.pm
@@ -3,7 +3,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -51,7 +51,7 @@
=head1 NAME
- RT::Group - RT\'s group object
+ RT::Group - RT's group object
=head1 SYNOPSIS
@@ -529,8 +529,9 @@ sub _ValidateUserDefinedName {
my $dupcheck = RT::Group->new(RT->SystemUser);
$dupcheck->LoadUserDefinedGroup($value);
- return (0, $self->loc("Group name '[_1]' is already in use", $value))
- if $dupcheck->id;
+ if ( $dupcheck->id && ( !$self->id || $self->id != $dupcheck->id ) ) {
+ return ( 0, $self->loc( "Group name '[_1]' is already in use", $value ) );
+ }
return 1;
}
diff --git a/rt/lib/RT/GroupMember.pm b/rt/lib/RT/GroupMember.pm
index 8df4a73..e3c5e1d 100755
--- a/rt/lib/RT/GroupMember.pm
+++ b/rt/lib/RT/GroupMember.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/GroupMembers.pm b/rt/lib/RT/GroupMembers.pm
index 0b56c02..52244bd 100755
--- a/rt/lib/RT/GroupMembers.pm
+++ b/rt/lib/RT/GroupMembers.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm
index 578109c..e7734e0 100755
--- a/rt/lib/RT/Groups.pm
+++ b/rt/lib/RT/Groups.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -392,7 +392,7 @@ sub ForWhichCurrentUserHasRight {
=head2 LimitToEnabled
-Only find items that haven\'t been disabled
+Only find items that haven't been disabled
=cut
@@ -450,7 +450,7 @@ sub Next {
sub _DoSearch {
my $self = shift;
- #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ #unless we really want to find disabled rows, make sure we're only finding enabled ones.
unless($self->{'find_disabled_rows'}) {
$self->LimitToEnabled();
}
diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm
index 03c262b..b449d20 100644
--- a/rt/lib/RT/Handle.pm
+++ b/rt/lib/RT/Handle.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -262,17 +262,19 @@ sub CheckCompatibility {
unless $version;
($version) = $version =~ /^(\d+\.\d+)/;
- return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
- if $version < 4;
+ return (0, "RT is unsupported on MySQL versions before 4.1. Your version is $version.")
+ if $version < 4.1;
# MySQL must have InnoDB support
- my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
- if ( lc $innodb eq "no" ) {
+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
+ my $innodb = lc($dbh->selectall_hashref("SHOW ENGINES", "engine")->{InnoDB}{support} || "no");
+ if ( $innodb eq "no" ) {
return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
- "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
- } elsif ( lc $innodb eq "disabled" ) {
+ "See <http://dev.mysql.com/doc/mysql/en/innodb-storage-engine.html>\n".
+ "and check that there are no 'skip-innodb' lines in your my.cnf.");
+ } elsif ( $innodb eq "disabled" ) {
return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
- "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
+ "Remove the 'skip-innodb' or 'innodb = OFF' line from your my.cnf file, restart MySQL, and try again.\n");
}
if ( $state eq 'post' ) {
@@ -280,14 +282,19 @@ sub CheckCompatibility {
unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
}
- }
- if ( $version >= 4.1 && $state eq 'post' ) {
- my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
+
+ $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
."Follow instructions in the UPGRADING.mysql file.");
}
}
+
+ my $max_packet = ($dbh->selectrow_array("show variables like 'max_allowed_packet'"))[1];
+ if ($state =~ /^(create|post)$/ and $max_packet <= (1024 * 1024)) {
+ my $max_packet = sprintf("%.1fM", $max_packet/1024/1024);
+ warn "max_allowed_packet is set to $max_packet, which limits the maximum attachment or email size that RT can process. Consider adjusting MySQL's max_allowed_packet setting.\n";
+ }
}
return (1)
}
@@ -578,7 +585,13 @@ sub cmp_version($$) {
return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
}
return 0;
-}}
+}
+
+sub version_words {
+ return keys %word;
+}
+
+}
=head2 InsertInitialData
diff --git a/rt/lib/RT/I18N.pm b/rt/lib/RT/I18N.pm
index e453cfa..0e75b9f 100644
--- a/rt/lib/RT/I18N.pm
+++ b/rt/lib/RT/I18N.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -209,16 +209,27 @@ sub SetMIMEEntityToEncoding {
# do the same for parts first of all
SetMIMEEntityToEncoding( $_, $enc, $preserve_words ) foreach $entity->parts;
- my $charset = _FindOrGuessCharset($entity) or return;
+ my $head = $entity->head;
+
+ my $charset = _FindOrGuessCharset($entity);
+ if ( $charset ) {
+ unless( Encode::find_encoding($charset) ) {
+ $RT::Logger->warning("Encoding '$charset' is not supported");
+ $charset = undef;
+ }
+ }
+ unless ( $charset ) {
+ $head->replace( "X-RT-Original-Content-Type" => $head->mime_attr('Content-Type') );
+ $head->mime_attr('Content-Type' => 'application/octet-stream');
+ return;
+ }
SetMIMEHeadToEncoding(
- $entity->head,
+ $head,
_FindOrGuessCharset($entity, 1) => $enc,
$preserve_words
);
- my $head = $entity->head;
-
# If this is a textual entity, we'd need to preserve its original encoding
$head->replace( "X-RT-Original-Encoding" => $charset )
if $head->mime_attr('content-type.charset') or IsTextualContentType($head->mime_type);
@@ -293,18 +304,30 @@ sub DecodeMIMEWordsToEncoding {
$str = MIME::Field::ParamVal->parse($str)->stringify;
}
+ # Pre-parse by removing all whitespace between encoded words
+ my $encoded_word = qr/
+ =\? # =?
+ ([^?]+?) # charset
+ (?:\*[^?]+)? # optional '*language'
+ \? # ?
+ ([QqBb]) # encoding
+ \? # ?
+ ([^?]+) # encoded string
+ \?= # ?=
+ /x;
+ $str =~ s/($encoded_word)\s+(?=$encoded_word)/$1/g;
+
+ # Also merge quoted-printable sections together, in case multiple
+ # octets of a single encoded character were split between chunks.
+ # Though not valid according to RFC 2047, this has been seen in the
+ # wild.
+ 1 while $str =~ s/(=\?[^?]+\?[Qq]\?)([^?]+)\?=\1([^?]+)\?=/$1$2$3?=/i;
+
# XXX TODO: use decode('MIME-Header', ...) and Encode::Alias to replace our
# custom MIME word decoding and charset canonicalization. We can't do this
# until we parse before decode, instead of the other way around.
my @list = $str =~ m/(.*?) # prefix
- =\? # =?
- ([^?]+?) # charset
- (?:\*[^?]+)? # optional '*language'
- \? # ?
- ([QqBb]) # encoding
- \? # ?
- ([^?]+) # encoded string
- \?= # ?=
+ $encoded_word
([^=]*) # trailing
/xgcs;
@@ -336,7 +359,14 @@ sub DecodeMIMEWordsToEncoding {
# now we have got a decoded subject, try to convert into the encoding
if ( $charset ne $to_charset || $charset =~ /^utf-?8(?:-strict)?$/i ) {
- Encode::from_to( $enc_str, $charset, $to_charset );
+ if ( Encode::find_encoding($charset) ) {
+ Encode::from_to( $enc_str, $charset, $to_charset );
+ } else {
+ $RT::Logger->warning("Charset '$charset' is not supported");
+ $enc_str =~ s/[^[:print:]]/\357\277\275/g;
+ Encode::from_to( $enc_str, 'UTF-8', $to_charset )
+ unless $to_charset eq 'utf-8';
+ }
}
# XXX TODO: RT doesn't currently do the right thing with mime-encoded headers
diff --git a/rt/lib/RT/I18N/cs.pm b/rt/lib/RT/I18N/cs.pm
index 58631b6..faea9d7 100644
--- a/rt/lib/RT/I18N/cs.pm
+++ b/rt/lib/RT/I18N/cs.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/I18N/i_default.pm b/rt/lib/RT/I18N/i_default.pm
index c220bc0..2b48c62 100644
--- a/rt/lib/RT/I18N/i_default.pm
+++ b/rt/lib/RT/I18N/i_default.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/I18N/ru.pm b/rt/lib/RT/I18N/ru.pm
index 1635a18..a98636f 100755
--- a/rt/lib/RT/I18N/ru.pm
+++ b/rt/lib/RT/I18N/ru.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Installer.pm b/rt/lib/RT/Installer.pm
index d12abb6..d876e10 100644
--- a/rt/lib/RT/Installer.pm
+++ b/rt/lib/RT/Installer.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -97,6 +97,7 @@ my %Meta = (
},
},
DatabaseAdmin => {
+ SkipWrite => 1,
Widget => '/Widgets/Form/String',
WidgetArguments => {
Default => 1,
@@ -106,6 +107,7 @@ my %Meta = (
},
},
DatabaseAdminPassword => {
+ SkipWrite => 1,
Widget => '/Widgets/Form/String',
WidgetArguments => {
Description => 'DBA password', #loc
@@ -149,6 +151,7 @@ my %Meta = (
},
},
Password => {
+ SkipWrite => 1,
Widget => '/Widgets/Form/String',
WidgetArguments => {
Description => 'Administrative password', #loc
@@ -274,10 +277,10 @@ sub SaveConfig {
$RT::Installer->{InstallConfig}{rtname};
if ( open my $fh, '>', $file ) {
- for ( keys %{ $RT::Installer->{InstallConfig} } ) {
+ for ( sort keys %{ $RT::Installer->{InstallConfig} } ) {
# we don't want to store root's password in config.
- next if $_ eq 'Password';
+ next if $class->Meta($_) and $class->Meta($_)->{SkipWrite};
$RT::Installer->{InstallConfig}{$_} = ''
unless defined $RT::Installer->{InstallConfig}{$_};
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm
index bcdc13c..c1a6f4f 100644
--- a/rt/lib/RT/Interface/CLI.pm
+++ b/rt/lib/RT/Interface/CLI.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -48,6 +48,7 @@
package RT::Interface::CLI;
use strict;
+use warnings;
use RT;
use base 'Exporter';
@@ -92,7 +93,7 @@ our @EXPORT_OK = qw(CleanEnv GetCurrentUser GetMessageContent debug loc);
=head2 CleanEnv
-Removes some of the nastiest nasties from the user\'s environment.
+Removes some of the nastiest nasties from the user's environment.
=cut
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
index dda6f70..ab319e6 100755
--- a/rt/lib/RT/Interface/Email.pm
+++ b/rt/lib/RT/Interface/Email.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -149,6 +149,9 @@ sub CheckForSuspiciousSender {
my ( $From, $junk ) = ParseSenderAddressFromHead($head);
+ # If unparseable (non-ASCII), $From can come back undef
+ return undef if not defined $From;
+
if ( ( $From =~ /^mailer-daemon\@/i )
or ( $From =~ /^postmaster\@/i )
or ( $From eq "" ))
@@ -222,8 +225,8 @@ add 'In-Reply-To' field to the error that points to this message.
=item Attach - optional text that attached to the error as 'message/rfc822' part.
-=item LogLevel - log level under which we should write explanation message into the
-log, by default we log it as critical.
+=item LogLevel - log level under which we should write the subject and
+explanation message into the log, by default we log it as critical.
=back
@@ -244,7 +247,7 @@ sub MailError {
$RT::Logger->log(
level => $args{'LogLevel'},
- message => $args{'Explanation'}
+ message => "$args{Subject}: $args{'Explanation'}",
) if $args{'LogLevel'};
# the colons are necessary to make ->build include non-standard headers
@@ -1059,7 +1062,7 @@ sub CreateUser {
Takes a hash containing QueueObj, Head and CurrentUser objects.
Returns a list of all email addresses in the To and Cc
-headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+headers b<except> the current Queue's email addresses, the CurrentUser's
email address and anything that the configuration sub RT::IsRTAddress matches.
=cut
@@ -1101,23 +1104,34 @@ sub IgnoreCcAddress {
=head2 ParseSenderAddressFromHead HEAD
-Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
-of the From (evaluated in order of Reply-To:, From:, Sender)
+Takes a MIME::Header object. Returns (user@host, friendly name, errors)
+where the first two values are the From (evaluated in order of
+Reply-To:, From:, Sender).
+
+A list of error messages may be returned even when a Sender value is
+found, since it could be a parse error for another (checked earlier)
+sender field. In this case, the errors aren't fatal, but may be useful
+to investigate the parse failure.
=cut
sub ParseSenderAddressFromHead {
my $head = shift;
+ my @sender_headers = ('Reply-To', 'From', 'Sender');
+ my @errors; # Accumulate any errors
#Figure out who's sending this message.
- foreach my $header ('Reply-To', 'From', 'Sender') {
+ foreach my $header ( @sender_headers ) {
my $addr_line = $head->get($header) || next;
my ($addr, $name) = ParseAddressFromHeader( $addr_line );
# only return if the address is not empty
- return ($addr, $name) if $addr;
+ return ($addr, $name, @errors) if $addr;
+
+ chomp $addr_line;
+ push @errors, "$header: $addr_line";
}
- return (undef, undef);
+ return (undef, undef, @errors);
}
=head2 ParseErrorsToAddressFromHead HEAD
@@ -1445,6 +1459,7 @@ sub Gateway {
}
@mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins;
$parser->_DecodeBodies;
+ $parser->RescueOutlook;
$parser->_PostProcessNewEntity;
my $head = $Message->head;
@@ -1476,6 +1491,10 @@ sub Gateway {
$args{'ticket'} ||= ExtractTicketId( $Message );
+ # ExtractTicketId may have been overridden, and edited the Subject
+ my $NewSubject = $Message->head->get('Subject');
+ chomp $NewSubject;
+
$SystemTicket = RT::Ticket->new( RT->SystemUser );
$SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
if ( $SystemTicket->id ) {
@@ -1560,9 +1579,11 @@ sub Gateway {
);
}
+ $head->replace('X-RT-Interface' => 'Email');
+
my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
Queue => $SystemQueueObj->Id,
- Subject => $Subject,
+ Subject => $NewSubject,
Requestor => \@Requestors,
Cc => \@Cc,
MIMEObj => $Message
@@ -1615,7 +1636,7 @@ sub Gateway {
#Warn the sender that we couldn't actually submit the comment.
MailError(
To => $ErrorsTo,
- Subject => "Message not recorded: $Subject",
+ Subject => "Message not recorded ($method): $Subject",
Explanation => $msg,
MIMEObj => $Message
);
diff --git a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
index 87a523d..c14bcf0 100755
--- a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
+++ b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
index e733bda..bfe4939 100644
--- a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
+++ b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -66,9 +66,12 @@ sub GetCurrentUser {
# We don't need to do any external lookups
- my ( $Address, $Name ) = ParseSenderAddressFromHead( $args{'Message'}->head );
+ my ( $Address, $Name, @errors ) = ParseSenderAddressFromHead( $args{'Message'}->head );
+ $RT::Logger->warning("Failed to parse ".join(', ', @errors))
+ if @errors;
+
unless ( $Address ) {
- $RT::Logger->error("Couldn't find sender's address");
+ $RT::Logger->error("Couldn't parse or find sender's address");
return ( $args{'CurrentUser'}, -1 );
}
diff --git a/rt/lib/RT/Interface/REST.pm b/rt/lib/RT/Interface/REST.pm
index aed8f39..5f8ff99 100644
--- a/rt/lib/RT/Interface/REST.pm
+++ b/rt/lib/RT/Interface/REST.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -58,8 +58,7 @@ sub custom_field_spec {
my $self = shift;
my $capture = shift;
- my $CF_char = '[\sa-z0-9_ :()/-]';
- my $CF_name = $CF_char . '+';
+ my $CF_name = '[^,]+';
$CF_name = '(' . $CF_name . ')' if $capture;
my $new_style = 'CF\.\{'.$CF_name.'\}';
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index 745a6f1..bdad213 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -266,6 +266,7 @@ sub HandleRequest {
# make user info up to date
$HTML::Mason::Commands::session{'CurrentUser'}
->Load( $HTML::Mason::Commands::session{'CurrentUser'}->id );
+ undef $HTML::Mason::Commands::session{'CurrentUser'}->{'LangHandle'};
}
else {
$HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new();
@@ -285,6 +286,10 @@ sub HandleRequest {
# Process per-page authentication callbacks
$HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Auth', CallbackPage => '/autohandler' );
+ if ( $ARGS->{'NotMobile'} ) {
+ $HTML::Mason::Commands::session{'NotMobile'} = 1;
+ }
+
unless ( _UserLoggedIn() ) {
_ForceLogout();
@@ -302,10 +307,14 @@ sub HandleRequest {
$m->out("\n$msg\n") if $msg;
$m->abort;
}
- # Specially handle /index.html so that we get a nicer URL
- elsif ( $m->request_comp->path eq '/index.html' ) {
- my $next = SetNextPage($ARGS);
- $m->comp('/NoAuth/Login.html', next => $next, actions => [$msg]);
+ # Specially handle /index.html and /m/index.html so that we get a nicer URL
+ elsif ( $m->request_comp->path =~ m{^(/m)?/index\.html$} ) {
+ my $mobile = $1 ? 1 : 0;
+ my $next = SetNextPage($ARGS);
+ $m->comp('/NoAuth/Login.html',
+ next => $next,
+ actions => [$msg],
+ mobile => $mobile);
$m->abort;
}
else {
@@ -325,7 +334,7 @@ sub HandleRequest {
ShowRequestedPage($ARGS);
LogRecordedSQLStatements(RequestData => {
- Path => $HTML::Mason::Commands::m->request_comp->path,
+ Path => $HTML::Mason::Commands::m->request_path,
});
# Process per-page final cleanup callbacks
@@ -436,6 +445,10 @@ sub TangentForLogin {
my $ARGS = shift;
my $hash = SetNextPage($ARGS);
my %query = (@_, next => $hash);
+
+ $query{mobile} = 1
+ if $HTML::Mason::Commands::m->request_comp->path =~ m{^/m(/|$)};
+
my $login = RT->Config->Get('WebURL') . 'NoAuth/Login.html?';
$login .= $HTML::Mason::Commands::m->comp('/Elements/QueryString', %query);
Redirect($login);
@@ -563,6 +576,7 @@ sub MaybeRejectPrivateComponentRequest {
/ # leading slash
( Elements |
_elements | # mobile UI
+ Callbacks |
Widgets |
autohandler | # requesting this directly is suspicious
l (_unsafe)? ) # loc component
@@ -792,7 +806,7 @@ sub LoadSessionFromCookie {
my $SessionCookie = ( $cookies{$cookiename} ? $cookies{$cookiename}->value : undef );
tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', $SessionCookie;
unless ( $SessionCookie && $HTML::Mason::Commands::session{'_session_id'} eq $SessionCookie ) {
- undef $cookies{$cookiename};
+ InstantiateNewSession();
}
if ( int RT->Config->Get('AutoLogoff') ) {
my $now = int( time / 60 );
@@ -877,6 +891,38 @@ sub Redirect {
$HTML::Mason::Commands::m->abort;
}
+=head2 CacheControlExpiresHeaders
+
+set both Cache-Control and Expires http headers
+
+=cut
+
+sub CacheControlExpiresHeaders {
+ my %args = @_;
+
+ my $Visibility = 'private';
+ if ( ! defined $args{Time} ) {
+ $args{Time} = 0;
+ } elsif ( $args{Time} eq 'no-cache' ) {
+ $args{Time} = 0;
+ } elsif ( $args{Time} eq 'forever' ) {
+ $args{Time} = 30 * 24 * 60 * 60;
+ $Visibility = 'public';
+ }
+
+ my $CacheControl = $args{Time}
+ ? sprintf "max-age=%d, %s", $args{Time}, $Visibility
+ : 'no-cache'
+ ;
+ $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = $CacheControl;
+
+ my $expires = RT::Date->new(RT->SystemUser);
+ $expires->SetToNow;
+ $expires->AddSeconds( $args{Time} ) if $args{Time};
+
+ $HTML::Mason::Commands::r->headers_out->{'Expires'} = $expires->RFC2616;
+}
+
=head2 StaticFileHeaders
Send the browser a few headers to try to get it to (somewhat agressively)
@@ -889,16 +935,12 @@ This routine could really use _accurate_ heuristics. (XXX TODO)
sub StaticFileHeaders {
my $date = RT::Date->new(RT->SystemUser);
- # make cache public
- $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
-
# remove any cookie headers -- if it is cached publicly, it
# shouldn't include anyone's cookie!
delete $HTML::Mason::Commands::r->err_headers_out->{'Set-Cookie'};
# Expire things in a month.
- $date->Set( Value => time + 30 * 24 * 60 * 60 );
- $HTML::Mason::Commands::r->headers_out->{'Expires'} = $date->RFC2616;
+ CacheControlExpiresHeaders( Time => 'forever' );
# if we set 'Last-Modified' then browser request a comp using 'If-Modified-Since'
# request, but we don't handle it and generate full reply again
@@ -912,15 +954,15 @@ sub StaticFileHeaders {
Takes C<PATH> and returns a boolean indicating that the user-specified partial
component path is safe.
-Currently "safe" means that the path does not start with a dot (C<.>) and does
-not contain a slash-dot C</.>.
+Currently "safe" means that the path does not start with a dot (C<.>), does
+not contain a slash-dot C</.>, and does not contain any nulls.
=cut
sub ComponentPathIsSafe {
my $self = shift;
my $path = shift;
- return $path !~ m{(?:^|/)\.};
+ return $path !~ m{(?:^|/)\.} and $path !~ m{\0};
}
=head2 PathIsSafe
@@ -1187,32 +1229,31 @@ sub ValidateWebConfig {
return if $_has_validated_web_config;
$_has_validated_web_config = 1;
- if (!$ENV{'rt.explicit_port'} && $ENV{SERVER_PORT} != RT->Config->Get('WebPort')) {
- $RT::Logger->warn("The actual SERVER_PORT ($ENV{SERVER_PORT}) does NOT match the configured WebPort ($RT::WebPort). Perhaps you should Set(\$WebPort, $ENV{SERVER_PORT}); in RT_SiteConfig.pm, otherwise your internal links may be broken.");
- }
-
- if ($ENV{HTTP_HOST}) {
- # match "example.com" or "example.com:80"
- my ($host) = $ENV{HTTP_HOST} =~ /^(.*?)(:\d+)?$/;
+ my $port = $ENV{SERVER_PORT};
+ my $host = $ENV{HTTP_X_FORWARDED_HOST} || $ENV{HTTP_X_FORWARDED_SERVER}
+ || $ENV{HTTP_HOST} || $ENV{SERVER_NAME};
+ ($host, $port) = ($1, $2) if $host =~ /^(.*?):(\d+)$/;
- if ($host ne RT->Config->Get('WebDomain')) {
- $RT::Logger->warn("The actual HTTP_HOST ($host) does NOT match the configured WebDomain ($RT::WebDomain). Perhaps you should Set(\$WebDomain, '$host'); in RT_SiteConfig.pm, otherwise your internal links may be broken.");
- }
+ if ( $port != RT->Config->Get('WebPort') and not $ENV{'rt.explicit_port'}) {
+ $RT::Logger->warn("The requested port ($port) does NOT match the configured WebPort ($RT::WebPort). "
+ ."Perhaps you should Set(\$WebPort, $port); in RT_SiteConfig.pm, "
+ ."otherwise your internal links may be broken.");
}
- else {
- if ($ENV{SERVER_NAME} ne RT->Config->Get('WebDomain')) {
- $RT::Logger->warn("The actual SERVER_NAME ($ENV{SERVER_NAME}) does NOT match the configured WebDomain ($RT::WebDomain). Perhaps you should Set(\$WebDomain, '$ENV{SERVER_NAME}'); in RT_SiteConfig.pm, otherwise your internal links may be broken.");
- }
+
+ if ( $host ne RT->Config->Get('WebDomain') ) {
+ $RT::Logger->warn("The requested host ($host) does NOT match the configured WebDomain ($RT::WebDomain). "
+ ."Perhaps you should Set(\$WebDomain, '$host'); in RT_SiteConfig.pm, "
+ ."otherwise your internal links may be broken.");
}
- #i don't understand how this was ever expected to work
- # (even without our dum double // hack)??
- #if ($ENV{SCRIPT_NAME} ne RT->Config->Get('WebPath')) {
- ( my $WebPath = RT->Config->Get('WebPath') ) =~ s(/+)(/)g;
- ( my $script_name = $ENV{SCRIPT_NAME} ) =~ s(/+)(/)g;
- my $script_name_prefix = substr($script_name, 0, length($WebPath));
- if ( $script_name_prefix ne $WebPath ) {
- $RT::Logger->warn("The actual SCRIPT_NAME ($script_name) does NOT match the configured WebPath ($WebPath). Perhaps you should Set(\$WebPath, '$script_name_prefix'); in RT_SiteConfig.pm, otherwise your internal links may be broken.");
+ # Unfortunately, there is no reliable way to get the _path_ that was
+ # requested at the proxy level; simply disable this warning if we're
+ # proxied and there's a mismatch.
+ my $proxied = $ENV{HTTP_X_FORWARDED_HOST} || $ENV{HTTP_X_FORWARDED_SERVER};
+ if ($ENV{SCRIPT_NAME} ne RT->Config->Get('WebPath') and not $proxied) {
+ $RT::Logger->warn("The requested path ($ENV{SCRIPT_NAME}) does NOT match the configured WebPath ($RT::WebPath). "
+ ."Perhaps you should Set(\$WebPath, '$ENV{SCRIPT_NAME}'); in RT_SiteConfig.pm, "
+ ."otherwise your internal links may be broken.");
}
}
@@ -1286,16 +1327,18 @@ sub IsCompCSRFWhitelisted {
# record.
delete $args{id};
- # If they have a valid results= from MaybeRedirectForResults, that's
- # also fine.
- delete $args{results} if $args{results}
- and $HTML::Mason::Commands::session{"Actions"}->{$args{results}};
+ # If they have a results= from MaybeRedirectForResults, that's also fine.
+ delete $args{results};
# The homepage refresh, which uses the Refresh header, doesn't send
# a referer in most browsers; whitelist the one parameter it reloads
# with, HomeRefreshInterval, which is safe
delete $args{HomeRefreshInterval};
+ # The NotMobile flag is fine for any page; it's only used to toggle a flag
+ # in the session related to which interface you get.
+ delete $args{NotMobile};
+
# If there are no arguments, then it's likely to be an idempotent
# request, which are not susceptible to CSRF
return 1 if !%args;
@@ -1711,6 +1754,7 @@ sub CreateTicket {
Cc => $ARGS{'Cc'},
Body => $sigless,
Type => $ARGS{'ContentType'},
+ Interface => RT::Interface::Web::MobileClient() ? 'Mobile' : 'Web',
);
if ( $ARGS{'Attachments'} ) {
@@ -1929,6 +1973,7 @@ sub ProcessUpdateMessage {
Subject => $args{ARGSRef}->{'UpdateSubject'},
Body => $args{ARGSRef}->{'UpdateContent'},
Type => $args{ARGSRef}->{'UpdateContentType'},
+ Interface => RT::Interface::Web::MobileClient() ? 'Mobile' : 'Web',
);
$Message->head->replace( 'Message-ID' => Encode::encode_utf8(
@@ -2067,11 +2112,13 @@ sub MakeMIMEEntity {
Body => undef,
AttachmentFieldName => undef,
Type => undef,
+ Interface => 'API',
@_,
);
my $Message = MIME::Entity->build(
Type => 'multipart/mixed',
"Message-Id" => Encode::encode_utf8( RT::Interface::Email::GenMessageId ),
+ "X-RT-Interface" => $args{Interface},
map { $_ => Encode::encode_utf8( $args{ $_} ) }
grep defined $args{$_}, qw(Subject From Cc)
);
@@ -2113,8 +2160,9 @@ sub MakeMIMEEntity {
$Message->head->set( 'Subject' => $filename );
}
- # Attachment parts really shouldn't get a Message-ID
+ # Attachment parts really shouldn't get a Message-ID or "interface"
$Message->head->delete('Message-ID');
+ $Message->head->delete('X-RT-Interface');
}
}
@@ -2126,6 +2174,37 @@ sub MakeMIMEEntity {
}
+sub ProcessAttachments {
+ my %args = (
+ ARGSRef => {},
+ @_
+ );
+
+ my $ARGSRef = $args{ARGSRef} || {};
+ # deal with deleting uploaded attachments
+ foreach my $key ( keys %$ARGSRef ) {
+ if ( $key =~ m/^DeleteAttach-(.+)$/ ) {
+ delete $session{'Attachments'}{$1};
+ }
+ $session{'Attachments'} = { %{ $session{'Attachments'} || {} } };
+ }
+
+ # store the uploaded attachment in session
+ if ( defined $ARGSRef->{'Attach'} && length $ARGSRef->{'Attach'} )
+ { # attachment?
+ my $attachment = MakeMIMEEntity( AttachmentFieldName => 'Attach' );
+
+ my $file_path = Encode::decode_utf8("$ARGSRef->{'Attach'}");
+ $session{'Attachments'} =
+ { %{ $session{'Attachments'} || {} }, $file_path => $attachment, };
+ }
+
+ # delete temporary storage entry to make WebUI clean
+ unless ( keys %{ $session{'Attachments'} } and $ARGSRef->{'UpdateAttach'} )
+ {
+ delete $session{'Attachments'};
+ }
+}
=head2 ParseDateToISO
@@ -2220,19 +2299,8 @@ sub ProcessACLs {
# Check if we want to grant rights to a previously rights-less user
for my $type (qw(user group)) {
- my $key = "AddPrincipalForRights-$type";
-
- next unless $ARGSref->{$key};
-
- my $principal;
- if ( $type eq 'user' ) {
- $principal = RT::User->new( $session{'CurrentUser'} );
- $principal->LoadByCol( Name => $ARGSref->{$key} );
- }
- else {
- $principal = RT::Group->new( $session{'CurrentUser'} );
- $principal->LoadUserDefinedGroup( $ARGSref->{$key} );
- }
+ my $principal = _ParseACLNewPrincipal($ARGSref, $type)
+ or next;
unless ($principal->PrincipalId) {
push @results, loc("Couldn't load the specified principal");
@@ -2332,7 +2400,34 @@ sub ProcessACLs {
return (@results);
}
+=head2 _ParseACLNewPrincipal
+
+Takes a hashref of C<%ARGS> and a principal type (C<user> or C<group>). Looks
+for the presence of rights being added on a principal of the specified type,
+and returns undef if no new principal is being granted rights. Otherwise loads
+up an L<RT::User> or L<RT::Group> object and returns it. Note that the object
+may not be successfully loaded, and you should check C<->id> yourself.
+
+=cut
+
+sub _ParseACLNewPrincipal {
+ my $ARGSref = shift;
+ my $type = lc shift;
+ my $key = "AddPrincipalForRights-$type";
+
+ return unless $ARGSref->{$key};
+ my $principal;
+ if ( $type eq 'user' ) {
+ $principal = RT::User->new( $session{'CurrentUser'} );
+ $principal->LoadByCol( Name => $ARGSref->{$key} );
+ }
+ elsif ( $type eq 'group' ) {
+ $principal = RT::Group->new( $session{'CurrentUser'} );
+ $principal->LoadUserDefinedGroup( $ARGSref->{$key} );
+ }
+ return $principal;
+}
=head2 UpdateRecordObj ( ARGSRef => \%ARGS, Object => RT::Record, AttributesRef => \@attribs)
@@ -2542,12 +2637,17 @@ sub ProcessTicketReminders {
Format => 'unknown',
Value => $args->{'NewReminder-Due'}
);
- my ( $add_id, $msg, $txnid ) = $Ticket->Reminders->Add(
+ my ( $add_id, $msg ) = $Ticket->Reminders->Add(
Subject => $args->{'NewReminder-Subject'},
Owner => $args->{'NewReminder-Owner'},
Due => $due_obj->ISO
);
- push @results, loc("Reminder '[_1]' added", $args->{'NewReminder-Subject'});
+ if ( $add_id ) {
+ push @results, loc("Reminder '[_1]' added", $args->{'NewReminder-Subject'});
+ }
+ else {
+ push @results, $msg;
+ }
}
return @results;
}
@@ -3010,6 +3110,24 @@ sub ProcessRecordLinks {
return (@results);
}
+=head2 ProcessTransactionSquelching
+
+Takes a hashref of the submitted form arguments, C<%ARGS>.
+
+Returns a hash of squelched addresses.
+
+=cut
+
+sub ProcessTransactionSquelching {
+ my $args = shift;
+ my %checked = map { $_ => 1 } grep { defined }
+ ( ref $args->{'TxnSendMailTo'} eq "ARRAY" ? @{$args->{'TxnSendMailTo'}} :
+ defined $args->{'TxnSendMailTo'} ? ($args->{'TxnSendMailTo'}) :
+ () );
+ my %squelched = map { $_ => 1 } grep { not $checked{$_} } split /,/, ($args->{'TxnRecipients'}||'');
+ return %squelched;
+}
+
=head2 _UploadedFile ( $arg );
Takes a CGI parameter name; if a file is uploaded under that name,
@@ -3235,9 +3353,9 @@ our @SCRUBBER_ALLOWED_TAGS = qw(
);
our %SCRUBBER_ALLOWED_ATTRIBUTES = (
- # Match http, ftp and relative urls
+ # Match http, https, ftp, mailto and relative urls
# XXX: we also scrub format strings with this module then allow simple config options
- href => qr{^(?:http:|ftp:|https:|/|__Web(?:Path|BaseURL|URL)__)}i,
+ href => qr{^(?:https?:|ftp:|mailto:|/|__Web(?:Path|BaseURL|URL)__)}i,
face => 1,
size => 1,
target => 1,
diff --git a/rt/lib/RT/Interface/Web/Handler.pm b/rt/lib/RT/Interface/Web/Handler.pm
index a740167..a1784c2 100644
--- a/rt/lib/RT/Interface/Web/Handler.pm
+++ b/rt/lib/RT/Interface/Web/Handler.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -205,10 +205,44 @@ sub CleanupRequest {
sub HTML::Mason::Exception::as_rt_error {
my ($self) = @_;
- $RT::Logger->error( $self->full_message );
+ $RT::Logger->error( $self->as_text );
return "An internal RT error has occurred. Your administrator can find more details in RT's log files.";
}
+=head1 CheckModPerlHandler
+
+Make sure we're not running with SetHandler perl-script.
+
+=cut
+
+sub CheckModPerlHandler{
+ my $self = shift;
+ my $env = shift;
+
+ # Plack::Handler::Apache2 masks MOD_PERL, so use MOD_PERL_API_VERSION
+ return unless( $env->{'MOD_PERL_API_VERSION'}
+ and $env->{'MOD_PERL_API_VERSION'} == 2);
+
+ my $handler = $env->{'psgi.input'}->handler;
+
+ return unless defined $handler && $handler eq 'perl-script';
+
+ $RT::Logger->critical(<<MODPERL);
+RT has problems when SetHandler is set to perl-script.
+Change SetHandler in your in httpd.conf to:
+
+ SetHandler modperl
+
+For a complete example mod_perl configuration, see:
+
+https://bestpractical.com/rt/docs/@{[$RT::VERSION =~ /^(\d\.\d)/]}/web_deployment.html#mod_perl-2.xx
+MODPERL
+
+ my $res = Plack::Response->new(500);
+ $res->content_type("text/plain");
+ $res->body("Server misconfiguration; see error log for details");
+ return $res;
+}
# PSGI App
@@ -231,6 +265,12 @@ sub PSGIApp {
return sub {
my $env = shift;
+
+ {
+ my $res = $self->CheckModPerlHandler($env);
+ return $self->_psgi_response_cb( $res->finalize ) if $res;
+ }
+
RT::ConnectToDatabase() unless RT->InstallMode;
my $req = Plack::Request->new($env);
diff --git a/rt/lib/RT/Interface/Web/Menu.pm b/rt/lib/RT/Interface/Web/Menu.pm
index 045df1f..e4e08d6 100644
--- a/rt/lib/RT/Interface/Web/Menu.pm
+++ b/rt/lib/RT/Interface/Web/Menu.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -314,4 +314,59 @@ sub children {
return wantarray ? @kids : \@kids;
}
+=head2 add_after
+
+Called on a child, inserts a new menu item after it and shifts any other
+menu items at this level to the right.
+
+L<child> by default would insert at the end of the list of children, unless you
+did manual sort_order calculations.
+
+Takes all the regular arguments to L<child>.
+
+=cut
+
+sub add_after { shift->_insert_sibling("after", @_) }
+
+=head2 add_before
+
+Called on a child, inserts a new menu item at the child's location and shifts
+the child and the other menu items at this level to the right.
+
+L<child> by default would insert at the end of the list of children, unless you
+did manual sort_order calculations.
+
+Takes all the regular arguments to L<child>.
+
+=cut
+
+sub add_before { shift->_insert_sibling("before", @_) }
+
+sub _insert_sibling {
+ my $self = shift;
+ my $where = shift;
+ my $parent = $self->parent;
+ my $sort_order;
+ for my $contemporary ($parent->children) {
+ if ( $contemporary->key eq $self->key ) {
+ if ($where eq "before") {
+ # Bump the current child and the following
+ $sort_order = $contemporary->sort_order;
+ }
+ elsif ($where eq "after") {
+ # Leave the current child along, bump the rest
+ $sort_order = $contemporary->sort_order + 1;
+ next;
+ }
+ else {
+ # never set $sort_order, act no differently than ->child()
+ }
+ }
+ if ( $sort_order ) {
+ $contemporary->sort_order( $contemporary->sort_order + 1 );
+ }
+ }
+ $parent->child( @_, sort_order => $sort_order );
+}
+
1;
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder.pm b/rt/lib/RT/Interface/Web/QueryBuilder.pm
index 79a0b97..5464278 100755
--- a/rt/lib/RT/Interface/Web/QueryBuilder.pm
+++ b/rt/lib/RT/Interface/Web/QueryBuilder.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
index 2cfc889..9bbd876 100755
--- a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+++ b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/Request.pm b/rt/lib/RT/Interface/Web/Request.pm
index d086511..cdd4594 100644
--- a/rt/lib/RT/Interface/Web/Request.pm
+++ b/rt/lib/RT/Interface/Web/Request.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -142,6 +142,10 @@ sub callback {
}
return @rv;
}
+
+sub clear_callback_cache {
+ %cache = %called = ();
+}
}
=head2 request_path
@@ -165,4 +169,21 @@ sub request_path {
return $path;
}
+=head2 abort
+
+Logs any recorded SQL statements for this request before calling the standard
+abort.
+
+=cut
+
+sub abort {
+ my $self = shift;
+ RT::Interface::Web::LogRecordedSQLStatements(
+ RequestData => {
+ Path => $self->request_path,
+ },
+ );
+ return $self->SUPER::abort(@_);
+}
+
1;
diff --git a/rt/lib/RT/Interface/Web/Session.pm b/rt/lib/RT/Interface/Web/Session.pm
index c5b88f1..4edd9bd 100644
--- a/rt/lib/RT/Interface/Web/Session.pm
+++ b/rt/lib/RT/Interface/Web/Session.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -207,8 +207,8 @@ sub _ClearOldDir {
foreach my $id( @{ $self->Ids } ) {
if( int $older_than ) {
- my $ctime = (stat(File::Spec->catfile($dir,$id)))[9];
- if( $ctime > $now - $older_than ) {
+ my $mtime = (stat(File::Spec->catfile($dir,$id)))[9];
+ if( $mtime > $now - $older_than ) {
$RT::Logger->debug("skipped session '$id', isn't old");
next;
}
@@ -224,6 +224,10 @@ sub _ClearOldDir {
tied(%session)->delete;
$RT::Logger->info("successfuly deleted session '$id'");
}
+
+ my $lock = Apache::Session::Lock::File->new;
+ $lock->clean( $dir, $older_than );
+
return;
}
diff --git a/rt/lib/RT/Lifecycle.pm b/rt/lib/RT/Lifecycle.pm
index 056599e..c905282 100644
--- a/rt/lib/RT/Lifecycle.pm
+++ b/rt/lib/RT/Lifecycle.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -411,8 +411,8 @@ sub Transitions {
return %{ $self->{'data'}{'transitions'} || {} }
unless @_;
- my $status = shift;
- return @{ $self->{'data'}{'transitions'}{ $status || '' } || [] };
+ my $status = shift || '';
+ return @{ $self->{'data'}{'transitions'}{ lc $status } || [] };
}
=head1 IsTransition
@@ -439,8 +439,8 @@ be checked on the ticket.
sub CheckRight {
my $self = shift;
- my $from = shift;
- my $to = shift;
+ my $from = lc shift;
+ my $to = lc shift;
if ( my $rights = $self->{'data'}{'rights'} ) {
my $check =
$rights->{ $from .' -> '. $to }
@@ -536,10 +536,11 @@ pairs:
sub Actions {
my $self = shift;
my $from = shift || return ();
+ $from = lc $from;
$self->FillCache unless keys %LIFECYCLES_CACHE;
- my @res = grep $_->{'from'} eq $from || ( $_->{'from'} eq '*' && $_->{'to'} ne $from ),
+ my @res = grep lc $_->{'from'} eq $from || ( $_->{'from'} eq '*' && lc $_->{'to'} ne $from ),
@{ $self->{'data'}{'actions'} };
# skip '* -> x' if there is '$from -> x'
@@ -633,6 +634,13 @@ sub ForLocalization {
sub loc { return RT->SystemUser->loc( @_ ) }
+sub CanonicalCase {
+ my $self = shift;
+ my ($status) = @_;
+ return undef unless defined $status;
+ return($self->{data}{canonical_case}{lc $status} || lc $status);
+}
+
sub FillCache {
my $self = shift;
@@ -647,45 +655,123 @@ sub FillCache {
active => [],
inactive => [],
);
- foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
- my @res;
+ foreach my $name ( keys %LIFECYCLES_CACHE ) {
+ next if $name eq "__maps__";
+ my $lifecycle = $LIFECYCLES_CACHE{$name};
+
+ my @statuses;
+ $lifecycle->{canonical_case} = {};
foreach my $type ( qw(initial active inactive) ) {
- push @{ $all{ $type } }, @{ $lifecycle->{ $type } || [] };
- push @res, @{ $lifecycle->{ $type } || [] };
+ for my $status (@{ $lifecycle->{ $type } || [] }) {
+ if (exists $lifecycle->{canonical_case}{lc $status}) {
+ warn "Duplicate status @{[lc $status]} in lifecycle $name";
+ } else {
+ $lifecycle->{canonical_case}{lc $status} = $status;
+ }
+ push @{ $all{ $type } }, $status;
+ push @statuses, $status;
+ }
+ }
+
+ # Lower-case for consistency
+ # ->{actions} are handled below
+ for my $state (keys %{ $lifecycle->{defaults} || {} }) {
+ my $status = $lifecycle->{defaults}{$state};
+ warn "Nonexistant status @{[lc $status]} in default states in $name lifecycle"
+ unless $lifecycle->{canonical_case}{lc $status};
+ $lifecycle->{defaults}{$state} =
+ $lifecycle->{canonical_case}{lc $status} || lc $status;
+ }
+ for my $from (keys %{ $lifecycle->{transitions} || {} }) {
+ warn "Nonexistant status @{[lc $from]} in transitions in $name lifecycle"
+ unless $from eq '' or $lifecycle->{canonical_case}{lc $from};
+ for my $status ( @{delete($lifecycle->{transitions}{$from}) || []} ) {
+ warn "Nonexistant status @{[lc $status]} in transitions in $name lifecycle"
+ unless $lifecycle->{canonical_case}{lc $status};
+ push @{ $lifecycle->{transitions}{lc $from} },
+ $lifecycle->{canonical_case}{lc $status} || lc $status;
+ }
+ }
+ for my $schema (keys %{ $lifecycle->{rights} || {} }) {
+ my ($from, $to) = split /\s*->\s*/, $schema, 2;
+ unless ($from and $to) {
+ warn "Invalid right transition $schema in $name lifecycle";
+ next;
+ }
+ warn "Nonexistant status @{[lc $from]} in right transition in $name lifecycle"
+ unless $from eq '*' or $lifecycle->{canonical_case}{lc $from};
+ warn "Nonexistant status @{[lc $to]} in right transition in $name lifecycle"
+ unless $to eq '*' or $lifecycle->{canonical_case}{lc $to};
+ $lifecycle->{rights}{lc($from) . " -> " .lc($to)}
+ = delete $lifecycle->{rights}{$schema};
}
my %seen;
- @res = grep !$seen{ lc $_ }++, @res;
- $lifecycle->{''} = \@res;
+ @statuses = grep !$seen{ $_ }++, @statuses;
+ $lifecycle->{''} = \@statuses;
unless ( $lifecycle->{'transitions'}{''} ) {
- $lifecycle->{'transitions'}{''} = [ grep $_ ne 'deleted', @res ];
+ $lifecycle->{'transitions'}{''} = [ grep $_ ne 'deleted', @statuses ];
}
- }
- foreach my $type ( qw(initial active inactive), '' ) {
- my %seen;
- @{ $all{ $type } } = grep !$seen{ lc $_ }++, @{ $all{ $type } };
- push @{ $all{''} }, @{ $all{ $type } } if $type;
- }
- $LIFECYCLES_CACHE{''} = \%all;
- foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
- my @res;
+ my @actions;
if ( ref $lifecycle->{'actions'} eq 'HASH' ) {
foreach my $k ( sort keys %{ $lifecycle->{'actions'} } ) {
- push @res, $k, $lifecycle->{'actions'}{ $k };
+ push @actions, $k, $lifecycle->{'actions'}{ $k };
}
} elsif ( ref $lifecycle->{'actions'} eq 'ARRAY' ) {
- @res = @{ $lifecycle->{'actions'} };
+ @actions = @{ $lifecycle->{'actions'} };
}
- my @tmp = splice @res;
- while ( my ($transition, $info) = splice @tmp, 0, 2 ) {
+ $lifecycle->{'actions'} = [];
+ while ( my ($transition, $info) = splice @actions, 0, 2 ) {
my ($from, $to) = split /\s*->\s*/, $transition, 2;
- push @res, { %$info, from => $from, to => $to };
+ unless ($from and $to) {
+ warn "Invalid action status change $transition in $name lifecycle";
+ next;
+ }
+ warn "Nonexistant status @{[lc $from]} in action in $name lifecycle"
+ unless $from eq '*' or $lifecycle->{canonical_case}{lc $from};
+ warn "Nonexistant status @{[lc $to]} in action in $name lifecycle"
+ unless $to eq '*' or $lifecycle->{canonical_case}{lc $to};
+ push @{ $lifecycle->{'actions'} },
+ { %$info,
+ from => ($lifecycle->{canonical_case}{lc $from} || lc $from),
+ to => ($lifecycle->{canonical_case}{lc $to} || lc $to), };
}
- $lifecycle->{'actions'} = \@res;
}
+
+ # Lower-case the transition maps
+ for my $mapname (keys %{ $LIFECYCLES_CACHE{'__maps__'} || {} }) {
+ my ($from, $to) = split /\s*->\s*/, $mapname, 2;
+ unless ($from and $to) {
+ warn "Invalid lifecycle mapping $mapname";
+ next;
+ }
+ warn "Nonexistant lifecycle $from in $mapname lifecycle map"
+ unless $LIFECYCLES_CACHE{$from};
+ warn "Nonexistant lifecycle $to in $mapname lifecycle map"
+ unless $LIFECYCLES_CACHE{$to};
+ my $map = delete $LIFECYCLES_CACHE{'__maps__'}{$mapname};
+ $LIFECYCLES_CACHE{'__maps__'}{"$from -> $to"} = $map;
+ for my $status (keys %{ $map }) {
+ warn "Nonexistant status @{[lc $status]} in $from in $mapname lifecycle map"
+ if $LIFECYCLES_CACHE{$from}
+ and not $LIFECYCLES_CACHE{$from}{canonical_case}{lc $status};
+ warn "Nonexistant status @{[lc $map->{$status}]} in $to in $mapname lifecycle map"
+ if $LIFECYCLES_CACHE{$to}
+ and not $LIFECYCLES_CACHE{$to}{canonical_case}{lc $map->{$status}};
+ $map->{lc $status} = lc delete $map->{$status};
+ }
+ }
+
+ foreach my $type ( qw(initial active inactive), '' ) {
+ my %seen;
+ @{ $all{ $type } } = grep !$seen{ $_ }++, @{ $all{ $type } };
+ push @{ $all{''} }, @{ $all{ $type } } if $type;
+ }
+ $LIFECYCLES_CACHE{''} = \%all;
+
return;
}
diff --git a/rt/lib/RT/Link.pm b/rt/lib/RT/Link.pm
index b26f564..7a27747 100644
--- a/rt/lib/RT/Link.pm
+++ b/rt/lib/RT/Link.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -96,33 +96,17 @@ sub Create {
@_ );
my $base = RT::URI->new( $self->CurrentUser );
- $base->FromURI( $args{'Base'} );
-
- unless ( $base->Resolver && $base->Scheme ) {
- my $msg = $self->loc("Couldn't resolve base '[_1]' into a URI.",
- $args{'Base'});
+ unless ($base->FromURI( $args{'Base'} )) {
+ my $msg = $self->loc("Couldn't resolve base '[_1]' into a URI.", $args{'Base'});
$RT::Logger->warning( "$self $msg" );
-
- if (wantarray) {
- return(undef, $msg);
- } else {
- return (undef);
- }
+ return wantarray ? (undef, $msg) : undef;
}
my $target = RT::URI->new( $self->CurrentUser );
- $target->FromURI( $args{'Target'} );
-
- unless ( $target->Resolver ) {
- my $msg = $self->loc("Couldn't resolve target '[_1]' into a URI.",
- $args{'Target'});
+ unless ($target->FromURI( $args{'Target'} )) {
+ my $msg = $self->loc("Couldn't resolve target '[_1]' into a URI.", $args{'Target'});
$RT::Logger->warning( "$self $msg" );
-
- if (wantarray) {
- return(undef, $msg);
- } else {
- return (undef);
- }
+ return wantarray ? (undef, $msg) : undef;
}
my $base_id = 0;
@@ -186,22 +170,21 @@ sub LoadByParams {
@_ );
my $base = RT::URI->new($self->CurrentUser);
- $base->FromURI( $args{'Base'} );
+ $base->FromURI( $args{'Base'} )
+ or return (0, $self->loc("Couldn't parse Base URI: [_1]", $args{Base}));
my $target = RT::URI->new($self->CurrentUser);
- $target->FromURI( $args{'Target'} );
-
- unless ($base->Resolver && $target->Resolver) {
- return ( 0, $self->loc("Couldn't load link") );
- }
-
+ $target->FromURI( $args{'Target'} )
+ or return (0, $self->loc("Couldn't parse Target URI: [_1]", $args{Target}));
my ( $id, $msg ) = $self->LoadByCols( Base => $base->URI,
Type => $args{'Type'},
Target => $target->URI );
unless ($id) {
- return ( 0, $self->loc("Couldn't load link") );
+ return ( 0, $self->loc("Couldn't load link: [_1]", $msg) );
+ } else {
+ return ($id, $msg);
}
}
diff --git a/rt/lib/RT/Links.pm b/rt/lib/RT/Links.pm
index ccc72d7..af36a5b 100644
--- a/rt/lib/RT/Links.pm
+++ b/rt/lib/RT/Links.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectClass.pm b/rt/lib/RT/ObjectClass.pm
index e1c66da..684af13 100644
--- a/rt/lib/RT/ObjectClass.pm
+++ b/rt/lib/RT/ObjectClass.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectClasses.pm b/rt/lib/RT/ObjectClasses.pm
index ac95ade..01cf77f 100644
--- a/rt/lib/RT/ObjectClasses.pm
+++ b/rt/lib/RT/ObjectClasses.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectCustomField.pm b/rt/lib/RT/ObjectCustomField.pm
index 61bc355..e7f350a 100644
--- a/rt/lib/RT/ObjectCustomField.pm
+++ b/rt/lib/RT/ObjectCustomField.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectCustomFieldValue.pm b/rt/lib/RT/ObjectCustomFieldValue.pm
index 98714a0..63da581 100644
--- a/rt/lib/RT/ObjectCustomFieldValue.pm
+++ b/rt/lib/RT/ObjectCustomFieldValue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectCustomFieldValues.pm b/rt/lib/RT/ObjectCustomFieldValues.pm
index dad4a69..a1d5391 100644
--- a/rt/lib/RT/ObjectCustomFieldValues.pm
+++ b/rt/lib/RT/ObjectCustomFieldValues.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -139,7 +139,7 @@ sub _DoSearch {
my $self = shift;
# unless we really want to find disabled rows,
- # make sure we\'re only finding enabled ones.
+ # make sure we're only finding enabled ones.
unless ( $self->{'find_expired_rows'} ) {
$self->LimitToEnabled();
}
@@ -151,7 +151,7 @@ sub _DoCount {
my $self = shift;
# unless we really want to find disabled rows,
- # make sure we\'re only finding enabled ones.
+ # make sure we're only finding enabled ones.
unless ( $self->{'find_expired_rows'} ) {
$self->LimitToEnabled();
}
diff --git a/rt/lib/RT/ObjectCustomFields.pm b/rt/lib/RT/ObjectCustomFields.pm
index 9864949..5bdc069 100644
--- a/rt/lib/RT/ObjectCustomFields.pm
+++ b/rt/lib/RT/ObjectCustomFields.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ObjectTopic.pm b/rt/lib/RT/ObjectTopic.pm
index ae5abb3..8ca01ae 100644
--- a/rt/lib/RT/ObjectTopic.pm
+++ b/rt/lib/RT/ObjectTopic.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -46,18 +46,10 @@
#
# END BPS TAGGED BLOCK }}}
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-
=head1 NAME
RT::ObjectTopic
-
=head1 SYNOPSIS
=head1 DESCRIPTION
@@ -66,8 +58,11 @@ RT::ObjectTopic
=cut
-no warnings 'redefine';
package RT::ObjectTopic;
+use strict;
+use warnings;
+no warnings 'redefine';
+
use RT::Record;
use RT::Topic;
diff --git a/rt/lib/RT/ObjectTopics.pm b/rt/lib/RT/ObjectTopics.pm
index 1ffb146..bdcff77 100644
--- a/rt/lib/RT/ObjectTopics.pm
+++ b/rt/lib/RT/ObjectTopics.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Plugin.pm b/rt/lib/RT/Plugin.pm
index 10eb837..1f97ec2 100644
--- a/rt/lib/RT/Plugin.pm
+++ b/rt/lib/RT/Plugin.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Pod/HTML.pm b/rt/lib/RT/Pod/HTML.pm
index 8ddce42..6896063 100644
--- a/rt/lib/RT/Pod/HTML.pm
+++ b/rt/lib/RT/Pod/HTML.pm
@@ -1,9 +1,59 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2013 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.
+#
+# 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 }}}
+
use strict;
use warnings;
package RT::Pod::HTML;
use base 'Pod::Simple::XHTML';
+use HTML::Entities qw//;
+
sub new {
my $self = shift->SUPER::new(@_);
$self->index(1);
@@ -11,6 +61,11 @@ sub new {
return $self;
}
+sub decode_entities {
+ my $self = shift;
+ return HTML::Entities::decode_entities($_[0]);
+}
+
sub perldoc_url_prefix { "http://metacpan.org/module/" }
sub html_header { '' }
@@ -20,8 +75,22 @@ sub html_footer {
return '<a href="./' . $toc . '">&larr; Back to index</a>';
}
-sub start_Verbatim { $_[0]{'scratch'} = "<pre>" }
-sub end_Verbatim { $_[0]{'scratch'} .= "</pre>"; $_[0]->emit; }
+sub start_F {
+ $_[0]{'scratch_F'} = $_[0]{'scratch'};
+ $_[0]{'scratch'} = "";
+}
+sub end_F {
+ my $self = shift;
+ my $text = $self->{scratch};
+ my $file = $self->decode_entities($text);
+
+ if (my $local = $self->resolve_local_link($file)) {
+ $text = qq[<a href="$local">$text</a>];
+ }
+
+ $self->{'scratch'} = delete $self->{scratch_F};
+ $self->{'scratch'} .= "<i>$text</i>";
+}
sub _end_head {
my $self = shift;
@@ -38,6 +107,17 @@ sub resolve_pod_page_link {
return $self->SUPER::resolve_pod_page_link(@_)
unless $self->batch_mode and $name;
+ my $local = $self->resolve_local_link($name, $section);
+
+ return $local
+ ? $local
+ : $self->SUPER::resolve_pod_page_link(@_);
+}
+
+sub resolve_local_link {
+ my $self = shift;
+ my ($name, $section) = @_;
+
$section = defined $section
? '#' . $self->idify($section, 1)
: '';
@@ -48,18 +128,28 @@ sub resolve_pod_page_link {
map { $self->encode_entities($_) }
split /::/, $name;
}
- elsif ($name =~ /^rt-/) {
+ elsif ($name =~ /^rt[-_]/) {
$local = $self->encode_entities($name);
}
+ elsif ($name eq "RT_Config" or $name eq "RT_Config.pm") {
+ $local = "RT_Config";
+ }
+ # These matches handle links that look like filenames, such as those we
+ # parse out of F<> tags.
+ elsif ( $name =~ m{^(?:lib/)(RT/[\w/]+?)\.pm$}
+ or $name =~ m{^(?:docs/)(.+?)\.pod$})
+ {
+ $local = join "/",
+ map { $self->encode_entities($_) }
+ split /\//, $1;
+ }
if ($local) {
# Resolve links correctly by going up
my $depth = $self->batch_mode_current_level - 1;
- return join "/",
- ($depth ? ".." x $depth : ()),
- "$local.html$section";
+ return ($depth ? "../" x $depth : "") . "$local.html$section";
} else {
- return $self->SUPER::resolve_pod_page_link(@_)
+ return;
}
}
diff --git a/rt/lib/RT/Pod/HTMLBatch.pm b/rt/lib/RT/Pod/HTMLBatch.pm
index 8d1b67f..f41a43a 100644
--- a/rt/lib/RT/Pod/HTMLBatch.pm
+++ b/rt/lib/RT/Pod/HTMLBatch.pm
@@ -1,3 +1,51 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2013 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.
+#
+# 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 }}}
+
use strict;
use warnings;
diff --git a/rt/lib/RT/Pod/Search.pm b/rt/lib/RT/Pod/Search.pm
index d6ddd2d..29e7d43 100644
--- a/rt/lib/RT/Pod/Search.pm
+++ b/rt/lib/RT/Pod/Search.pm
@@ -1,3 +1,51 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2013 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.
+#
+# 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 }}}
+
use strict;
use warnings;
diff --git a/rt/lib/RT/Principal.pm b/rt/lib/RT/Principal.pm
index 0ee03f1..175f1b0 100644
--- a/rt/lib/RT/Principal.pm
+++ b/rt/lib/RT/Principal.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Principals.pm b/rt/lib/RT/Principals.pm
index 69e49ef..9cf8cbb 100644
--- a/rt/lib/RT/Principals.pm
+++ b/rt/lib/RT/Principals.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm
index a942bb6..ee68b81 100755
--- a/rt/lib/RT/Queue.pm
+++ b/rt/lib/RT/Queue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -263,14 +263,10 @@ sub Lifecycle {
sub SetLifecycle {
my $self = shift;
- my $value = shift;
+ my $value = shift || 'default';
- if ( $value && $value ne 'default' ) {
- return (0, $self->loc('[_1] is not valid lifecycle', $value ))
- unless $self->ValidateLifecycle( $value );
- } else {
- $value = undef;
- }
+ return ( 0, $self->loc( '[_1] is not a valid lifecycle', $value ) )
+ unless $self->ValidateLifecycle($value);
return $self->_Set( Field => 'Lifecycle', Value => $value, @_ );
}
@@ -410,12 +406,10 @@ sub Create {
return ($val, $msg) unless $val;
}
- if ( $args{'Lifecycle'} && $args{'Lifecycle'} ne 'default' ) {
- return ( 0, $self->loc('Invalid lifecycle name') )
- unless $self->ValidateLifecycle( $args{'Lifecycle'} );
- } else {
- $args{'Lifecycle'} = undef;
- }
+ $args{'Lifecycle'} ||= 'default';
+
+ return ( 0, $self->loc('[_1] is not a valid lifecycle', $args{'Lifecycle'} ) )
+ unless $self->ValidateLifecycle( $args{'Lifecycle'} );
my %attrs = map {$_ => 1} $self->ReadableAttributes;
@@ -871,7 +865,7 @@ PrinicpalId The RT::Principal id of the user or group that's being added as a wa
Email The email address of the new watcher. If a user with this
email address can't be found, a new nonprivileged user will be created.
-If the watcher you\'re trying to set has an RT account, set the Owner parameter to their User Id. Otherwise, set the Email parameter to their Email address.
+If the watcher you're trying to set has an RT account, set the Owner parameter to their User Id. Otherwise, set the Email parameter to their Email address.
Returns a tuple of (status/id, message).
@@ -971,7 +965,8 @@ sub _AddWatcher {
if ( $group->HasMember( $principal)) {
- return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
+ return ( 0, $self->loc('[_1] is already a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
}
@@ -979,7 +974,8 @@ sub _AddWatcher {
unless ($m_id) {
$RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
- return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
+ return ( 0, $self->loc('Could not make [_1] a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
}
return ( 1, $self->loc("Added [_1] to members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
}
@@ -1051,8 +1047,8 @@ sub DeleteWatcher {
# see if this user is already a watcher.
unless ( $group->HasMember($principal)) {
- return ( 0,
- $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
+ return ( 0, $self->loc('[_1] is not a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
}
my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
@@ -1060,7 +1056,8 @@ sub DeleteWatcher {
$RT::Logger->error("Failed to delete ".$principal->Id.
" as a member of group ".$group->Id.": ".$m_msg);
- return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
+ return ( 0, $self->loc('Could not remove [_1] as a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
}
return ( 1, $self->loc("Removed [_1] from members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
@@ -1236,6 +1233,7 @@ sub _Set {
unless ( $self->CurrentUserHasRight('AdminQueue') ) {
return ( 0, $self->loc('Permission Denied') );
}
+ RT->System->QueueCacheNeedsUpdate(1);
return ( $self->SUPER::_Set(@_) );
}
@@ -1560,7 +1558,7 @@ sub _CoreAccessible {
SubjectTag =>
{read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
Lifecycle =>
- {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
+ {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => 'default'},
InitialPriority =>
{read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
FinalPriority =>
diff --git a/rt/lib/RT/Queues.pm b/rt/lib/RT/Queues.pm
index feb3491..45cb686 100755
--- a/rt/lib/RT/Queues.pm
+++ b/rt/lib/RT/Queues.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm
index 313888c..6601a0d 100755
--- a/rt/lib/RT/Record.pm
+++ b/rt/lib/RT/Record.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -501,19 +501,24 @@ sub _Set {
# $ret is a Class::ReturnValue object. as such, in a boolean context, it's a bool
# we want to change the standard "success" message
if ($status) {
- $msg =
- $self->loc(
- "[_1] changed from [_2] to [_3]",
- $self->loc( $args{'Field'} ),
- ( $old_val ? '"' . $old_val . '"' : $self->loc("(no value)") ),
- '"' . $self->__Value( $args{'Field'}) . '"'
- );
- } else {
-
- $msg = $self->CurrentUser->loc_fuzzy($msg);
+ if ($self->SQLType( $args{'Field'}) =~ /text/) {
+ $msg = $self->loc(
+ "[_1] updated",
+ $self->loc( $args{'Field'} ),
+ );
+ } else {
+ $msg = $self->loc(
+ "[_1] changed from [_2] to [_3]",
+ $self->loc( $args{'Field'} ),
+ ( $old_val ? '"' . $old_val . '"' : $self->loc("(no value)") ),
+ '"' . $self->__Value( $args{'Field'}) . '"',
+ );
+ }
+ } else {
+ $msg = $self->CurrentUser->loc_fuzzy($msg);
}
- return wantarray ? ($status, $msg) : $ret;
+ return wantarray ? ($status, $msg) : $ret;
}
@@ -888,6 +893,8 @@ sub Update {
$value =~ s/\r\n/\n/gs;
+ my $truncated_value = $self->TruncateValue($attribute, $value);
+
# If Queue is 'General', we want to resolve the queue name for
# the object.
@@ -902,8 +909,12 @@ sub Update {
my $name = $self->$object->Name;
next if $name eq $value || $name eq ($value || 0);
};
- next if $value eq $self->$attribute();
- next if ($value || 0) eq $self->$attribute();
+
+ my $current = $self->$attribute();
+ # RT::Queue->Lifecycle returns a Lifecycle object instead of name
+ $current = eval { $current->Name } if ref $current;
+ next if $truncated_value eq $current;
+ next if ( $truncated_value || 0 ) eq $current;
};
$new_values{$attribute} = $value;
@@ -1418,7 +1429,7 @@ sub _AddLink {
Delete a link. takes a paramhash of Base, Target and Type.
Either Base or Target must be null. The null value will
-be replaced with this ticket\'s id
+be replaced with this ticket's id
=cut
@@ -1633,29 +1644,37 @@ sub CustomFields {
$cfs->SetContextObject( $self );
# XXX handle multiple types properly
$cfs->LimitToLookupType( $self->CustomFieldLookupType );
- $cfs->LimitToGlobalOrObjectId(
- $self->_LookupId( $self->CustomFieldLookupType )
- );
+ $cfs->LimitToGlobalOrObjectId( $self->CustomFieldLookupId );
$cfs->ApplySortOrder;
return $cfs;
}
-# TODO: This _only_ works for RT::Class classes. it doesn't work, for example,
-# for RT::IR classes.
+# TODO: This _only_ works for RT::Foo classes. it doesn't work, for
+# example, for RT::IR::Foo classes.
-sub _LookupId {
+sub CustomFieldLookupId {
my $self = shift;
- my $lookup = shift;
+ my $lookup = shift || $self->CustomFieldLookupType;
my @classes = ($lookup =~ /RT::(\w+)-/g);
+ # Work on "RT::Queue", for instance
+ return $self->Id unless @classes;
+
my $object = $self;
+ # Save a ->Load call by not calling ->FooObj->Id, just ->Foo
+ my $final = shift @classes;
foreach my $class (reverse @classes) {
my $method = "${class}Obj";
$object = $object->$method;
}
- return $object->Id;
+ my $id = $object->$final;
+ unless (defined $id) {
+ my $method = "${final}Obj";
+ $id = $object->$method->Id;
+ }
+ return $id;
}
diff --git a/rt/lib/RT/Reminders.pm b/rt/lib/RT/Reminders.pm
index 2b66325..42f4e1d 100644
--- a/rt/lib/RT/Reminders.pm
+++ b/rt/lib/RT/Reminders.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -116,6 +116,16 @@ sub Add {
@_
);
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($self->Ticket);
+ if ( !$ticket->id ) {
+ return ( 0, $self->loc( "Failed to load ticket [_1]", $self->Ticket ) );
+ }
+
+ if ( $ticket->Status eq 'deleted' ) {
+ return ( 0, $self->loc("Can't link to a deleted ticket") );
+ }
+
my $reminder = RT::Ticket->new($self->CurrentUser);
my ( $status, $msg ) = $reminder->Create(
Subject => $args{'Subject'},
diff --git a/rt/lib/RT/Report/Tickets.pm b/rt/lib/RT/Report/Tickets.pm
index de40dbd..b73bbaa 100644
--- a/rt/lib/RT/Report/Tickets.pm
+++ b/rt/lib/RT/Report/Tickets.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -57,22 +57,27 @@ use warnings;
sub Groupings {
my $self = shift;
my %args = (@_);
- my @fields = map {$_, $_} qw(
- Status
- Queue
- );
-
- foreach my $type ( qw(Owner Creator LastUpdatedBy Requestor Cc AdminCc Watcher) ) {
- push @fields, $type.' '.$_, $type.'.'.$_ foreach qw(
- Name EmailAddress RealName NickName Organization Lang City Country Timezone
- );
+ my @fields =
+ map { $self->CurrentUser->loc($_), $_ } qw( Status Queue ); # loc_qw
+
+ foreach my $type ( qw(Owner Creator LastUpdatedBy Requestor Cc AdminCc Watcher) ) { # loc_qw
+ for my $field (
+ qw( Name EmailAddress RealName NickName Organization Lang City Country Timezone ) # loc_qw
+ )
+ {
+ push @fields,
+ $self->CurrentUser->loc($type) . ' '
+ . $self->CurrentUser->loc($field), $type . '.' . $field;
+ }
}
- for my $field (qw(Due Resolved Created LastUpdated Started Starts Told)) {
- for my $frequency (qw(Hourly Daily Monthly Annually)) {
- my $item = $field.$frequency;
- push @fields, $item, $item;
+ for my $field (qw(Due Resolved Created LastUpdated Started Starts Told)) { # loc_qw
+ for my $frequency (qw(Hourly Daily Monthly Annually)) { # loc_qw
+ push @fields,
+ $self->CurrentUser->loc($field)
+ . $self->CurrentUser->loc($frequency),
+ $field . $frequency;
}
}
@@ -93,7 +98,11 @@ sub Groupings {
}
$CustomFields->LimitToGlobal;
while ( my $CustomField = $CustomFields->Next ) {
- push @fields, "Custom field '". $CustomField->Name ."'", "CF.{". $CustomField->id ."}";
+ push @fields, $self->CurrentUser->loc(
+ "Custom field '[_1]'",
+ $CustomField->Name
+ ),
+ "CF.{" . $CustomField->id . "}";
}
}
return @fields;
diff --git a/rt/lib/RT/Report/Tickets/Entry.pm b/rt/lib/RT/Report/Tickets/Entry.pm
index 87754c4..eb38993 100644
--- a/rt/lib/RT/Report/Tickets/Entry.pm
+++ b/rt/lib/RT/Report/Tickets/Entry.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Rule.pm b/rt/lib/RT/Rule.pm
index b007a4e..c5c8b09 100644
--- a/rt/lib/RT/Rule.pm
+++ b/rt/lib/RT/Rule.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Ruleset.pm b/rt/lib/RT/Ruleset.pm
index de3983f..26227b7 100644
--- a/rt/lib/RT/Ruleset.pm
+++ b/rt/lib/RT/Ruleset.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/SQL.pm b/rt/lib/RT/SQL.pm
index 22def26..15715a7 100644
--- a/rt/lib/RT/SQL.pm
+++ b/rt/lib/RT/SQL.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -69,7 +69,7 @@ my @tokens = qw[VALUE AGGREGATOR OPERATOR OPEN_PAREN CLOSE_PAREN KEYWORD];
use Regexp::Common qw /delimited/;
my $re_aggreg = qr[(?i:AND|OR)];
my $re_delim = qr[$RE{delimited}{-delim=>qq{\'\"}}];
-my $re_value = qr[[+-]?\d+|NULL|$re_delim];
+my $re_value = qr[[+-]?\d+|(?i:NULL)|$re_delim];
my $re_keyword = qr[[{}\w\.]+|$re_delim];
my $re_op = qr[=|!=|>=|<=|>|<|(?i:IS NOT)|(?i:IS)|(?i:NOT LIKE)|(?i:LIKE)|(?i:NOT STARTSWITH)|(?i:STARTSWITH)|(?i:NOT ENDSWITH)|(?i:ENDSWITH)]; # long to short
my $re_open_paren = qr[\(];
diff --git a/rt/lib/RT/SavedSearch.pm b/rt/lib/RT/SavedSearch.pm
index f7695d6..7c4df8b 100644
--- a/rt/lib/RT/SavedSearch.pm
+++ b/rt/lib/RT/SavedSearch.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -77,7 +77,7 @@ An object of this class is called "search"
=cut
-sub ObjectName { "search" }
+sub ObjectName { "search" } # loc
sub PostLoad {
my $self = shift;
@@ -115,6 +115,28 @@ sub UpdateAttribute {
return ($status, $msg);
}
+=head2 RT::SavedSearch->EscapeDescription STRING
+
+This is a class method because system-level saved searches aren't true
+C<RT::SavedSearch> objects but direct C<RT::Attribute> objects.
+
+Returns C<STRING> with all square brackets except those in C<[_1]> escaped,
+ready for passing as the first argument to C<loc()>.
+
+=cut
+
+sub EscapeDescription {
+ my $self = shift;
+ my $desc = shift;
+ if ($desc) {
+ # We only use [_1] in saved search descriptions, so let's escape other "["
+ # and "]" unless they are escaped already.
+ $desc =~ s/(?<!~)\[(?!_1\])/~[/g;
+ $desc =~ s/(?<!~)(?<!\[_1)\]/~]/g;
+ }
+ return $desc;
+}
+
=head2 Type
Returns the type of this search, e.g. 'Ticket'. Useful for denoting the
diff --git a/rt/lib/RT/SavedSearches.pm b/rt/lib/RT/SavedSearches.pm
index 15c90dc..af8f482 100644
--- a/rt/lib/RT/SavedSearches.pm
+++ b/rt/lib/RT/SavedSearches.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -70,6 +70,7 @@ package RT::SavedSearches;
use RT::SavedSearch;
use strict;
+use warnings;
use base 'RT::SharedSettings';
sub RecordClass {
diff --git a/rt/lib/RT/Scrip.pm b/rt/lib/RT/Scrip.pm
index 8f97e74..5fa7165 100755
--- a/rt/lib/RT/Scrip.pm
+++ b/rt/lib/RT/Scrip.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -233,7 +233,7 @@ sub QueueObj {
=head2 ActionObj
-Retuns an RT::Action object with this Scrip\'s Action
+Retuns an RT::Action object with this Scrip's Action
=cut
@@ -285,7 +285,7 @@ sub LoadModules {
=head2 TemplateObj
-Retuns an RT::Template object with this Scrip\'s Template
+Retuns an RT::Template object with this Scrip's Template
=cut
@@ -362,7 +362,7 @@ sub Apply {
=head2 IsApplicable
-Calls the Condition object\'s IsApplicable method
+Calls the Condition object's IsApplicable method
Upon success, returns the applicable Transaction object.
Otherwise, undef is returned.
@@ -633,7 +633,7 @@ sub CompileCheck {
do {
no strict 'vars';
- eval "sub { $code }";
+ eval "sub { $code \n }";
};
next if !$@;
diff --git a/rt/lib/RT/ScripAction.pm b/rt/lib/RT/ScripAction.pm
index 13ab47e..44f9bd8 100755
--- a/rt/lib/RT/ScripAction.pm
+++ b/rt/lib/RT/ScripAction.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ScripActions.pm b/rt/lib/RT/ScripActions.pm
index 322f7fc..a3a1622 100755
--- a/rt/lib/RT/ScripActions.pm
+++ b/rt/lib/RT/ScripActions.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/ScripCondition.pm b/rt/lib/RT/ScripCondition.pm
index 4156b69..e7e4652 100755
--- a/rt/lib/RT/ScripCondition.pm
+++ b/rt/lib/RT/ScripCondition.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -185,7 +185,7 @@ sub LoadCondition {
=head2 Describe
-Helper method to call the condition module\'s Describe method.
+Helper method to call the condition module's Describe method.
=cut
@@ -198,7 +198,7 @@ sub Describe {
=head2 IsApplicable
-Helper method to call the condition module\'s IsApplicable method.
+Helper method to call the condition module's IsApplicable method.
=cut
diff --git a/rt/lib/RT/ScripConditions.pm b/rt/lib/RT/ScripConditions.pm
index 145d94d..6668497 100755
--- a/rt/lib/RT/ScripConditions.pm
+++ b/rt/lib/RT/ScripConditions.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm
index fa33f7e..af8323e 100755
--- a/rt/lib/RT/Scrips.pm
+++ b/rt/lib/RT/Scrips.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Search.pm b/rt/lib/RT/Search.pm
index 7bf2f4a..7ec50de 100755
--- a/rt/lib/RT/Search.pm
+++ b/rt/lib/RT/Search.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Search/ActiveTicketsInQueue.pm b/rt/lib/RT/Search/ActiveTicketsInQueue.pm
index 8a97118..da1cdb6 100644
--- a/rt/lib/RT/Search/ActiveTicketsInQueue.pm
+++ b/rt/lib/RT/Search/ActiveTicketsInQueue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -66,6 +66,7 @@ Find all active tickets in the queue named in the argument passed in
package RT::Search::ActiveTicketsInQueue;
use strict;
+use warnings;
use base qw(RT::Search);
diff --git a/rt/lib/RT/Search/FromSQL.pm b/rt/lib/RT/Search/FromSQL.pm
index fe0d874..4cb17f5 100644
--- a/rt/lib/RT/Search/FromSQL.pm
+++ b/rt/lib/RT/Search/FromSQL.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -66,6 +66,7 @@ Find all tickets described by the SQL statement passed as an argument
package RT::Search::FromSQL;
use strict;
+use warnings;
use base qw(RT::Search);
=head2 Describe
diff --git a/rt/lib/RT/Search/Googleish.pm b/rt/lib/RT/Search/Googleish.pm
index 1b4071f..f8465f0 100644
--- a/rt/lib/RT/Search/Googleish.pm
+++ b/rt/lib/RT/Search/Googleish.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -203,7 +203,7 @@ our @GUESS = (
[ 40 => sub { return "status" if /^((in)?active|any)$/i } ],
[ 50 => sub {
my $q = RT::Queue->new( $_[2] );
- return "queue" if $q->Load($_) and $q->Id
+ return "queue" if $q->Load($_) and $q->Id and not $q->Disabled
}],
[ 60 => sub {
my $u = RT::User->new( $_[2] );
diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm
index 4278f75..adc8a98 100644
--- a/rt/lib/RT/SearchBuilder.pm
+++ b/rt/lib/RT/SearchBuilder.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -279,7 +279,7 @@ sub Limit {
|(NOT\s*)?(STARTS|ENDS)WITH
|(NOT\s*)?MATCHES
|IS(\s*NOT)?
- |IN
+ |(NOT\s*)?IN
|\@\@)$/ix) {
$RT::Logger->crit("Possible SQL injection attack: $ARGS{FIELD} $ARGS{OPERATOR}");
$self->SUPER::Limit(
diff --git a/rt/lib/RT/SharedSetting.pm b/rt/lib/RT/SharedSetting.pm
index 833308c..3467167 100644
--- a/rt/lib/RT/SharedSetting.pm
+++ b/rt/lib/RT/SharedSetting.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -210,11 +210,11 @@ sub Save {
$self->{'Attribute'} = $object->Attributes->WithId($att_id);
$self->{'Id'} = $att_id;
$self->{'Privacy'} = $privacy;
- return ( 1, $self->loc( "Saved [_1] [_2]", $self->ObjectName, $name ) );
+ return ( 1, $self->loc( "Saved [_1] [_2]", $self->loc( $self->ObjectName ), $name ) );
}
else {
$RT::Logger->error($self->ObjectName . " save failure: $att_msg");
- return ( 0, $self->loc("Failed to create [_1] attribute", $self->ObjectName) );
+ return ( 0, $self->loc("Failed to create [_1] attribute", $self->loc( $self->ObjectName ) ) );
}
}
diff --git a/rt/lib/RT/SharedSettings.pm b/rt/lib/RT/SharedSettings.pm
index c2c9abe..6e7ec3b 100644
--- a/rt/lib/RT/SharedSettings.pm
+++ b/rt/lib/RT/SharedSettings.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -70,6 +70,7 @@ package RT::SharedSettings;
use RT::SharedSetting;
use strict;
+use warnings;
use base 'RT::Base';
sub new {
diff --git a/rt/lib/RT/Shredder.pm b/rt/lib/RT/Shredder.pm
index 4f96e16..bebd599 100644
--- a/rt/lib/RT/Shredder.pm
+++ b/rt/lib/RT/Shredder.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -720,7 +720,7 @@ sub GetFileName
=head4 StoragePath
Returns an absolute path to the storage dir. See
-L<CONFIGURATION/$ShredderStoragePath>.
+L</$ShredderStoragePath>.
See also description of the L</GetFileName> method.
diff --git a/rt/lib/RT/Shredder/ACE.pm b/rt/lib/RT/Shredder/ACE.pm
index e2fa750..7a50d9a 100644
--- a/rt/lib/RT/Shredder/ACE.pm
+++ b/rt/lib/RT/Shredder/ACE.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Attachment.pm b/rt/lib/RT/Shredder/Attachment.pm
index aa59bf6..9cd4088 100644
--- a/rt/lib/RT/Shredder/Attachment.pm
+++ b/rt/lib/RT/Shredder/Attachment.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/CachedGroupMember.pm b/rt/lib/RT/Shredder/CachedGroupMember.pm
index f5542a2..9f1668f 100644
--- a/rt/lib/RT/Shredder/CachedGroupMember.pm
+++ b/rt/lib/RT/Shredder/CachedGroupMember.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Constants.pm b/rt/lib/RT/Shredder/Constants.pm
index ba160ce..b09b52f 100644
--- a/rt/lib/RT/Shredder/Constants.pm
+++ b/rt/lib/RT/Shredder/Constants.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/CustomField.pm b/rt/lib/RT/Shredder/CustomField.pm
index 43b759e..8c7dc22 100644
--- a/rt/lib/RT/Shredder/CustomField.pm
+++ b/rt/lib/RT/Shredder/CustomField.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/CustomFieldValue.pm b/rt/lib/RT/Shredder/CustomFieldValue.pm
index 769336c..9a9d369 100644
--- a/rt/lib/RT/Shredder/CustomFieldValue.pm
+++ b/rt/lib/RT/Shredder/CustomFieldValue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Dependencies.pm b/rt/lib/RT/Shredder/Dependencies.pm
index 4b03cca..9364887 100644
--- a/rt/lib/RT/Shredder/Dependencies.pm
+++ b/rt/lib/RT/Shredder/Dependencies.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Shredder::Dependencies;
use strict;
+use warnings;
use RT::Shredder::Exceptions;
use RT::Shredder::Constants;
use RT::Shredder::Dependency;
diff --git a/rt/lib/RT/Shredder/Dependency.pm b/rt/lib/RT/Shredder/Dependency.pm
index 7300bee..2800771 100644
--- a/rt/lib/RT/Shredder/Dependency.pm
+++ b/rt/lib/RT/Shredder/Dependency.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::Shredder::Dependency;
use strict;
+use warnings;
use RT::Shredder::Constants;
use RT::Shredder::Exceptions;
diff --git a/rt/lib/RT/Shredder/Exceptions.pm b/rt/lib/RT/Shredder/Exceptions.pm
index b6a44b6..8c1d6ed 100644
--- a/rt/lib/RT/Shredder/Exceptions.pm
+++ b/rt/lib/RT/Shredder/Exceptions.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Group.pm b/rt/lib/RT/Shredder/Group.pm
index 0736793..bbf84ab 100644
--- a/rt/lib/RT/Shredder/Group.pm
+++ b/rt/lib/RT/Shredder/Group.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/GroupMember.pm b/rt/lib/RT/Shredder/GroupMember.pm
index 5f06fae..a7e0b42 100644
--- a/rt/lib/RT/Shredder/GroupMember.pm
+++ b/rt/lib/RT/Shredder/GroupMember.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Link.pm b/rt/lib/RT/Shredder/Link.pm
index 94beb86..a442910 100644
--- a/rt/lib/RT/Shredder/Link.pm
+++ b/rt/lib/RT/Shredder/Link.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm b/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm
index 6bd236c..7612f4a 100644
--- a/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm
+++ b/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/POD.pm b/rt/lib/RT/Shredder/POD.pm
index 8dc320e..6cc8695 100644
--- a/rt/lib/RT/Shredder/POD.pm
+++ b/rt/lib/RT/Shredder/POD.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin.pm b/rt/lib/RT/Shredder/Plugin.pm
index ad9af6a..60ba333 100644
--- a/rt/lib/RT/Shredder/Plugin.pm
+++ b/rt/lib/RT/Shredder/Plugin.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Attachments.pm b/rt/lib/RT/Shredder/Plugin/Attachments.pm
index 0eaeeaf..f0f64a1 100644
--- a/rt/lib/RT/Shredder/Plugin/Attachments.pm
+++ b/rt/lib/RT/Shredder/Plugin/Attachments.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Base.pm b/rt/lib/RT/Shredder/Plugin/Base.pm
index d9610d4..0adadfd 100644
--- a/rt/lib/RT/Shredder/Plugin/Base.pm
+++ b/rt/lib/RT/Shredder/Plugin/Base.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Base/Dump.pm b/rt/lib/RT/Shredder/Plugin/Base/Dump.pm
index d4705b7..903a962 100644
--- a/rt/lib/RT/Shredder/Plugin/Base/Dump.pm
+++ b/rt/lib/RT/Shredder/Plugin/Base/Dump.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Base/Search.pm b/rt/lib/RT/Shredder/Plugin/Base/Search.pm
index eb28ba6..a493cd8 100644
--- a/rt/lib/RT/Shredder/Plugin/Base/Search.pm
+++ b/rt/lib/RT/Shredder/Plugin/Base/Search.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Objects.pm b/rt/lib/RT/Shredder/Plugin/Objects.pm
index d5a7abb..2090574 100644
--- a/rt/lib/RT/Shredder/Plugin/Objects.pm
+++ b/rt/lib/RT/Shredder/Plugin/Objects.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/SQLDump.pm b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
index 91c2179..2e7c259 100644
--- a/rt/lib/RT/Shredder/Plugin/SQLDump.pm
+++ b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Summary.pm b/rt/lib/RT/Shredder/Plugin/Summary.pm
index aa21242..9b533bc 100644
--- a/rt/lib/RT/Shredder/Plugin/Summary.pm
+++ b/rt/lib/RT/Shredder/Plugin/Summary.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Tickets.pm b/rt/lib/RT/Shredder/Plugin/Tickets.pm
index 67ee4a6..0344487 100644
--- a/rt/lib/RT/Shredder/Plugin/Tickets.pm
+++ b/rt/lib/RT/Shredder/Plugin/Tickets.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Plugin/Users.pm b/rt/lib/RT/Shredder/Plugin/Users.pm
index 1ff2c50..244a262 100644
--- a/rt/lib/RT/Shredder/Plugin/Users.pm
+++ b/rt/lib/RT/Shredder/Plugin/Users.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Principal.pm b/rt/lib/RT/Shredder/Principal.pm
index 4444164..5dc04b3 100644
--- a/rt/lib/RT/Shredder/Principal.pm
+++ b/rt/lib/RT/Shredder/Principal.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Queue.pm b/rt/lib/RT/Shredder/Queue.pm
index 2c0d068..80a1c84 100644
--- a/rt/lib/RT/Shredder/Queue.pm
+++ b/rt/lib/RT/Shredder/Queue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Record.pm b/rt/lib/RT/Shredder/Record.pm
index d1c74bf..d70bf12 100644
--- a/rt/lib/RT/Shredder/Record.pm
+++ b/rt/lib/RT/Shredder/Record.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Scrip.pm b/rt/lib/RT/Shredder/Scrip.pm
index 0af7a03..74878b6 100644
--- a/rt/lib/RT/Shredder/Scrip.pm
+++ b/rt/lib/RT/Shredder/Scrip.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/ScripAction.pm b/rt/lib/RT/Shredder/ScripAction.pm
index 62f01f8..cdad7e4 100644
--- a/rt/lib/RT/Shredder/ScripAction.pm
+++ b/rt/lib/RT/Shredder/ScripAction.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/ScripCondition.pm b/rt/lib/RT/Shredder/ScripCondition.pm
index b48862a..857f062 100644
--- a/rt/lib/RT/Shredder/ScripCondition.pm
+++ b/rt/lib/RT/Shredder/ScripCondition.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Template.pm b/rt/lib/RT/Shredder/Template.pm
index 40a03c9..4ac6daf 100644
--- a/rt/lib/RT/Shredder/Template.pm
+++ b/rt/lib/RT/Shredder/Template.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Ticket.pm b/rt/lib/RT/Shredder/Ticket.pm
index 312b8fd..2e54536 100644
--- a/rt/lib/RT/Shredder/Ticket.pm
+++ b/rt/lib/RT/Shredder/Ticket.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/Transaction.pm b/rt/lib/RT/Shredder/Transaction.pm
index 4b23ce6..37e6ba5 100644
--- a/rt/lib/RT/Shredder/Transaction.pm
+++ b/rt/lib/RT/Shredder/Transaction.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Shredder/User.pm b/rt/lib/RT/Shredder/User.pm
index 1c46203..6e19da7 100644
--- a/rt/lib/RT/Shredder/User.pm
+++ b/rt/lib/RT/Shredder/User.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Squish.pm b/rt/lib/RT/Squish.pm
index eb31a63..e64b711 100644
--- a/rt/lib/RT/Squish.pm
+++ b/rt/lib/RT/Squish.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Squish/CSS.pm b/rt/lib/RT/Squish/CSS.pm
index 9914514..1e2a453 100644
--- a/rt/lib/RT/Squish/CSS.pm
+++ b/rt/lib/RT/Squish/CSS.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Squish/JS.pm b/rt/lib/RT/Squish/JS.pm
index 6309d01..4dd24b7 100644
--- a/rt/lib/RT/Squish/JS.pm
+++ b/rt/lib/RT/Squish/JS.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/System.pm b/rt/lib/RT/System.pm
index d9aaf1c..cf3d2d0 100644
--- a/rt/lib/RT/System.pm
+++ b/rt/lib/RT/System.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Template.pm b/rt/lib/RT/Template.pm
index e509454..fd4b511 100755
--- a/rt/lib/RT/Template.pm
+++ b/rt/lib/RT/Template.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -335,7 +335,7 @@ sub Parse {
my ($rv, $msg);
- if ($self->Content =~ m{^Content-Type:\s+text/html\b}im) {
+ if (not $self->IsEmpty and $self->Content =~ m{^Content-Type:\s+text/html\b}im) {
local $RT::Transaction::PreferredContentType = 'text/html';
($rv, $msg) = $self->_Parse(@_);
}
@@ -458,7 +458,7 @@ sub _ParseContentPerl {
foreach my $key ( keys %{ $args{TemplateArgs} } ) {
my $val = $args{TemplateArgs}{ $key };
next unless ref $val;
- next if ref $val =~ /^(ARRAY|HASH|SCALAR|CODE)$/;
+ next if ref($val) =~ /^(ARRAY|HASH|SCALAR|CODE)$/;
$args{TemplateArgs}{ $key } = \$val;
}
diff --git a/rt/lib/RT/Templates.pm b/rt/lib/RT/Templates.pm
index be571f0..b4da636 100755
--- a/rt/lib/RT/Templates.pm
+++ b/rt/lib/RT/Templates.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Test.pm b/rt/lib/RT/Test.pm
index 3e7c910..55fd88a 100644
--- a/rt/lib/RT/Test.pm
+++ b/rt/lib/RT/Test.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -54,12 +54,20 @@ use warnings;
use base 'Test::More';
+# We use the Test::NoWarnings catching and reporting functionality, but need to
+# wrap it in our own special handler because of the warn handler installed via
+# RT->InitLogging().
+require Test::NoWarnings;
+
+my $Test_NoWarnings_Catcher = $SIG{__WARN__};
+my $check_warnings_in_end = 1;
+
use Socket;
use File::Temp qw(tempfile);
use File::Path qw(mkpath);
use File::Spec;
-our @EXPORT = qw(is_empty diag parse_mail works fails);
+our @EXPORT = qw(is_empty diag parse_mail works fails plan done_testing);
my %tmp = (
directory => undef,
@@ -94,20 +102,27 @@ problem in Perl that hides the top-level optree from L<Devel::Cover>.
our $port;
our @SERVERS;
+BEGIN {
+ delete $ENV{$_} for qw/LANGUAGE LC_ALL LC_MESSAGES LANG/;
+ $ENV{LANG} = "C";
+};
+
sub import {
my $class = shift;
my %args = %rttest_opt = @_;
+ $rttest_opt{'nodb'} = $args{'nodb'} = 1 if $^C;
+
# Spit out a plan (if we got one) *before* we load modules
if ( $args{'tests'} ) {
- $class->builder->plan( tests => $args{'tests'} )
+ plan( tests => $args{'tests'} )
unless $args{'tests'} eq 'no_declare';
}
elsif ( exists $args{'tests'} ) {
# do nothing if they say "tests => undef" - let them make the plan
}
elsif ( $args{'skip_all'} ) {
- $class->builder->plan(skip_all => $args{'skip_all'});
+ plan(skip_all => $args{'skip_all'});
}
else {
$class->builder->no_plan unless $class->builder->has_plan;
@@ -139,7 +154,7 @@ sub import {
__reconnect_rt()
unless $args{nodb};
- RT::InitLogging();
+ __init_logging();
RT->Plugins;
@@ -168,12 +183,15 @@ sub import {
}
Test::More->export_to_level($level);
+ Test::NoWarnings->export_to_level($level);
- # blow away their diag so we can redefine it without warning
+ # Blow away symbols we redefine to avoid warnings.
# better than "no warnings 'redefine'" because we might accidentally
# suppress a mistaken redefinition
no strict 'refs';
delete ${ caller($level) . '::' }{diag};
+ delete ${ caller($level) . '::' }{plan};
+ delete ${ caller($level) . '::' }{done_testing};
__PACKAGE__->export_to_level($level);
}
@@ -280,9 +298,15 @@ Set( \$RTAddressRegexp , qr/^bad_re_that_doesnt_match\$/i);
print $config "Set( \$DatabaseName , '$dbname');\n";
print $config "Set( \$DatabaseUser , 'u${dbname}');\n";
}
+ if ( $ENV{'RT_TEST_DB_HOST'} ) {
+ print $config "Set( \$DatabaseHost , '$ENV{'RT_TEST_DB_HOST'}');\n";
+ }
if ( $args{'plugins'} ) {
print $config "Set( \@Plugins, qw(". join( ' ', @{ $args{'plugins'} } ) .") );\n";
+
+ my $plugin_data = File::Spec->rel2abs("t/data/plugins");
+ print $config qq[\$RT::PluginPath = "$plugin_data";\n];
}
if ( $INC{'Devel/Cover.pm'} ) {
@@ -421,7 +445,7 @@ sub bootstrap_db {
$RT::Handle->InsertSchema;
$RT::Handle->InsertACL unless $db_type eq 'Oracle';
- RT->InitLogging;
+ __init_logging();
__reconnect_rt();
$RT::Handle->InsertInitialData
@@ -603,6 +627,28 @@ sub __disconnect_rt {
if DBIx::SearchBuilder::Record::Cachable->can("FlushCache");
}
+sub __init_logging {
+ my $filter;
+ {
+ # We use local to ensure that the $filter we grab is from InitLogging
+ # and not the handler generated by a previous call to this function
+ # itself.
+ local $SIG{__WARN__};
+ RT::InitLogging();
+ $filter = $SIG{__WARN__};
+ }
+ $SIG{__WARN__} = sub {
+ if ($filter) {
+ my $status = $filter->(@_);
+ if ($status and $status eq 'IGNORE') {
+ return; # pretend the bad dream never happened
+ }
+ }
+ # Avoid reporting this anonymous call frame as the source of the warning.
+ goto &$Test_NoWarnings_Catcher;
+ };
+}
+
=head1 UTILITIES
@@ -1079,17 +1125,28 @@ sub clean_caught_mails {
Takes a path relative to the location of the test file that is being
run and returns a path that takes the invocation path into account.
-e.g. RT::Test::get_relocatable_dir(File::Spec->updir(), 'data', 'emails')
+e.g. C<RT::Test::get_relocatable_dir(File::Spec->updir(), 'data', 'emails')>
+
+Parent directory traversals (C<..> or File::Spec->updir()) are naively
+canonicalized based on the test file path (C<$0>) so that symlinks aren't
+followed. This is the exact opposite behaviour of most filesystems and is
+considered "wrong", however it is necessary for some subsets of tests which are
+symlinked into the testing tree.
=cut
sub get_relocatable_dir {
- (my $volume, my $directories, my $file) = File::Spec->splitpath($0);
- if (File::Spec->file_name_is_absolute($directories)) {
- return File::Spec->catdir($directories, @_);
- } else {
- return File::Spec->catdir(File::Spec->curdir(), $directories, @_);
+ my @directories = File::Spec->splitdir(
+ File::Spec->rel2abs((File::Spec->splitpath($0))[1])
+ );
+ push @directories, File::Spec->splitdir($_) for @_;
+
+ my @clean;
+ for (@directories) {
+ if ($_ eq "..") { pop @clean }
+ elsif ($_ ne ".") { push @clean, $_ }
}
+ return File::Spec->catdir(@clean);
}
=head2 get_relocatable_file
@@ -1413,6 +1470,8 @@ sub start_inline_server {
# Clear out squished CSS and JS cache, since it's retained across
# servers, since it's in-process
RT::Interface::Web->ClearSquished;
+ require RT::Interface::Web::Request;
+ RT::Interface::Web::Request->clear_callback_cache;
Test::More::ok(1, "psgi test server ok");
$TEST_APP = $self->test_app(@_);
@@ -1519,15 +1578,38 @@ sub fails {
Test::More::ok(!$_[0], $_[1] || 'This should fail');
}
+sub plan {
+ my ($cmd, @args) = @_;
+ my $builder = RT::Test->builder;
+
+ if ($cmd eq "skip_all") {
+ $check_warnings_in_end = 0;
+ } elsif ($cmd eq "tests") {
+ # Increment the test count for the warnings check
+ $args[0]++;
+ }
+ $builder->plan($cmd, @args);
+}
+
+sub done_testing {
+ my $builder = RT::Test->builder;
+
+ Test::NoWarnings::had_no_warnings();
+ $check_warnings_in_end = 0;
+
+ $builder->done_testing(@_);
+}
+
END {
my $Test = RT::Test->builder;
return if $Test->{Original_Pid} != $$;
-
# we are in END block and should protect our exit code
# so calls below may call system or kill that clobbers $?
local $?;
+ Test::NoWarnings::had_no_warnings() if $check_warnings_in_end;
+
RT::Test->stop_server(1);
# not success
diff --git a/rt/lib/RT/Test/Apache.pm b/rt/lib/RT/Test/Apache.pm
index b2733ea..256945a 100644
--- a/rt/lib/RT/Test/Apache.pm
+++ b/rt/lib/RT/Test/Apache.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Test/Email.pm b/rt/lib/RT/Test/Email.pm
index a8170c9..8cf6839 100644
--- a/rt/lib/RT/Test/Email.pm
+++ b/rt/lib/RT/Test/Email.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Test/GnuPG.pm b/rt/lib/RT/Test/GnuPG.pm
index 6cebb77..ec44672 100644
--- a/rt/lib/RT/Test/GnuPG.pm
+++ b/rt/lib/RT/Test/GnuPG.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -48,6 +48,7 @@
package RT::Test::GnuPG;
use strict;
+use warnings;
use Test::More;
use base qw(RT::Test);
use File::Temp qw(tempdir);
@@ -68,8 +69,10 @@ sub import {
$t->plan( skip_all => 'gpg executable is required.' )
unless RT::Test->find_executable('gpg');
- require RT::Crypt::GnuPG;
$class->SUPER::import(%args);
+ require RT::Crypt::GnuPG;
+ return $class->export_to_level(1)
+ if $^C;
RT::Test::diag "GnuPG --homedir " . RT->Config->Get('GnuPGOptions')->{'homedir'};
diff --git a/rt/lib/RT/Test/Web.pm b/rt/lib/RT/Test/Web.pm
index c2d9ac3..8611102 100644
--- a/rt/lib/RT/Test/Web.pm
+++ b/rt/lib/RT/Test/Web.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
index 5f76e05..4da1d48 100755
--- a/rt/lib/RT/Ticket.pm
+++ b/rt/lib/RT/Ticket.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -54,7 +54,7 @@
=head1 DESCRIPTION
-This module lets you manipulate RT\'s ticket object.
+This module lets you manipulate RT's ticket object.
=head1 METHODS
@@ -197,8 +197,8 @@ Arguments: ARGS is a hash of named parameters. Valid parameters are:
AdminCc - A reference to a list of email addresses or Names
SquelchMailTo - A reference to a list of email addresses -
who should this ticket not mail
- Type -- The ticket\'s type. ignore this for now
- Owner -- This ticket\'s owner. either an RT::User object or this user\'s id
+ Type -- The ticket's type. ignore this for now
+ Owner -- This ticket's owner. either an RT::User object or this user's id
Subject -- A string describing the subject of the ticket
Priority -- an integer from 0 to 99
InitialPriority -- an integer from 0 to 99
@@ -207,8 +207,8 @@ Arguments: ARGS is a hash of named parameters. Valid parameters are:
TimeEstimated -- an integer. estimated time for this task in minutes
TimeWorked -- an integer. time worked so far in minutes
TimeLeft -- an integer. time remaining in minutes
- Starts -- an ISO date describing the ticket\'s start date and time in GMT
- Due -- an ISO date describing the ticket\'s due date and time in GMT
+ Starts -- an ISO date describing the ticket's start date and time in GMT
+ Due -- an ISO date describing the ticket's due date and time in GMT
MIMEObj -- a MIME::Entity object with the content of the initial ticket request.
CustomField-<n> -- a scalar or array of values for the customfield with the id <n>
@@ -299,6 +299,7 @@ sub Create {
$args{'Status'} = $cycle->DefaultOnCreate;
}
+ $args{'Status'} = lc $args{'Status'};
unless ( $cycle->IsValid( $args{'Status'} ) ) {
return ( 0, 0,
$self->loc("Status '[_1]' isn't a valid status for tickets in this queue.",
@@ -460,6 +461,11 @@ sub Create {
}
}
+ $args{'Type'} = lc $args{'Type'}
+ if $args{'Type'} =~ /^(ticket|approval|reminder)$/i;
+
+ $args{'Subject'} =~ s/\n//g;
+
$RT::Handle->BeginTransaction();
my %params = (
@@ -783,6 +789,15 @@ sub Create {
}
}
+sub SetType {
+ my $self = shift;
+ my $value = shift;
+
+ # Force lowercase on internal RT types
+ $value = lc $value
+ if $value =~ /^(ticket|approval|reminder)$/i;
+ return $self->_Set(Field => 'Type', Value => $value, @_);
+}
@@ -850,8 +865,8 @@ sub _Parse822HeadersForAttributes {
=head2 Import PARAMHASH
Import a ticket.
-Doesn\'t create a transaction.
-Doesn\'t supply queue defaults, etc.
+Doesn't create a transaction.
+Doesn't supply queue defaults, etc.
Returns: TICKETID
@@ -885,7 +900,7 @@ sub Import {
$QueueObj = RT::Queue->new(RT->SystemUser);
$QueueObj->Load( $args{'Queue'} );
- #TODO error check this and return 0 if it\'s not loading properly +++
+ #TODO error check this and return 0 if it's not loading properly +++
}
elsif ( ref( $args{'Queue'} ) eq 'RT::Queue' ) {
$QueueObj = RT::Queue->new(RT->SystemUser);
@@ -1103,7 +1118,7 @@ PrincipalId The RT::Principal id of the user or group that's being added as a wa
Email The email address of the new watcher. If a user with this
email address can't be found, a new nonprivileged user will be created.
-If the watcher you\'re trying to set has an RT account, set the PrincipalId paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
+If the watcher you're trying to set has an RT account, set the PrincipalId paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
=cut
@@ -1206,7 +1221,8 @@ sub _AddWatcher {
if ( $group->HasMember( $principal)) {
- return ( 0, $self->loc('That principal is already a [_1] for this ticket', $self->loc($args{'Type'})) );
+ return ( 0, $self->loc('[_1] is already a [_2] for this ticket',
+ $principal->Object->Name, $self->loc($args{'Type'})) );
}
@@ -1215,7 +1231,8 @@ sub _AddWatcher {
unless ($m_id) {
$RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
- return ( 0, $self->loc('Could not make that principal a [_1] for this ticket', $self->loc($args{'Type'})) );
+ return ( 0, $self->loc('Could not make [_1] a [_2] for this ticket',
+ $principal->Object->Name, $self->loc($args{'Type'})) );
}
unless ( $args{'Silent'} ) {
@@ -1226,7 +1243,8 @@ sub _AddWatcher {
);
}
- return ( 1, $self->loc('Added principal as a [_1] for this ticket', $self->loc($args{'Type'})) );
+ return ( 1, $self->loc('Added [_1] as a [_2] for this ticket',
+ $principal->Object->Name, $self->loc($args{'Type'})) );
}
@@ -1325,8 +1343,8 @@ sub DeleteWatcher {
unless ( $group->HasMember($principal) ) {
return ( 0,
- $self->loc( 'That principal is not a [_1] for this ticket',
- $args{'Type'} ) );
+ $self->loc( '[_1] is not a [_2] for this ticket',
+ $principal->Object->Name, $args{'Type'} ) );
}
my ( $m_id, $m_msg ) = $group->_DeleteMember( $principal->Id );
@@ -1339,8 +1357,8 @@ sub DeleteWatcher {
return (0,
$self->loc(
- 'Could not remove that principal as a [_1] for this ticket',
- $args{'Type'} ) );
+ 'Could not remove [_1] as a [_2] for this ticket',
+ $principal->Object->Name, $args{'Type'} ) );
}
unless ( $args{'Silent'} ) {
@@ -1421,7 +1439,7 @@ sub UnsquelchMailTo {
=head2 RequestorAddresses
- B<Returns> String: All Ticket Requestor email addresses as a string.
+B<Returns> String: All Ticket Requestor email addresses as a string.
=cut
@@ -1794,7 +1812,7 @@ sub SetQueue {
unless ( $old_lifecycle->HasMoveMap( $new_lifecycle ) ) {
return ( 0, $self->loc("There is no mapping for statuses between these queues. Contact your system administrator.") );
}
- $new_status = $old_lifecycle->MoveMap( $new_lifecycle )->{ $self->Status };
+ $new_status = $old_lifecycle->MoveMap( $new_lifecycle )->{ lc $self->Status };
return ( 0, $self->loc("Mapping between queues' lifecycles is incomplete. Contact your system administrator.") )
unless $new_status;
}
@@ -1891,6 +1909,13 @@ sub QueueObj {
return ($self->{_queue_obj});
}
+sub SetSubject {
+ my $self = shift;
+ my $value = shift;
+ $value =~ s/\n//g;
+ return $self->_Set( Field => 'Subject', Value => $value );
+}
+
=head2 SubjectTag
Takes nothing. Returns SubjectTag for this ticket. Includes
@@ -2256,6 +2281,11 @@ sub Correspond {
my @results = $self->_RecordNote(%args);
+ unless ( $results[0] ) {
+ $RT::Handle->Rollback();
+ return @results;
+ }
+
#Set the last told date to now if this isn't mail from the requestor.
#TODO: Note that this will wrongly ack mail from any non-requestor as a "told"
unless ( $self->IsRequestor($self->CurrentUser->id) ) {
@@ -2312,6 +2342,9 @@ sub _RecordNote {
);
}
+ $args{'MIMEObj'}->head->replace('X-RT-Interface' => 'API')
+ unless $args{'MIMEObj'}->head->get('X-RT-Interface');
+
# convert text parts into utf-8
RT::I18N::SetMIMEEntityToUTF8( $args{'MIMEObj'} );
@@ -2502,7 +2535,7 @@ sub _Links {
Delete a link. takes a paramhash of Base, Target, Type, Silent,
SilentBase and SilentTarget. Either Base or Target must be null.
-The null value will be replaced with this ticket\'s id.
+The null value will be replaced with this ticket's id.
If Silent is true then no transaction would be recorded, in other
case you can control creation of transactions on both base and
@@ -2653,9 +2686,7 @@ sub __GetTicketFromURI {
# If the other URI is an RT::Ticket, we want to make sure the user
# can modify it too...
my $uri_obj = RT::URI->new( $self->CurrentUser );
- $uri_obj->FromURI( $args{'URI'} );
-
- unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+ unless ($uri_obj->FromURI( $args{'URI'} )) {
my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
$RT::Logger->warning( $msg );
return( 0, $msg );
@@ -3196,11 +3227,16 @@ sub ValidateStatus {
return 0;
}
-
+sub Status {
+ my $self = shift;
+ my $value = $self->_Value( 'Status' );
+ return $value unless $self->QueueObj;
+ return $self->QueueObj->Lifecycle->CanonicalCase( $value );
+}
=head2 SetStatus STATUS
-Set this ticket\'s status. STATUS can be one of: new, open, stalled, resolved, rejected or deleted.
+Set this ticket's status. STATUS can be one of: new, open, stalled, resolved, rejected or deleted.
Alternatively, you can pass in a list of named parameters (Status => STATUS, Force => FORCE, SetStarted => SETSTARTED ).
If FORCE is true, ignore unresolved dependencies and force a status change.
@@ -3226,7 +3262,7 @@ sub SetStatus {
my $lifecycle = $self->QueueObj->Lifecycle;
- my $new = $args{'Status'};
+ my $new = lc $args{'Status'};
unless ( $lifecycle->IsValid( $new ) ) {
return (0, $self->loc("Status '[_1]' isn't a valid status for tickets in this queue.", $self->loc($new)));
}
@@ -3274,7 +3310,7 @@ sub SetStatus {
#Actually update the status
my ($val, $msg)= $self->_Set(
Field => 'Status',
- Value => $args{Status},
+ Value => $new,
TimeTaken => 0,
CheckACL => 0,
TransactionType => 'Status',
@@ -3578,6 +3614,9 @@ sub _Set {
OldValue => $Old,
TimeTaken => $args{'TimeTaken'},
);
+ # Ensure that we can read the transaction, even if the change
+ # just made the ticket unreadable to us
+ $TransObj->{ _object_is_readable } = 1;
return ( $Trans, scalar $TransObj->BriefDescription );
}
else {
@@ -3790,37 +3829,29 @@ sub TransactionCustomFields {
}
+=head2 LoadCustomFieldByIdentifier
-=head2 CustomFieldValues
-
-# Do name => id mapping (if needed) before falling back to
-# RT::Record's CustomFieldValues
-
-See L<RT::Record>
+Finds and returns the custom field of the given name for the ticket,
+overriding L<RT::Record/LoadCustomFieldByIdentifier> to look for
+queue-specific CFs before global ones.
=cut
-sub CustomFieldValues {
+sub LoadCustomFieldByIdentifier {
my $self = shift;
my $field = shift;
- return $self->SUPER::CustomFieldValues( $field ) if !$field || $field =~ /^\d+$/;
+ return $self->SUPER::LoadCustomFieldByIdentifier($field)
+ if ref $field or $field =~ /^\d+$/;
my $cf = RT::CustomField->new( $self->CurrentUser );
$cf->SetContextObject( $self );
$cf->LoadByNameAndQueue( Name => $field, Queue => $self->Queue );
- unless ( $cf->id ) {
- $cf->LoadByNameAndQueue( Name => $field, Queue => 0 );
- }
-
- # If we didn't find a valid cfid, give up.
- return RT::ObjectCustomFieldValues->new( $self->CurrentUser ) unless $cf->id;
-
- return $self->SUPER::CustomFieldValues( $cf->id );
+ $cf->LoadByNameAndQueue( Name => $field, Queue => 0 ) unless $cf->id;
+ return $cf;
}
-
=head2 CustomFieldLookupType
Returns the RT::Ticket lookup type, which can be passed to
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
index c9986f4..06b17e2 100755
--- a/rt/lib/RT/Tickets.pm
+++ b/rt/lib/RT/Tickets.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -158,6 +158,9 @@ our %FIELD_METADATA = (
WillResolve => [ 'DATE' => 'WillResolve', ], #loc_left_pair
);
+# Lower Case version of FIELDS, for case insensitivity
+our %LOWER_CASE_FIELDS = map { ( lc($_) => $_ ) } (keys %FIELD_METADATA);
+
our %SEARCHABLE_SUBFIELDS = (
User => [qw(
EmailAddress Name RealName Nickname Organization Address1 Address2
@@ -379,7 +382,11 @@ sub _EnumLimit {
my $class = "RT::" . $meta->[1];
my $o = $class->new( $sb->CurrentUser );
$o->Load($value);
- $value = $o->Id;
+ $value = $o->Id || 0;
+ } elsif ( $field eq "Type" ) {
+ $value = lc $value if $value =~ /^(ticket|approval|reminder)$/i;
+ } elsif ($field eq "Status") {
+ $value = lc $value;
}
$sb->_SQLLimit(
FIELD => $field,
@@ -463,7 +470,7 @@ sub _LinkLimit {
my $is_local = 1;
if ( $is_null ) {
- $op = ($op =~ /^(=|IS)$/)? 'IS': 'IS NOT';
+ $op = ($op =~ /^(=|IS)$/i)? 'IS': 'IS NOT';
}
elsif ( $value =~ /\D/ ) {
$is_local = 0;
@@ -941,22 +948,23 @@ sub _WatcherLimit {
die "Invalid watcher subfield: '$rest{SUBKEY}'";
}
+ # if it's equality op and search by Email or Name then we can preload user
+ # we do it to help some DBs better estimate number of rows and get better plans
+ if ( $op =~ /^!?=$/ && (!$rest{'SUBKEY'} || $rest{'SUBKEY'} eq 'Name' || $rest{'SUBKEY'} eq 'EmailAddress') ) {
+ my $o = RT::User->new( $self->CurrentUser );
+ my $method =
+ !$rest{'SUBKEY'}
+ ? $field eq 'Owner'? 'Load' : 'LoadByEmail'
+ : $rest{'SUBKEY'} eq 'EmailAddress' ? 'LoadByEmail': 'Load';
+ $o->$method( $value );
+ $rest{'SUBKEY'} = 'id';
+ $value = $o->id || 0;
+ }
+
# Owner was ENUM field, so "Owner = 'xxx'" allowed user to
# search by id and Name at the same time, this is workaround
# to preserve backward compatibility
if ( $field eq 'Owner' ) {
- if ( $op =~ /^!?=$/ && (!$rest{'SUBKEY'} || $rest{'SUBKEY'} eq 'Name' || $rest{'SUBKEY'} eq 'EmailAddress') ) {
- my $o = RT::User->new( $self->CurrentUser );
- my $method = ($rest{'SUBKEY'}||'') eq 'EmailAddress' ? 'LoadByEmail': 'Load';
- $o->$method( $value );
- $self->_SQLLimit(
- FIELD => 'Owner',
- OPERATOR => $op,
- VALUE => $o->id,
- %rest,
- );
- return;
- }
if ( ($rest{'SUBKEY'}||'') eq 'id' ) {
$self->_SQLLimit(
FIELD => 'Owner',
@@ -972,7 +980,7 @@ sub _WatcherLimit {
my $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type );
$self->_OpenParen;
- if ( $op =~ /^IS(?: NOT)?$/ ) {
+ if ( $op =~ /^IS(?: NOT)?$/i ) {
# is [not] empty case
my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
@@ -1569,6 +1577,29 @@ sub _CustomFieldLimit {
}
}
+ if ( $cf && $cf->Type =~ /^Date(?:Time)?$/ ) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+ if ( $date->Unix ) {
+
+ if (
+ $cf->Type eq 'Date'
+ || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i
+ || ( $value !~ /midnight|\d+:\d+:\d+/i
+ && $date->Time( Timezone => 'user' ) eq '00:00:00' )
+ )
+ {
+ $value = $date->Date( Timezone => 'user' );
+ }
+ else {
+ $value = $date->DateTime;
+ }
+ }
+ else {
+ $RT::Logger->warn("$value is not a valid date string");
+ }
+ }
+
my $single_value = !$cf || !$cfid || $cf->SingleValue;
my $cfkey = $cfid ? $cfid : "$queue.$field";
@@ -1664,27 +1695,12 @@ sub _CustomFieldLimit {
}
else {
# need special treatment for Date
- if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' ) {
-
- if ( $value =~ /:/ ) {
- # there is time speccified.
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
- $self->_SQLLimit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => "=",
- VALUE => $date->ISO,
- %rest,
- );
- }
- else {
+ if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
# no time specified, that means we want everything on a
# particular day. in the database, we need to check for >
# and < the edges of that day.
my $date = RT::Date->new( $self->CurrentUser );
$date->Set( Format => 'unknown', Value => $value );
- $date->SetToMidnight( Timezone => 'server' );
my $daystart = $date->ISO;
$date->AddDay;
my $dayend = $date->ISO;
@@ -1702,14 +1718,13 @@ sub _CustomFieldLimit {
$self->_SQLLimit(
ALIAS => $TicketCFs,
FIELD => 'Content',
- OPERATOR => "<=",
+ OPERATOR => "<",
VALUE => $dayend,
%rest,
ENTRYAGGREGATOR => 'AND',
);
$self->_CloseParen;
- }
}
elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
@@ -2470,7 +2485,7 @@ sub LimitType {
VALUE => $args{'VALUE'},
OPERATOR => $args{'OPERATOR'},
DESCRIPTION => join( ' ',
- $self->loc('Type'), $args{'OPERATOR'}, $args{'Limit'}, ),
+ $self->loc('Type'), $args{'OPERATOR'}, $args{'VALUE'}, ),
);
}
@@ -2533,7 +2548,7 @@ sub LimitId {
Takes a paramhash with the fields OPERATOR and VALUE.
OPERATOR is one of =, >, < or !=.
-VALUE is a value to match the ticket\'s priority against
+VALUE is a value to match the ticket's priority against
=cut
@@ -2556,7 +2571,7 @@ sub LimitPriority {
Takes a paramhash with the fields OPERATOR and VALUE.
OPERATOR is one of =, >, < or !=.
-VALUE is a value to match the ticket\'s initial priority against
+VALUE is a value to match the ticket's initial priority against
=cut
@@ -2580,7 +2595,7 @@ sub LimitInitialPriority {
Takes a paramhash with the fields OPERATOR and VALUE.
OPERATOR is one of =, >, < or !=.
-VALUE is a value to match the ticket\'s final priority against
+VALUE is a value to match the ticket's final priority against
=cut
@@ -2753,7 +2768,7 @@ sub LimitOwner {
Takes a paramhash with the fields OPERATOR, TYPE and VALUE.
OPERATOR is one of =, LIKE, NOT LIKE or !=.
- VALUE is a value to match the ticket\'s watcher email addresses against
+ VALUE is a value to match the ticket's watcher email addresses against
TYPE is the sort of watchers you want to match against. Leave it undef if you want to search all of them
diff --git a/rt/lib/RT/Tickets_SQL.pm b/rt/lib/RT/Tickets_SQL.pm
index ec1bb49..608862a 100644
--- a/rt/lib/RT/Tickets_SQL.pm
+++ b/rt/lib/RT/Tickets_SQL.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -57,10 +57,7 @@ use RT::SQL;
# Import configuration data from the lexcial scope of __PACKAGE__ (or
# at least where those two Subroutines are defined.)
-our (%FIELD_METADATA, %dispatch, %can_bundle);
-
-# Lower Case version of FIELDS, for case insensitivity
-my %lcfields = map { ( lc($_) => $_ ) } (keys %FIELD_METADATA);
+our (%FIELD_METADATA, %LOWER_CASE_FIELDS, %dispatch, %can_bundle);
sub _InitSQL {
my $self = shift;
@@ -193,8 +190,8 @@ sub _parser {
# normalize key and get class (type)
my $class;
- if (exists $lcfields{lc $key}) {
- $key = $lcfields{lc $key};
+ if (exists $LOWER_CASE_FIELDS{lc $key}) {
+ $key = $LOWER_CASE_FIELDS{lc $key};
$class = $FIELD_METADATA{$key}->[0];
}
die "Unknown field '$key' in '$string'" unless $class;
diff --git a/rt/lib/RT/Topic.pm b/rt/lib/RT/Topic.pm
index 3499a4d..3e91e9d 100644
--- a/rt/lib/RT/Topic.pm
+++ b/rt/lib/RT/Topic.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Topics.pm b/rt/lib/RT/Topics.pm
index fe7c3d8..01674fb 100644
--- a/rt/lib/RT/Topics.pm
+++ b/rt/lib/RT/Topics.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -47,6 +47,7 @@
# END BPS TAGGED BLOCK }}}
use strict;
+use warnings;
no warnings qw(redefine);
package RT::Topics;
diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm
index 3344687..48d4e8c 100755
--- a/rt/lib/RT/Transaction.pm
+++ b/rt/lib/RT/Transaction.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -48,7 +48,7 @@
=head1 NAME
- RT::Transaction - RT\'s transaction object
+ RT::Transaction - RT's transaction object
=head1 SYNOPSIS
@@ -386,13 +386,24 @@ sub Content {
}
$content =~ s/^/> /gm;
- $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name)
- . "\n$content\n\n";
+ $content = $self->QuoteHeader . "\n$content\n\n";
}
return ($content);
}
+=head2 QuoteHeader
+
+Returns text prepended to content when transaction is quoted
+(see C<Quote> argument in L</Content>). By default returns
+localized "On <date> <user name> wrote:\n".
+
+=cut
+
+sub QuoteHeader {
+ my $self = shift;
+ return $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name);
+}
=head2 Addresses
@@ -640,11 +651,14 @@ sub BriefDescription {
return ( $self->loc( "[_1] deleted", $obj_type ) );
}
else {
+ my $canon = $self->Object->can("QueueObj")
+ ? sub { $self->Object->QueueObj->Lifecycle->CanonicalCase(@_) }
+ : sub { return $_[0] };
return (
$self->loc(
"Status changed from [_1] to [_2]",
- "'" . $self->loc( $self->OldValue ) . "'",
- "'" . $self->loc( $self->NewValue ) . "'"
+ "'" . $self->loc( $canon->($self->OldValue) ) . "'",
+ "'" . $self->loc( $canon->($self->NewValue) ) . "'"
)
);
@@ -782,8 +796,7 @@ sub BriefDescription {
my $value;
if ( $self->NewValue ) {
my $URI = RT::URI->new( $self->CurrentUser );
- $URI->FromURI( $self->NewValue );
- if ( $URI->Resolver ) {
+ if ( $URI->FromURI( $self->NewValue ) ) {
$value = $URI->Resolver->AsString;
}
else {
@@ -821,8 +834,7 @@ sub BriefDescription {
my $value;
if ( $self->OldValue ) {
my $URI = RT::URI->new( $self->CurrentUser );
- $URI->FromURI( $self->OldValue );
- if ( $URI->Resolver ) {
+ if ( $URI->FromURI( $self->OldValue ) ){
$value = $URI->Resolver->AsString;
}
else {
@@ -1072,6 +1084,11 @@ sub CurrentUserCanSee {
$cf->Load( $cf_id );
return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
}
+
+ # Transactions that might have changed the ->Object's visibility to
+ # the current user are marked readable
+ return 1 if $self->{ _object_is_readable };
+
# Defer to the object in question
return $self->Object->CurrentUserCanSee("Transaction");
}
@@ -1181,37 +1198,31 @@ sub UpdateCustomFields {
}
}
+=head2 LoadCustomFieldByIdentifier
-
-=head2 CustomFieldValues
-
- Do name => id mapping (if needed) before falling back to RT::Record's CustomFieldValues
-
- See L<RT::Record>
+Finds and returns the custom field of the given name for the
+transaction, overriding L<RT::Record/LoadCustomFieldByIdentifier> to
+look for queue-specific CFs before global ones.
=cut
-sub CustomFieldValues {
+sub LoadCustomFieldByIdentifier {
my $self = shift;
my $field = shift;
- if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) {
-
- # XXX: $field could be undef when we want fetch values for all CFs
- # do we want to cover this situation somehow here?
- unless ( defined $field && $field =~ /^\d+$/o ) {
- my $CFs = RT::CustomFields->new( $self->CurrentUser );
- $CFs->SetContextObject( $self->Object );
- $CFs->Limit( FIELD => 'Name', VALUE => $field );
- $CFs->LimitToLookupType($self->CustomFieldLookupType);
- $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
- $field = $CFs->First->id if $CFs->First;
- }
- }
- return $self->SUPER::CustomFieldValues($field);
-}
+ return $self->SUPER::LoadCustomFieldByIdentifier($field)
+ if ref $field or $field =~ /^\d+$/;
+ return $self->SUPER::LoadCustomFieldByIdentifier($field)
+ unless UNIVERSAL::can( $self->Object, 'QueueObj' );
+ my $CFs = RT::CustomFields->new( $self->CurrentUser );
+ $CFs->SetContextObject( $self->Object );
+ $CFs->Limit( FIELD => 'Name', VALUE => $field );
+ $CFs->LimitToLookupType($self->CustomFieldLookupType);
+ $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
+ return $CFs->First || RT::CustomField->new( $self->CurrentUser );
+}
=head2 CustomFieldLookupType
diff --git a/rt/lib/RT/Transactions.pm b/rt/lib/RT/Transactions.pm
index 86a05f7..3c9dac4 100755
--- a/rt/lib/RT/Transactions.pm
+++ b/rt/lib/RT/Transactions.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/URI.pm b/rt/lib/RT/URI.pm
index 284a75e..c0958ca 100644
--- a/rt/lib/RT/URI.pm
+++ b/rt/lib/RT/URI.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/URI/a.pm b/rt/lib/RT/URI/a.pm
index b88af26..9475ba1 100644
--- a/rt/lib/RT/URI/a.pm
+++ b/rt/lib/RT/URI/a.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/URI/base.pm b/rt/lib/RT/URI/base.pm
index 19888b0..63af140 100644
--- a/rt/lib/RT/URI/base.pm
+++ b/rt/lib/RT/URI/base.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -49,6 +49,7 @@
package RT::URI::base;
use strict;
+use warnings;
use base qw(RT::Base);
=head1 NAME
diff --git a/rt/lib/RT/URI/fsck_com_article.pm b/rt/lib/RT/URI/fsck_com_article.pm
index 0c09b7c..2b2132f 100644
--- a/rt/lib/RT/URI/fsck_com_article.pm
+++ b/rt/lib/RT/URI/fsck_com_article.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/URI/fsck_com_rt.pm b/rt/lib/RT/URI/fsck_com_rt.pm
index 1c11e22..34249d0 100644
--- a/rt/lib/RT/URI/fsck_com_rt.pm
+++ b/rt/lib/RT/URI/fsck_com_rt.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/URI/t.pm b/rt/lib/RT/URI/t.pm
index e73f644..71c81fa 100644
--- a/rt/lib/RT/URI/t.pm
+++ b/rt/lib/RT/URI/t.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
index f26ace4..5511b9f 100755
--- a/rt/lib/RT/User.pm
+++ b/rt/lib/RT/User.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -167,18 +167,10 @@ sub Create {
return ( 0, $self->loc("Must specify 'Name' attribute") );
}
- #SANITY CHECK THE NAME AND ABORT IF IT'S TAKEN
- if (RT->SystemUser) { #This only works if RT::SystemUser has been defined
- my $TempUser = RT::User->new(RT->SystemUser);
- $TempUser->Load( $args{'Name'} );
- return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
-
- my ($val, $message) = $self->ValidateEmailAddress( $args{'EmailAddress'} );
- return (0, $message) unless ( $val );
- } else {
- $RT::Logger->warning( "$self couldn't check for pre-existing users");
- }
-
+ my ( $val, $msg ) = $self->ValidateName( $args{'Name'} );
+ return ( 0, $msg ) unless $val;
+ ( $val, $msg ) = $self->ValidateEmailAddress( $args{'EmailAddress'} );
+ return ( 0, $msg ) unless ($val);
$RT::Handle->BeginTransaction();
# Groups deal with principal ids, rather than user ids.
@@ -270,6 +262,30 @@ sub Create {
return ( $id, $self->loc('User created') );
}
+=head2 ValidateName STRING
+
+Returns either (0, "failure reason") or 1 depending on whether the given
+name is valid.
+
+=cut
+
+sub ValidateName {
+ my $self = shift;
+ my $name = shift;
+
+ return ( 0, $self->loc('empty name') ) unless defined $name && length $name;
+
+ my $TempUser = RT::User->new( RT->SystemUser );
+ $TempUser->Load($name);
+
+ if ( $TempUser->id && ( !$self->id || $TempUser->id != $self->id ) ) {
+ return ( 0, $self->loc('Name in use') );
+ }
+ else {
+ return 1;
+ }
+}
+
=head2 ValidatePassword STRING
Returns either (0, "failure reason") or 1 depending on whether the given
@@ -572,6 +588,25 @@ sub ValidateEmailAddress {
}
}
+=head2 SetName
+
+Check to make sure someone else isn't using this name already
+
+=cut
+
+sub SetName {
+ my $self = shift;
+ my $Value = shift;
+
+ my ( $val, $message ) = $self->ValidateName($Value);
+ if ($val) {
+ return $self->_Set( Field => 'Name', Value => $Value );
+ }
+ else {
+ return ( 0, $message );
+ }
+}
+
=head2 SetEmailAddress
Check to make sure someone else isn't using this email address already
@@ -1393,7 +1428,7 @@ $user->WatchedQueues('Cc', 'AdminCc');
sub WatchedQueues {
my $self = shift;
- my @roles = @_ || ('Cc', 'AdminCc');
+ my @roles = @_ ? @_ : ('Cc', 'AdminCc');
$RT::Logger->debug('WatcheQueues got user ' . $self->Name);
diff --git a/rt/lib/RT/Users.pm b/rt/lib/RT/Users.pm
index 2784fc7..0f5ca70 100755
--- a/rt/lib/RT/Users.pm
+++ b/rt/lib/RT/Users.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -118,7 +118,7 @@ sub PrincipalsAlias {
=head2 LimitToEnabled
-Only find items that haven\'t been disabled
+Only find items that haven't been disabled
=cut
diff --git a/rt/lib/RT/Util.pm b/rt/lib/RT/Util.pm
index 24efe71..38c3c20 100644
--- a/rt/lib/RT/Util.pm
+++ b/rt/lib/RT/Util.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)