summaryrefslogtreecommitdiff
path: root/rt/share/html/Admin
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2012-04-24 11:35:56 -0700
committerIvan Kohler <ivan@freeside.biz>2012-04-24 11:35:56 -0700
commit6587f6ba7d047ddc1686c080090afe7d53365bd4 (patch)
treeec77342668e8865aca669c9b4736e84e3077b523 /rt/share/html/Admin
parent47153aae5c2fc00316654e7277fccd45f72ff611 (diff)
first pass RT4 merge, RT#13852
Diffstat (limited to 'rt/share/html/Admin')
-rw-r--r--rt/share/html/Admin/Articles/Classes/CustomFields.html64
-rw-r--r--rt/share/html/Admin/Articles/Classes/GroupRights.html73
-rw-r--r--rt/share/html/Admin/Articles/Classes/Modify.html199
-rw-r--r--rt/share/html/Admin/Articles/Classes/Objects.html154
-rw-r--r--rt/share/html/Admin/Articles/Classes/Topics.html68
-rw-r--r--rt/share/html/Admin/Articles/Classes/UserRights.html72
-rw-r--r--rt/share/html/Admin/Articles/Classes/index.html91
-rw-r--r--rt/share/html/Admin/Articles/Elements/Topics215
-rw-r--r--rt/share/html/Admin/Articles/index.html50
-rw-r--r--rt/share/html/Admin/Elements/EditQueueWatcherGroup56
-rw-r--r--rt/share/html/Admin/Elements/EditRights175
-rw-r--r--rt/share/html/Admin/Elements/EditRightsCategoryTabs129
-rw-r--r--rt/share/html/Admin/Elements/Portal52
-rw-r--r--rt/share/html/Admin/Elements/SelectCustomFieldRenderType70
-rw-r--r--rt/share/html/Admin/Global/CustomFields/Class-Article.html54
-rw-r--r--rt/share/html/Admin/Global/Topics.html63
-rw-r--r--rt/share/html/Admin/Tools/Queries.html129
-rw-r--r--rt/share/html/Admin/Tools/Theme.html309
18 files changed, 2023 insertions, 0 deletions
diff --git a/rt/share/html/Admin/Articles/Classes/CustomFields.html b/rt/share/html/Admin/Articles/Classes/CustomFields.html
new file mode 100644
index 000000000..868c288b8
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/CustomFields.html
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Admin/Elements/EditCustomFields, %ARGS, title => $title, Object => $Object, ObjectType => 'RT::Class', SubType => 'RT::Article' &>
+<%INIT>
+my $Object = RT::Class->new( $session{'CurrentUser'} );
+
+$Object->Load($id) || Abort( loc( "Couldn't load object [_1]", $id ) );
+my $FriendlySubTypes =
+ RT::CustomField->new( $session{'CurrentUser'} )
+ ->FriendlyLookupType( $Object->CustomFieldLookupType );
+
+my $title = loc( 'Edit Custom Fields for [_1]', $Object->Name );
+
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/GroupRights.html b/rt/share/html/Admin/Articles/Classes/GroupRights.html
new file mode 100644
index 000000000..092ea4b23
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/GroupRights.html
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify group rights for Class [_1]', $ClassObj->Name) &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="GroupRights.html" id="ModifyGroupRights" name="ModifyGroupRights">
+<input type="hidden" name="id" value="<% $ClassObj->id %>" />
+
+ <& /Admin/Elements/EditRights, Context => $ClassObj, Principals => \@principals &>
+
+ <& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+<%INIT>
+
+if (!defined $id) {
+ $m->comp("/Elements/Error", Why => loc("No Class defined"));
+}
+
+my $ClassObj = RT::Class->new($session{'CurrentUser'});
+$ClassObj->Load($id) || $m->comp("/Elements/Error", Why => loc("Couldn't load Class [_1]",$id));
+
+my @results = ProcessACLs(\%ARGS);
+my @principals = GetPrincipalsMap($ClassObj, qw(System Groups));
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/Modify.html b/rt/share/html/Admin/Articles/Classes/Modify.html
new file mode 100644
index 000000000..8dbd2897d
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/Modify.html
@@ -0,0 +1,199 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form name="ModifyClass" action="<% RT->Config->Get('WebPath') %>/Admin/Articles/Classes/Modify.html" method="post">
+<input type="hidden" name="Submitted" value="1" />
+%if ($Create ) {
+<input type="hidden" name="id" value="new" />
+% } else {
+<input type="hidden" name="id" value="<%$ClassObj->Id%>" />
+% }
+
+<table>
+<tr>
+<td align="right"><&|/l&>Class Name</&>: </td>
+<td><input name="Name" value="<% $ClassObj->Name || $Name || ''%>" /></td>
+</tr>
+<tr>
+<td align="right"><&|/l&>Description</&>:</td>
+<td><input name="Description" value="<% $ClassObj->Description || $Description || '' %>" size=60 /></td>
+</tr>
+<tr>
+<td>&nbsp;</td>
+<td><input type="checkbox" name="Enabled" value="1" <%$EnabledChecked%>> <&|/l&>Enabled (Unchecking this box disables this class)</&></td>
+</tr>
+<tr>
+<td>&nbsp;</td>
+<td><input type="checkbox" name="HotList" value="1" <%$HotListChecked%>> <&|/l&>All Articles in this class should be listed in a dropdown of the ticket reply page</&></td>
+</tr>
+</table>
+
+<h3>When inserting articles in this class into emails:</h3>
+<ul>
+<li><input type="checkbox" name="Include-Name" value="1" <% $include{Name} %>> Include article name</li>
+<li><input type="checkbox" name="Include-Summary" value="1" <% $include{Summary} %>> Include article summary</li>
+% if ( $cfs ) {
+% while (my $cf = $cfs->Next) {
+<li>Include custom field '<% $cf->Name %>'
+ <ul>
+ <li><input type="checkbox" name="Include-CF-Title-<% $cf->Id %>" <% $include{"CF-Title-".$cf->Id} %> />Title</li>
+ <li><input type="checkbox" name="Include-CF-Value-<% $cf->Id %>" <% $include{"CF-Value-".$cf->Id} %> />Value</li>
+ </ul>
+</li>
+% } }
+</ul>
+
+%$m->callback( CallbackName => 'BeforeSubmit', CustomFields => $cfs, ClassObj => $ClassObj );
+
+% if ( $Create ) {
+<& /Elements/Submit, Label => loc('Create') &>
+% } else {
+<& /Elements/Submit, Label => loc('Save Changes') &>
+% }
+</form>
+
+<%INIT>
+
+my $ClassObj = RT::Class->new($session{'CurrentUser'});
+my ($title, @results, $Disabled);
+
+if ($Create) {
+ $title = loc("Create a Class");
+} else {
+ my ( $val, $msg );
+ if ($id eq 'new') {
+ ($val, $msg) = $ClassObj->Create(Name => $Name);
+ if ( $val ) {
+ push @results, $msg;
+ }
+ else {
+ push @results, loc('Class could not be created: [_1]', $msg);
+ }
+ } else {
+ $ClassObj->Load($id) || $ClassObj->Load($Name) || $m->comp("/Elements/Error", Why => "Couldn't load class '$Name'");
+ $val = $ClassObj->id;
+ }
+
+ if ( $val ) {
+ $title = loc('Modify the Class [_1]', $ClassObj->Name);
+ }
+ else {
+ $title = loc("Create a Class");
+ $Create = 1;
+ }
+}
+
+if ($ClassObj->Id()) {
+ $ARGS{HotList} ||= 0 if $Submitted;
+ my @attribs= qw(Description Name HotList);
+ $m->callback( CallbackName => 'AttributeList', Attributes => \@attribs, ARGSRef => \%ARGS );
+ push @results, UpdateRecordObject( AttributesRef => \@attribs,
+ Object => $ClassObj,
+ ARGSRef => \%ARGS);
+}
+
+#we're asking about enabled on the web page but really care about disabled.
+if ((defined $Enabled && $Enabled == 1) or (not defined $Enabled and $Create)) {
+ $Disabled = 0;
+} else {
+ $Disabled = 1;
+}
+
+my %include = (Name => 1, Summary => 1);
+
+my $cfs;
+if ( $ClassObj->id ) {
+ $cfs = $ClassObj->ArticleCustomFields;
+ $include{"CF-Title-".$_->Id} = $include{"CF-Value-".$_->Id} = 1 while $_ = $cfs->Next;
+}
+
+if ( $ClassObj->id && $Submitted ) {
+ if ( $Disabled != $ClassObj->Disabled) {
+ my ($code, $msg) = $ClassObj->SetDisabled($Disabled);
+ push @results, loc('Enabled status [_1]', loc_fuzzy($msg));
+ }
+
+ for (keys %include) {
+ if ($ARGS{"Include-$_"}) {
+ $ClassObj->DeleteAttribute("Skip-$_");
+ } else {
+ $ClassObj->SetAttribute(Name => "Skip-$_", Content => 1);
+ }
+ }
+}
+
+# This code does automatic redirection if any updates happen.
+MaybeRedirectForResults(
+ Actions => \@results,
+ Arguments => { id => $ClassObj->Id },
+) if $ClassObj->id;
+
+
+if ( $ClassObj->id ) {
+ $include{$_} = not $ClassObj->FirstAttribute("Skip-$_") for keys %include;
+}
+
+$include{$_} = $include{$_} ? " CHECKED" : "" for keys %include;
+
+my $EnabledChecked = ($Create ? $Disabled : $ClassObj->Disabled()) ? "" : "CHECKED";
+my $HotListChecked = $ClassObj->id && $ClassObj->HotList ? "CHECKED" : "";
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$result => undef
+$Name => undef
+$Create => undef
+$Description => undef
+$Submitted => undef
+$Enabled => undef
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/Objects.html b/rt/share/html/Admin/Articles/Classes/Objects.html
new file mode 100644
index 000000000..f7be9f598
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/Objects.html
@@ -0,0 +1,154 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="Objects.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
+
+% if ( $is_global ) {
+<h2><&|/l&>Applies to all objects</&></h2>
+<input type="checkbox" name="RemoveClass-<% $Class->id %>" value="0" />
+<&|/l&>check this box to remove this Class globally and be able to choose specific Queues.</&>
+% } else {
+<h2><&|/l&>Apply globally</&></h2>
+
+<input type="checkbox" name="AddClass-<% $Class->id %>" value="0" />
+<&|/l&>check this box to apply this Class globally to all Queues.</&>
+
+<h2><&|/l&>Selected Queues</&></h2>
+<& /Elements/CollectionList,
+ OrderBy => 'id',
+ Order => 'ASC',
+ %ARGS,
+ Collection => $applied,
+ Rows => 0,
+ Page => 1,
+ Format => $format,
+ DisplayFormat => "'__CheckBox.{RemoveClass-". $Class->id ."}__',". $format,
+ AllowSorting => 0,
+ ShowEmpty => 0,
+ PassArguments => [
+ qw(id Format Rows Page Order OrderBy),
+ ],
+&>
+
+<h2><&|/l&>Unselected Queues</&></h2>
+<& /Elements/CollectionList,
+ OrderBy => 'id',
+ Order => 'ASC',
+ %ARGS,
+ Collection => $not_applied,
+ Rows => 50,
+ Format => $format,
+ DisplayFormat => "'__CheckBox.{AddClass-". $Class->id ."}__',". $format,
+ AllowSorting => 1,
+ ShowEmpty => 0,
+ PassArguments => [
+ qw(id Format Rows Page Order OrderBy),
+ ],
+&>
+
+% }
+
+<& /Elements/Submit, Name => 'UpdateObjs' &>
+</form>
+
+<%INIT>
+my $Class = RT::Class->new($session{'CurrentUser'});
+$Class->Load($id) or Abort(loc("Could not load Class [_1]"), $id);
+
+my (@results);
+
+if ( $UpdateObjs ) {
+ if ( defined (my $del = $ARGS{'RemoveClass-'.$Class->id}) ) {
+ foreach my $id ( ref $del? (@$del) : ($del) ) {
+ my $object = RT::Queue->new( $session{'CurrentUser'} );
+ if ( $id ) {
+ $object->Load( $id );
+ next unless $object->id;
+ }
+
+ my ($status, $msg) = $Class->RemoveFromObject( $object );
+ push @results, $msg;
+ }
+ }
+ if ( defined (my $add = $ARGS{'AddClass-'.$Class->id}) ) {
+ foreach my $id ( ref $add? (@$add) : ($add) ) {
+ my $object = RT::Queue->new( $session{'CurrentUser'} );
+ if ( $id ) {
+ $object->Load( $id );
+ next unless $object->id;
+ }
+
+ my ($status, $msg) = $Class->AddToObject( $object );
+ push @results, $msg;
+ }
+ }
+}
+
+my $is_global = $Class->IsApplied(0);
+
+my $applied = $Class->AppliedTo;
+my $not_applied = $Class->NotAppliedTo;
+
+my $collection_class = ref($applied);
+$collection_class =~ s/^RT:://;
+
+my $format = RT->Config->Get('AdminSearchResultFormat')->{$collection_class}
+ || '__id__,__Name__';
+
+my $title = loc('Modify associated objects for [_1]', $Class->Name);
+
+</%INIT>
+<%ARGS>
+$id => undef
+$FindDisabledObjects => 0
+$UpdateObjs => 0
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/Topics.html b/rt/share/html/Admin/Articles/Classes/Topics.html
new file mode 100644
index 000000000..5418d81a5
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/Topics.html
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+
+<& /Admin/Articles/Elements/Topics, title => $title, RootObj => $ClassObj, %ARGS &>
+
+<%INIT>
+
+my $ClassObj = RT::Class->new($session{'CurrentUser'});
+$ClassObj->Load($ARGS{'id'}) || $m->comp("/Elements/Error", Why => "Couldn't load class '$id'");
+
+my $title = $Modify
+ ? loc("Modify topic for [_1]", $ClassObj->Name)
+ : loc("Edit topic hierarchy for [_1]", $ClassObj->Name);
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$Modify => ""
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/UserRights.html b/rt/share/html/Admin/Articles/Classes/UserRights.html
new file mode 100644
index 000000000..c1aedeb33
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/UserRights.html
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify user rights for class [_1]', $ClassObj->Name) &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="UserRights.html" name="ModifyUserRights" id="ModifyUserRights">
+<input type="hidden" name="id" value="<% $ClassObj->id %>" />
+ <& /Admin/Elements/EditRights, Context => $ClassObj, Principals => \@principals &>
+
+ <& /Elements/Submit, Label => loc('Save Changes') &>
+<%INIT>
+my @results = ProcessACLs(\%ARGS);
+
+if (!defined $id) {
+ $m->comp("/Elements/Error", Why => loc("No Class defined"));
+}
+
+my $ClassObj = RT::Class->new($session{'CurrentUser'});
+$ClassObj->Load($id) || $m->comp("/Elements/Error", Why => loc("Couldn't load Class [_1]",$id));
+
+my @principals = GetPrincipalsMap($ClassObj, 'Users');
+
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Classes/index.html b/rt/share/html/Admin/Articles/Classes/index.html
new file mode 100644
index 000000000..8c32677c9
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Classes/index.html
@@ -0,0 +1,91 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => loc("Select a Class") &>
+<& /Elements/Tabs &>
+
+<h1><%$caption%>:</h1>
+<p><&|/l&>Select a Class</&>:</p>
+% unless ($Classes->Count) {
+<em><&|/l&>No Classes matching search criteria found.</&></em>
+% } else {
+<& /Elements/CollectionList,
+ OrderBy => 'Name',
+ Order => 'ASC',
+ %ARGS,
+ Format => $Format,
+ Collection => $Classes,
+ AllowSorting => 1,
+ PassArguments => [qw( Format Rows Page Order OrderBy FindDisabledClasses )],
+&>
+% }
+
+<form method="post" action="<% RT->Config->Get('WebPath') %>/Admin/Articles/Classes/index.html">
+<input type="checkbox" name="FindDisabledClasses" value="1" /><&|/l&>Include disabled classes in listing.</&><br />
+<div align="right"><input type="submit" value="<&|/l&>Go!</&>" /></div>
+</form>
+<%INIT>
+my $caption;
+my $Classes = RT::Classes->new($session{'CurrentUser'});
+
+if ($FindDisabledClasses) {
+ $caption = loc("All Classes");
+ $Classes->{'find_disabled_rows'} = 1;
+ $Classes->UnLimit();
+} else {
+ $caption = loc("Enabled Classes");
+ $Classes->LimitToEnabled();
+
+}
+
+$Format ||= RT->Config->Get('AdminSearchResultFormat')->{'Classes'};
+
+</%INIT>
+<%ARGS>
+$FindDisabledClasses => 0
+$Format => undef
+</%ARGS>
diff --git a/rt/share/html/Admin/Articles/Elements/Topics b/rt/share/html/Admin/Articles/Elements/Topics
new file mode 100644
index 000000000..96ddaf00c
--- /dev/null
+++ b/rt/share/html/Admin/Articles/Elements/Topics
@@ -0,0 +1,215 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%RT->Config->Get('WebPath')%><% $m->request_comp->path |n %>" method="post">
+<input type="hidden" name="id" value="<%$RootObj->Id%>" />
+
+% if (!$Modify) {
+<table>
+<tr>
+<td><&|/l&>Topic Name</&></td>
+<td><input type="text" name="Name" size="30" /></td>
+</tr>
+<tr>
+<td><&|/l&>Description</&></td>
+<td><input type="text" name="Description" size="50" /></td>
+</tr>
+</table>
+% } else {
+<a href="Topics.html">New topic</a>
+% }
+
+<& .tree, Element => $tree, Action => $Modify ? "Move" : "Add", Prefix => $Modify ? "Topic-$Modify-Parent" : "Insert", RootObj => $RootObj, Modify => $Modify &>
+
+</form>
+
+<%def .edit>
+&nbsp;
+<table style="margin-top: -0.75em">
+<tr>
+ <td>Topic Name:</td>
+ <td><input type="text" name="Topic-<%$topic->Id%>-Name" size="20" value="<%$topic->Name%>" /></td>
+ <td><input type="submit" name="Update" value="Update"><br /></td>
+</tr>
+<tr>
+ <td>Description:</td>
+ <td> <input type="text" name="Topic-<%$topic->Id%>-Description" size="20" value="<%$topic->Description%>" /></td>
+ <td><input type="submit" name="Delete-Topic-<%$topic->Id%>" value="Delete" /></td>
+</tr>
+</table>
+<%args>
+$topic
+</%args>
+</%def>
+
+<%def .tree>
+% my $topic = $Element->getNodeValue;
+% unless ($Element->isRoot) {
+% if ($Modify and $topic->Id == $Modify) {
+% $Action = "";
+<& .edit, topic => $topic &>
+% } else {
+<a href="<%RT->Config->Get('WebPath')%><% $m->request_comp->path |n %>?id=<%$RootObj->Id%>&Modify=<%$topic->Id%>"
+ title="<%$topic->Description%>"><%$topic->Name || loc("(no name)") %></a>
+% }
+% }
+<ul>
+% for my $e (sort {$a->getNodeValue->Name cmp $b->getNodeValue->Name} $Element->getAllChildren) {
+<li><& .tree, Element => $e, Action => $Action, Prefix => $Prefix, RootObj => $RootObj, Modify => $Modify &></li>
+% }
+% if ($Action) {
+% unless ($Action eq "Move" and grep {$_->getNodeValue->Id == $Modify} $Element->getAllChildren) {
+<li><input type="submit" name="<%$Prefix%>-<%$topic eq "root" ? 0 : $topic->Id%>" value="<&|/l&><%$Action%> here</&>" /></li>
+% }
+% }
+</ul>
+<%args>
+$Element
+$Action
+$Prefix
+$RootObj
+$Modify
+</%args>
+</%def>
+
+<%INIT>
+
+my @results;
+
+for my $k (keys %ARGS) {
+ if ($k =~ /^Topic-(\d+)-(Name|Description)/) {
+ my $topic = RT::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ next if $ARGS{$k} eq $topic->$2;
+ my $proc = "Set$2";
+ my ($val, $msg) = $topic->$proc($ARGS{$k});
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+ } elsif ($k =~ /^Topic-(\d+)-Parent-(\d+)/) {
+ my $topic = RT::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ next if $2 eq $topic->Parent;
+ my $old = $topic->Parent;
+ my $new = "$2";
+ my ($val, $msg) = $topic->setParent($new);
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+ } elsif ($k =~ /^Insert-(\d+)/) {
+ my $topic = RT::Topic->new($session{'CurrentUser'});
+ my ($id, $msg) = $topic->Create(
+ Parent => $1,
+ Name => $ARGS{'Name'},
+ Description => $ARGS{'Description'},
+ ObjectType => ref($RootObj),
+ ObjectId => $RootObj->Id,
+ );
+ push @results, $msg;
+ }
+}
+for my $k (keys %ARGS) {
+ next unless $k =~ /^Delete-Topic-(\d+)/;
+ my $topic = RT::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ my ($val, $msg) = $topic->DeleteAll();
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+}
+
+my $topics = RT::Topics->new($session{'CurrentUser'});
+$topics->LimitToObject($RootObj);
+$topics->OrderByCols({FIELD => 'Parent'}, {FIELD => 'id'});
+
+use Tree::Simple;
+my $tree = Tree::Simple->new(Tree::Simple->ROOT);
+my %lookup = (0 => $tree);
+
+my @todo;
+while (my $topic = $topics->Next) {
+ push @todo, $topic;
+}
+
+{
+ my $changed = 0;
+ my @work = @todo;
+ @todo = ();
+ for my $topic (@work) {
+ if (defined $lookup{$topic->Parent}) {
+ $lookup{$topic->Id} = Tree::Simple->new($topic, $lookup{$topic->Parent});
+ $changed = 1;
+ } else {
+ push @todo, $topic;
+ }
+ }
+ redo unless $changed == 0;
+}
+
+for my $topic (@todo) {
+ $topic->setParent(0);
+ $lookup{$topic->Id} = Tree::Simple->new($topic, $tree);
+ push @results, "Reparented orphan ".$topic->Id." to root";
+}
+
+</%INIT>
+
+
+<%ARGS>
+$RootObj => undef
+$title => ""
+$Modify => ""
+</%ARGS>
+
diff --git a/rt/share/html/Admin/Articles/index.html b/rt/share/html/Admin/Articles/index.html
new file mode 100644
index 000000000..d697dc3e0
--- /dev/null
+++ b/rt/share/html/Admin/Articles/index.html
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Elements/Header, Title => loc('Article Administration') &>
+<& /Elements/Tabs &>
+<& /Elements/ListMenu, menu => Menu()->child('tools')->child('config')->child('articles') &>
diff --git a/rt/share/html/Admin/Elements/EditQueueWatcherGroup b/rt/share/html/Admin/Elements/EditQueueWatcherGroup
new file mode 100644
index 000000000..8bc1de29a
--- /dev/null
+++ b/rt/share/html/Admin/Elements/EditQueueWatcherGroup
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<%ARGS>
+$Label
+$QueueObj
+$Watchers
+</%ARGS>
+<span><% $Label %>:</span>
+
+<& /Admin/Elements/EditQueueWatchers, QueueObj => $QueueObj, Watchers => $Watchers &>
+
diff --git a/rt/share/html/Admin/Elements/EditRights b/rt/share/html/Admin/Elements/EditRights
new file mode 100644
index 000000000..e5b9908b5
--- /dev/null
+++ b/rt/share/html/Admin/Elements/EditRights
@@ -0,0 +1,175 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<%args>
+$Context
+$Principals
+$AddPrincipal => undef
+</%args>
+<%init>
+use Scalar::Util qw(blessed);
+
+# Let callbacks get at principals and context before we do anything with them
+$m->callback( Principals => $Principals, Context => $Context );
+
+# Try to detect if we want to include an add user/group box
+unless ( $AddPrincipal ) {
+ my $last = $Principals->[-1];
+ if ( $last->[0] =~ /Groups/i ) {
+ $AddPrincipal = 'group'; # loc
+ }
+ elsif ( $last->[0] =~ /Users/i ) {
+ $AddPrincipal = 'user'; # loc
+ }
+}
+</%init>
+%# Principals is an array of arrays, where the inner arrays are like:
+%# [ 'Category name' => $CollectionObj => 'DisplayColumn' => 1 ]
+%# The last value is a boolen determining if the value of DisplayColumn
+%# should be loc()-ed before display.
+
+<script type="text/javascript">
+ jQuery(function() {
+ jQuery(".rights-editor").tabs({
+ select: function(ev, ui) {
+ window.location.hash = ui.tab.hash;
+ }
+ });
+ jQuery(".rights-editor .category-tabs").tabs();
+ });
+</script>
+
+<div class="rights-editor clearfix">
+ <ul>
+<%perl>
+for my $category (@$Principals) {
+ my ($name, $collection, $col, $loc) = @$category;
+</%perl>
+<li class="category"><% loc($name) %></li>
+<%perl>
+ while ( my $obj = $collection->Next ) {
+ my $display = ref $col eq 'CODE' ? $col->($obj) : $obj->$col;
+ my $id = "acl-$name-" . $obj->PrincipalId;
+ $id =~ s/[^a-zA-Z0-9\-]/_/g;
+</%perl>
+<li><a href="#<% $id %>"><% $loc ? loc($display) : $display %></a></li>
+<%perl>
+ }
+}
+</%perl>
+% if ( $AddPrincipal ) {
+ <li class="category"><&|/l, loc($AddPrincipal) &>Add [_1]</&></li>
+ <li class="addprincipal">
+ <a href="#acl-AddPrincipal">
+ <input type="text" value=""
+ name="AddPrincipalForRights-<% lc $AddPrincipal %>"
+ id="AddPrincipalForRights-<% lc $AddPrincipal %>" />
+ <script type="text/javascript">
+ jQuery(function() {
+ jQuery("#AddPrincipalForRights-<% lc $AddPrincipal %>").keyup(function(){
+ toggle_addprincipal_validity(this, true);
+ });
+
+% if (lc $AddPrincipal eq 'group') {
+ jQuery("#AddPrincipalForRights-<% lc $AddPrincipal %>").autocomplete({
+ source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Groups",
+ select: addprincipal_onselect,
+ change: addprincipal_onchange
+ });
+% }
+ });
+ </script>
+% my $type = lc $AddPrincipal eq 'user' ? loc('username') : loc($AddPrincipal);
+ <span class="warning"><&|/l, $type &>Invalid [_1]</&></span>
+ </a>
+ </li>
+% }
+ </ul>
+
+<%perl>
+# Now generate our rights panels for each principal
+for my $category (@$Principals) {
+ my ($name, $collection, $col, $loc) = @$category;
+ while ( my $obj = $collection->Next ) {
+ my $display = ref $col eq 'CODE' ? $col->($obj) : $obj->$col;
+ my $id = "acl-$name-" . $obj->PrincipalId;
+ $id =~ s/[^a-zA-Z0-9\-]/_/g;
+</%perl>
+
+ <div id="<% $id %>">
+ <h3>
+ <% $loc ? loc($display) : $display %>
+<%perl>
+if ($obj->isa('RT::Group') and $obj->Domain eq 'UserDefined') {
+ my $subgroups = $obj->GroupMembersObj( Recursively => 1 );
+ $subgroups->LimitToUserDefinedGroups;
+ $subgroups->Limit( FIELD => 'Name', OPERATOR => '!=', VALUE => $obj->Name );
+
+ if ( $subgroups->Count ) {
+ my $inc = join ", ", map $_->Name, @{$subgroups->ItemsArrayRef};
+</%perl>
+ <span class="subgroups"><&|/l, $inc &>includes [_1]</&></span>\
+<%perl>
+ }
+}
+</%perl>
+ </h3>
+ <& EditRightsCategoryTabs, Context => $Context, Principal => $obj, id => $id &>
+ </div>
+<%perl>
+ }
+}
+
+if ( $AddPrincipal ) {
+</%perl>
+ <div id="acl-AddPrincipal">
+ <h3><&|/l, loc($AddPrincipal) &>Add rights for this [_1]</&></h3>
+ <& EditRightsCategoryTabs, Context => $Context, id => 'acl-AddPrincipal' &>
+ </div>
+% }
+
+</div>
diff --git a/rt/share/html/Admin/Elements/EditRightsCategoryTabs b/rt/share/html/Admin/Elements/EditRightsCategoryTabs
new file mode 100644
index 000000000..786cafdd8
--- /dev/null
+++ b/rt/share/html/Admin/Elements/EditRightsCategoryTabs
@@ -0,0 +1,129 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<%args>
+$Context
+$Principal => undef
+$id
+$acldesc => ''
+</%args>
+<%init>
+# XXX OPTIMIZATION: Moving the calls to AvailableRights and RightCategories up
+# one component to avoid calling them for every principal would be a win, but
+# it's cleaner to do it here. The values can really be computed once per
+# $Context.
+
+# Find all our available rights...
+my (%available_rights, %categories);
+if ( blessed($Context) and $Context->can('AvailableRights') ) {
+ %available_rights = %{$Context->AvailableRights};
+} else {
+ %available_rights = ( loc('System Error') => loc("No rights found") );
+}
+
+# ...and their categories
+if ( blessed($Context) and $Context->can('RightCategories') ) {
+ my %right_categories = %{$Context->RightCategories};
+
+ for my $right (keys %available_rights) {
+ push @{$categories{$right_categories{$right}}}, $right;
+ }
+}
+
+# Find all the current rights for this principal
+my %current_rights;
+if ($Principal) {
+ my $acls = RT::ACL->new($session{'CurrentUser'});
+ $acls->LimitToObject( $Context );
+ $acls->LimitToPrincipal( Id => $Principal->PrincipalId );
+
+ while ( my $ace = $acls->Next ) {
+ my $right = $ace->RightName;
+ $current_rights{$right} = 1;
+ }
+}
+
+my %category_desc = (
+ 'General' => loc('General rights'),
+ 'Staff' => loc('Rights for Staff'),
+ 'Admin' => loc('Rights for Administrators'),
+ 'Status' => loc('Status changes'),
+);
+
+my %catsort = ( General => 1, Staff => 2, Admin => 3, Status => 4 );
+
+$acldesc ||= join '-', ($Principal ? $Principal->PrincipalId : 'addprincipal'),
+ ref($Context), $Context->Id;
+</%init>
+ <div class="category-tabs">
+ <ul>
+% for my $category (sort { $catsort{$a} <=> $catsort{$b} } keys %categories) {
+ <li><a href="#<% "$id-$category" %>"><% $category_desc{$category} || loc('Miscellaneous') %></a></li>
+% }
+ </ul>
+% for my $category (sort { $catsort{$a} <=> $catsort{$b} } keys %categories) {
+ <div id="<% "$id-$category" %>">
+ <ul class="rights-list">
+% for my $right (sort { $available_rights{$a} cmp $available_rights{$b} } @{$categories{$category}}) {
+ <li>
+ <input type="checkbox" class="checkbox"
+ name="SetRights-<% $acldesc %>"
+ id="SetRights-<% $acldesc %>-<% $right %>"
+ value="<% $right %>"
+ <% $current_rights{$right} ? 'checked' : '' %> />
+ <label for="SetRights-<% $acldesc %>-<% $right %>" title="<% loc($right) %>">
+ <% loc($available_rights{$right}) %>
+ <span class="separator">&mdash;</span>
+ <span class="rightname"><% loc($right) %></span>
+ </label>
+ </li>
+% }
+ </ul>
+ </div>
+% }
+ </div>
+ <input type="hidden" name="CheckACL" value="<% $acldesc %>" />
diff --git a/rt/share/html/Admin/Elements/Portal b/rt/share/html/Admin/Elements/Portal
new file mode 100644
index 000000000..d5e75c598
--- /dev/null
+++ b/rt/share/html/Admin/Elements/Portal
@@ -0,0 +1,52 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<div id="rt-portal">
+<&| /Widgets/TitleBox, title => 'RT Portal' &>
+<iframe src="http://bestpractical.com/rt/integration/news?utm_source=rt&utm_medium=iframe&utm_campaign=<%$RT::VERSION%>"></iframe>
+</&>
+</div>
diff --git a/rt/share/html/Admin/Elements/SelectCustomFieldRenderType b/rt/share/html/Admin/Elements/SelectCustomFieldRenderType
new file mode 100644
index 000000000..5034c4dfb
--- /dev/null
+++ b/rt/share/html/Admin/Elements/SelectCustomFieldRenderType
@@ -0,0 +1,70 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<select name="<%$Name%>">
+%for my $option (@types) {
+<option value="<%$option%>"<%$option eq $Default && qq[ selected="selected"] |n%>><% $option %></option>
+%}
+</select>
+<%INIT>
+my $cf = RT::CustomField->new($session{'CurrentUser'});
+$Default ||= $cf->DefaultRenderType($TypeComposite);
+
+my @types = $cf->RenderTypes($TypeComposite);
+
+# XXX: We currently don't support cascaded select CFs when
+# rendering as a list, so don't offer it for now.
+if ( $BasedOn ) {
+ @types = grep { not /List/ } @types;
+}
+</%INIT>
+<%ARGS>
+$Default => undef
+$TypeComposite => 'Select-0'
+$Name => 'RenderType'
+$BasedOn => 0
+</%ARGS>
diff --git a/rt/share/html/Admin/Global/CustomFields/Class-Article.html b/rt/share/html/Admin/Global/CustomFields/Class-Article.html
new file mode 100644
index 000000000..771e9efac
--- /dev/null
+++ b/rt/share/html/Admin/Global/CustomFields/Class-Article.html
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RT::Class', Object => $object, SubType => 'RT::Article' &>
+<%INIT>
+ my $title = loc( 'Edit Custom Fields for articles in all classes');
+ my $object = RT::Class->new($session{'CurrentUser'});
+</%INIT>
diff --git a/rt/share/html/Admin/Global/Topics.html b/rt/share/html/Admin/Global/Topics.html
new file mode 100644
index 000000000..b1a1d9796
--- /dev/null
+++ b/rt/share/html/Admin/Global/Topics.html
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Admin/Articles/Elements/Topics, RootObj => $RT::System, title => $title, %ARGS &>
+
+<%INIT>
+
+my $title = $Modify
+ ? loc("Modify global topics")
+ : loc("Edit global topic hierarchy");
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+$Modify => ""
+</%ARGS>
diff --git a/rt/share/html/Admin/Tools/Queries.html b/rt/share/html/Admin/Tools/Queries.html
new file mode 100644
index 000000000..2fe2d5ac1
--- /dev/null
+++ b/rt/share/html/Admin/Tools/Queries.html
@@ -0,0 +1,129 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<%init>
+my $title = loc('SQL Queries');
+unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
+ Abort(loc('This feature is only available to system administrators.'));
+}
+</%init>
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/jquery.tablesorter.min.js"></script>
+
+<&|/Widgets/TitleBox, title => loc('SQL Queries') &>
+% my $history = $RT::Handle->QueryHistory;
+% if (!RT->Config->Get('StatementLog')) {
+ <p><&|/l&>You must set StatementLog to true to enable this query history page.</&></p>
+% } elsif (!$history) {
+ <p><&|/l&>This server process has recorded no SQL queries.</&></p>
+% } else {
+ <script type="text/javascript">
+ jQuery(function () { jQuery(".tablesorter").tablesorter(); });
+ </script>
+
+ <ol>
+% my $r = 0;
+% for my $request (@$history) {
+% ++$r;
+
+% my ($seconds, $count) = (0, 0);
+% for my $statement (@{ $request->{Queries} }) {
+% $seconds += $statement->[3];
+% $count++;
+% }
+
+ <li>
+ <tt><% $request->{Path} %></tt> - <i><&|/l, sprintf('%.4f', $seconds) &>[_1]s</&></i>
+ <a href="#" onclick="return hideshow('queries-<%$r%>');"><&|/l, $count &>Toggle [quant,_1,query,queries]</&></a>
+ <table id="queries-<%$r%>" class="tablesorter hidden">
+ <thead>
+ <tr>
+ <th><&|/l&>index</&></th>
+ <th><&|/l&>duration</&></th>
+ <th><&|/l&>statement</&></th>
+ </tr>
+ </thead>
+ <tbody>
+% my $s = 0;
+% my @undup;
+% for my $statement (@{ $request->{Queries} }) {
+% my ($dup) = grep {$_->[1] eq $statement->[1]} @undup[-(@undup > 3?3:@undup)..-1];
+% if ($dup) {
+% $dup->[2] = [$dup->[2]] unless $dup->[5];
+% push @{$dup->[2]}, $statement->[2];
+% $dup->[3] += $statement->[3];
+% $dup->[5] ||= 1; $dup->[5]++;
+% } else {
+% push @undup, $statement;
+% }
+% }
+% for my $statement (@undup) {
+% my ( $time, $sql, $bind, $duration, $trace, $dup ) = @$statement;
+% $sql = $RT::Handle->FillIn($sql, $bind) unless $dup;
+ <tr>
+ <td><% ++$s %><% $dup ? " (x $dup)" : "" %></td>
+ <td><i><&|/l, sprintf('%.4f', $duration) &>[_1]s</&></i></td>
+ <td>
+ <tt><% $sql %></tt>
+% if ($dup and @{$bind->[0]}) {
+% for my $b (@{$bind}) {
+ <br><tt>[<% join(", ", @$b) %>]</tt>
+% }
+% }
+ <a class="query-stacktrace-toggle" href="#" onclick="return hideshow('trace-<%$r%>-<%$s%>');"><&|/l &>Toggle stack trace</&></a>
+ <pre id="trace-<%$r%>-<%$s%>" class="hidden"><% $trace %></pre>
+ </td>
+ </tr>
+% }
+ </tbody>
+ </table>
+ </li>
+% }
+ </ol>
+% }
+</&>
diff --git a/rt/share/html/Admin/Tools/Theme.html b/rt/share/html/Admin/Tools/Theme.html
new file mode 100644
index 000000000..11888cac5
--- /dev/null
+++ b/rt/share/html/Admin/Tools/Theme.html
@@ -0,0 +1,309 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+<& /Admin/Elements/Header,
+ Title => loc("Theme"),
+&>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/farbtastic.js"></script>
+
+<div id="simple-customize">
+<div id="upload-logo">
+ <h2>Logo</h2>
+ <& /Elements/Logo, id => 'logo-theme-editor', ShowName => 0 &>
+ <form method="POST" enctype="multipart/form-data">
+ <label for="logo-upload"><&|/l&>Upload a new logo</&>:</label>
+ <input type="file" name="logo-upload" id="logo-upload" /><br />
+ <div class="gd-support">
+% if (%gd_can) {
+ <&|/l, $valid_image_types &>Your system supports automatic color suggestions for: [_1]</&>
+% } else {
+ <&|/l&>GD is disabled or not installed. You can upload an image, but you won't get automatic color suggestions.</&>
+% }
+ </div>
+ <input name="reset_logo" value="Reset to default RT Logo" type="submit" />
+ <input type="submit" value="Upload" />
+ </form>
+</div>
+
+<div id="customize-theme">
+ <h2>Customize the RT theme</h2>
+ <ol>
+ <li>
+ <label for="section"><&|/l&>Select a section</&>:</label>
+ <select id="section"></select>
+ </li>
+ <li>
+ <div class="description"><&|/l&>Select a color for the section</&>:</div>
+% if ($colors) {
+<div class="primary-colors">
+% for (@$colors) {
+% my $fg = $_->{l} >= $text_threshold ? 'black' : 'white';
+<button type="button" class="color-template"
+ style="background-color: rgb(<% $_->{c} %>); color: <% $fg %>;">
+ <&|/l&>Text</&>
+</button>
+% }
+</div>
+% }
+ <div id="color-picker"></div>
+ </li>
+ </ol>
+</div>
+</div>
+
+<div id="custom-css">
+ <h2>Custom CSS (Advanced)</h2>
+
+ <form method="POST">
+ <textarea rows=20 id="user_css" name="user_css" wrap="off"><% $user_css %></textarea><br />
+ <input id="try" type="button" class="button" value="Try" />
+ <input id="reset" type="reset" value="Reset" type="submit" />
+ <input name="reset_css" value="Reset to default RT Theme" type="submit" />
+ <input value="Save" type="submit" />
+ </form>
+</div>
+
+<%ONCE>
+my @sections = (
+ ['Page' => ['body']],
+ ['Header' => ['div#quickbar', 'body.aileron #main-navigation #app-nav > li, body.aileron #main-navigation #app-nav > li > a, #prefs-menu > li, #prefs-menu > li > a, #logo .rtname']],
+ ['Page title' => ['div#header h1']],
+ ['Page content' => ['div#body']],
+ ['Buttons' => ['input[type="reset"], input[type="submit"], input[class="button"]']],
+ ['Button hover' => ['input[type="reset"]:hover, input[type="submit"]:hover, input[class="button"]:hover']],
+);
+</%ONCE>
+<script type="text/javascript">
+var section_css_mapping = <% JSON(\@sections) |n%>;
+
+jQuery(function($) {
+
+ jQuery.each(section_css_mapping, function(i,v){
+ $('select#section').append($("<option/>")
+ .attr('value', v[0])
+ .text(v[0]));
+ });
+
+ $("style#sitecss").text($('#user_css').val());
+ $('#try').click(function() {
+ $("style#sitecss").text($('#user_css').val());
+ });
+
+ $('#reset').click(function() {
+ setTimeout(function() {
+ $("style#sitecss").text($('#user_css').val());
+ }, 1000);
+ });
+
+ function change_color(bg, fg) {
+ var section = $('select#section').val();
+
+ var applying = jQuery.grep(section_css_mapping, function(a){ return a[0] == section })[0][1];
+ var css = $('#user_css').val();
+ if (applying) {
+ var specials = new RegExp("([.*+?|()\\[\\]{}\\\\])", "g");
+ for (var name in applying) {
+ var selector = (applying[name]).replace(specials, "\\$1");
+ var rule = new RegExp('^'+selector+'\\s*\{.*?\}', "m");
+ var newcss = "background: " + bg;
+
+ /* Don't set the text color on <body> as it affects too much */
+ if (applying[name] != "body")
+ newcss += "; color: " + fg;
+
+ /* Kill the border on the quickbar if we're styling it */
+ if (applying[name].match(/quickbar/))
+ newcss += "; border: none;"
+
+ /* Page title's text color is the selected color */
+ if (applying[name].match(/#header/))
+ newcss = "color: " + bg;
+
+ /* Nav doesn't need a background, but it wants text color */
+ if (applying[name].match(/#main-navigation/))
+ newcss = "color: " + fg;
+
+ css = css.replace(rule, applying[name]+" { "+newcss+" }");
+ }
+ }
+ $('#user_css').val(css);
+ $("style#sitecss").text(css);
+ }
+
+ $('#color-picker').farbtastic(function(color){ change_color(color, this.hsl[2] > <% $text_threshold %> ? '#000' : '#fff') });
+
+ $('button.color-template').click(function() {
+ change_color($(this).css('background-color'), $(this).css('color'));
+ });
+
+
+});
+</script>
+<%INIT>
+unless ($session{'CurrentUser'}->HasRight( Object=> RT->System, Right => 'SuperUser')) {
+ Abort(loc('This feature is only available to system administrators.'));
+}
+
+use Digest::MD5 'md5_hex';
+
+my $text_threshold = 0.6;
+my @results;
+my $imgdata;
+
+if (my $file_hash = _UploadedFile( 'logo-upload' )) {
+ my ($id, $msg) = RT->System->SetAttribute( Name => "UserLogo",
+ Description => "User-provided logo",
+ Content => {
+ type => $file_hash->{ContentType},
+ data => $file_hash->{LargeContent},
+ hash => md5_hex($file_hash->{LargeContent}),
+ } );
+ push @results, loc("Unable to set UserLogo: [_1]", $msg) unless $id;
+
+ $imgdata = $file_hash->{LargeContent};
+}
+elsif ($ARGS{'reset_logo'}) {
+ RT->System->DeleteAttribute('UserLogo');
+}
+else {
+ if (my $attr = RT->System->FirstAttribute('UserLogo')) {
+ my $content = $attr->Content;
+ if (ref($content) eq 'HASH') {
+ $imgdata = $content->{data};
+ }
+ else {
+ RT->System->DeleteAttribute('UserLogo');
+ }
+ }
+}
+
+if ($user_css) {
+ if ($ARGS{'reset_css'}) {
+ RT->System->DeleteAttribute('UserCSS');
+ undef $user_css;
+ }
+ else {
+ my ($id, $msg) = RT->System->SetAttribute( Name => "UserCSS",
+ Description => "User-provided css",
+ Content => $user_css );
+ push @results, loc("Unable to set UserCSS: [_1]", $msg) unless $id;
+ }
+}
+
+if (!$user_css) {
+ my $attr = RT->System->FirstAttribute('UserCSS');
+ $user_css = $attr ? $attr->Content : join(
+ "\n\n" => map {
+ join "\n" => "/* ". $_->[0] ." */",
+ map { "$_ {}" } @{$_->[1]}
+ } @sections
+ );
+}
+
+# XXX: move this to some other modules
+
+use List::MoreUtils qw(uniq);
+
+my $has_color_analyzer = eval { require Convert::Color; 1 };
+my $colors;
+my %gd_can;
+my $valid_image_types;
+
+if (not RT->Config->Get('DisableGD') and $has_color_analyzer) {
+ require GD;
+
+ # Always find out what GD can read...
+ for my $type (qw(Png Jpeg Gif)) {
+ $gd_can{$type}++ if GD::Image->can("newFrom${type}Data");
+ }
+ $valid_image_types = join(", ", map { uc } sort { lc $a cmp lc $b } keys %gd_can);
+
+ # ...but only analyze the image if we have data
+ if ($imgdata) {
+ if ( my $img = GD::Image->new($imgdata) ) {
+ $colors = analyze_img($img);
+ }
+ else {
+ # This has to be one damn long line because the loc() needs to be
+ # source parsed correctly.
+ push @results, loc("Automatically suggested theme colors aren't available for your image. This might be because you uploaded an image type that your installed version of GD doesn't support. Supported types are: [_1]. You can recompile libgd and GD.pm to include support for other image types.", $valid_image_types);
+ }
+ }
+}
+
+sub analyze_img {
+ my $img = shift;
+ my $color;
+
+ for my $i (0..$img->width-1) {
+ for my $j (0..$img->height-1) {
+ my @color = $img->rgb( $img->getPixel($i,$j) );
+ my $hsl = Convert::Color->new('rgb:'.join(',',map { $_ / 255 } @color))->convert_to('hsl');
+ my $c = join(',',@color);
+ next if $hsl->lightness < 0.1;
+ $color->{$c} ||= { h => $hsl->hue, s => $hsl->saturation, l => $hsl->lightness, cnt => 0, c => $c};
+ $color->{$c}->{cnt}++;
+ }
+ }
+
+ for (values %$color) {
+ $_->{rank} = $_->{s} * $_->{cnt};
+ }
+ my @top5 = grep { defined and $_->{'l'} and $_->{'c'} }
+ (sort { $b->{rank} <=> $a->{rank} } values %$color)[0..5];
+ if ((scalar uniq map {$_->{rank}} @top5) == 1) {
+ warn "bad";
+ }
+ return \@top5;
+}
+</%INIT>
+<%ARGS>
+$user_css => ''
+</%ARGS>