summaryrefslogtreecommitdiff
path: root/rt/share/html/Admin/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'rt/share/html/Admin/Tools')
-rw-r--r--rt/share/html/Admin/Tools/Configuration.html68
-rw-r--r--rt/share/html/Admin/Tools/Queries.html129
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Dumps/dhandler7
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/DumpFileLink2
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/Error/NoRights8
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage11
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment7
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket4
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User4
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/ObjectCheckBox2
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/PluginArguments4
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/PluginHelp4
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/SelectObjects2
-rw-r--r--rt/share/html/Admin/Tools/Shredder/Elements/SelectPlugin4
-rw-r--r--rt/share/html/Admin/Tools/Shredder/autohandler2
-rw-r--r--rt/share/html/Admin/Tools/Shredder/index.html16
-rw-r--r--rt/share/html/Admin/Tools/Theme.html309
-rw-r--r--rt/share/html/Admin/Tools/index.html13
18 files changed, 523 insertions, 73 deletions
diff --git a/rt/share/html/Admin/Tools/Configuration.html b/rt/share/html/Admin/Tools/Configuration.html
index f4c648a11..ed7d4651b 100644
--- a/rt/share/html/Admin/Tools/Configuration.html
+++ b/rt/share/html/Admin/Tools/Configuration.html
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -53,10 +53,7 @@ unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'Super
}
</%init>
<& /Admin/Elements/Header, Title => $title &>
-<& /Admin/Elements/ToolTabs,
- current_tab => 'Admin/Tools/Configuration.html',
- current_subtab => 'Admin/Tools/Configuration.html',
- Title => $title &>
+<& /Elements/Tabs &>
<&|/Widgets/TitleBox, title => loc("RT Configuration") &>
<table border="0" cellspacing="0" cellpadding="5" width="100%" class="collection">
@@ -68,7 +65,7 @@ unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'Super
<%PERL>
my $index_conf;
foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
- my $val = RT->Config->Get( $key );
+ my $val = RT->Config->GetObfuscated( $key );
next unless defined $val;
my $meta = RT->Config->Meta( $key );
@@ -91,19 +88,13 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
<td class="collection-as-table"><% $key %></td>
<td class="collection-as-table">
% if ( $key =~ /Password(?!Length)/i ) {
-<em>Password not printed</em>\
-% } elsif ( !ref $val ) {
-<% "$val" %>\
-% } elsif ( ref $val eq 'ARRAY' ) {
-<% join ', ', @$val %>\
-% } elsif ( ref $val eq 'HASH' ) {
-<% join ', ', %$val %>\
+<em><% loc('Password not printed' ) %></em>\
% } else {
-<% ref $val %>\
+<% stringify($val) |n %>\
% }
</td>
<td class="collection-as-table" style="white-space: nowrap">
-% if ( $description =~ /^.*site config$/ ) {
+% if ( $meta->{'Source'}{'SiteConfig'} ) {
<span style="font-weight: bold"><% $description %></span>
% } else {
<% $description %>
@@ -134,7 +125,7 @@ foreach my $key ( sort keys %{*RT::} ) {
<td class="collection-as-table">RT::<% $key %></td>
<td class="collection-as-table">
% if ( $key =~ /Password(?!Length)/i ) {
-<em>Password not printed</em>
+<em><% loc('Password not printed' ) %></em>\
% } else {
<% ${'RT::'.$key} %>
% }
@@ -157,7 +148,7 @@ for my $type (qw/Tickets Queues Transactions Groups PrivilegedUsers Unprivileged
my $count;
my $class = 'RT::' . $type;
$class =~ s/Privileged|Unprivileged//;
- my $collection = $class->new($RT::SystemUser);
+ my $collection = $class->new(RT->SystemUser);
$collection->UnLimit;
if ($type =~ /PrivilegedUsers/) {
$user_count = $collection->CountAll;
@@ -182,7 +173,7 @@ for my $type (qw/Tickets Queues Transactions Groups PrivilegedUsers Unprivileged
<&|/Widgets/TitleBox, title => loc("Mason template search order") &>
<ol>
-% foreach my $path ( map { $_->[1] } $m->interp->comp_root_array ) {
+% foreach my $path ( RT::Interface::Web->ComponentRoots ) {
<li><% $path %></li>
% }
</ol>
@@ -199,6 +190,30 @@ for my $type (qw/Tickets Queues Transactions Groups PrivilegedUsers Unprivileged
</td>
</table>
+<&|/Widgets/TitleBox, title => loc("Global Attributes") &>
+<table border="0" cellspacing="0" cellpadding="5" width="100%" class="collection">
+<tr class="collection-as-table">
+<th class="collection-as-table"><&|/l&>Name</&></th>
+<th class="collection-as-table"><&|/l&>Value</&></th>
+</tr>
+% my $attrs = $RT::System->Attributes;
+% my $index_size = 0;
+% while ( my $attr = $attrs->Next ) {
+<tr class="<% $index_size%2 ? 'oddline' : 'evenline'%>">
+% if ($attr->Name eq 'UserLogo') {
+% my $content = $attr->Content;
+% $content->{data} = defined $content->{data} ? 'DATA' : 'undef'
+% if exists $content->{data};
+<td><% $attr->Name %></td><td><% stringify($content) |n %></td>
+% } else {
+<td><% $attr->Name %></td><td><% stringify($attr->Content) |n %></td>
+% }
+</tr>
+% $index_size++;
+% }
+</table>
+</&>
+
<&|/Widgets/TitleBox, title => loc("Loaded perl modules")&>
<table border="0" cellspacing="0" cellpadding="5" width="100%" class="collection">
<tr class="collection-as-table">
@@ -227,7 +242,7 @@ if ($item =~ /^\s*(.*?)\s*v(\S+);/) {
<%$ver%>
</td>
<td class="collection-as-table">
- <% $INC{$distfile} %>
+ <% $INC{$distfile} || '' %>
</td>
</tr>
% }
@@ -240,3 +255,18 @@ if ($item =~ /^\s*(.*?)\s*v(\S+);/) {
<% Config::myconfig() %>
</pre>
</&>
+
+<%INIT>
+use Data::Dumper;
+local $Data::Dumper::Terse = 1;
+local $Data::Dumper::Indent = 2;
+
+sub stringify {
+ my $value = shift;
+ my $output = Dumper $value;
+ RT::Interface::Web::EscapeUTF8(\$output);
+ $output =~ s/ /&nbsp;/g;
+ $output =~ s!\n!<br />!g;
+ return $output;
+}
+</%INIT>
diff --git a/rt/share/html/Admin/Tools/Queries.html b/rt/share/html/Admin/Tools/Queries.html
new file mode 100644
index 000000000..dbc6fc5fe
--- /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" |n,j%>);"><&|/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" |n,j%>);"><&|/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/Shredder/Dumps/dhandler b/rt/share/html/Admin/Tools/Shredder/Dumps/dhandler
index e742001dd..0d24fa0af 100644
--- a/rt/share/html/Admin/Tools/Shredder/Dumps/dhandler
+++ b/rt/share/html/Admin/Tools/Shredder/Dumps/dhandler
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -48,9 +48,6 @@
<%ATTR>
AutoFlush => 0
</%ATTR>
-<%FLAGS>
-inherit => undef
-</%FLAGS>
<%INIT>
my $arg = $m->dhandler_arg;
$m->abort(404) if $arg =~ m{\.\.|/|\\};
@@ -64,5 +61,5 @@ my $buf;
while( read $fh, $buf, 1024*1024 ) {
$m->out($buf);
}
-return 0;
+$m->abort;
</%INIT>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/DumpFileLink b/rt/share/html/Admin/Tools/Shredder/Elements/DumpFileLink
index 0f96b4348..5690377cd 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/DumpFileLink
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/DumpFileLink
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoRights b/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoRights
index 0d4e572ad..429313755 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoRights
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoRights
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -46,10 +46,6 @@
%#
%# END BPS TAGGED BLOCK }}}
<& /Admin/Elements/Header, Title => 'Error' &>
-<& /Admin/Elements/ToolTabs,
- current_tab => 'Admin/Tools/Shredder',
- current_subtab => 'Admin/Tools/Shredder',
- Title => 'Error',
-&>
+<& /Elements/Tabs &>
<div class="error"><% loc("You don't have <b>SuperUser</b> right.") |n%></div>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage b/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage
index bae4685b0..ae3b96e9b 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -49,11 +49,8 @@
$Path => ''
</%ARGS>
<& /Admin/Elements/Header, Title => 'Error' &>
-<& /Admin/Elements/ToolTabs,
- current_tab => 'Admin/Tools/Shredder',
- current_subtab => 'Admin/Tools/Shredder',
- Title => 'Error',
-&>
+<& /Elements/Tabs &>
<div class="error">
-<% loc('Shredder needs a directory to write dumps to. Please check that you have <span class="file-path">[_1]</span> and it is writable by your web server.', $m->interp->apply_escapes( $Path ) ) |n%>
+% my $path_tag = q{<span class="file-path">} . $m->interp->apply_escapes($Path, 'h') . q{</span>};
+<&|/l_unsafe, $path_tag &>Shredder needs a directory to write dumps to. Please ensure that the directory [_1] exists and that it is writable by your web server.</&>
</div>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment
index 02ef90b3e..0da910d77 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -48,6 +48,7 @@
<%ARGS>
$Object => undef
</%ARGS>
-<a href="<% RT->Config->Get('WebURL') %>/Ticket/Attachment/<% $Object->TransactionId %>/<% $Object->id %>/">
-<% loc('Attachment') %>(<% loc('id') %>:<% $Object->id %>, <% loc('FileName') %>: <% $Object->Filename || loc('(no value)') %>)
+% my $name = (defined $Object->Filename and length $Object->Filename) ? $Object->Filename : loc("(no value)");
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Attachment/<% $Object->TransactionId %>/<% $Object->id %>/">
+<% loc('Attachment') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Filename') %>: <% $name %>)
</a>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket
index ec052d642..35f1aa8d8 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -48,6 +48,6 @@
<%ARGS>
$Object => undef
</%ARGS>
-<a href="<% RT->Config->Get('WebURL') %>/Ticket/Display.html?id=<% $Object->id %>">
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Display.html?id=<% $Object->id %>">
<% loc('Ticket') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Subject') %>: <% substr($Object->Subject, 0, 30) %>...)
</a>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User
index 125f4f2c8..d7627eb14 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -48,6 +48,6 @@
<%ARGS>
$Object => undef
</%ARGS>
-<a href="<% RT->Config->Get('WebURL') %>/Admin/Users/Modify.html?id=<% $Object->id %>">
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Users/Modify.html?id=<% $Object->id %>">
<% loc('User') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Name') %>: <% $Object->Name %>)
</a>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/ObjectCheckBox b/rt/share/html/Admin/Tools/Shredder/Elements/ObjectCheckBox
index e14c5c5a8..3c2e1e7b6 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/ObjectCheckBox
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/ObjectCheckBox
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/PluginArguments b/rt/share/html/Admin/Tools/Shredder/Elements/PluginArguments
index 67b46ad9d..d9926af9c 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/PluginArguments
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/PluginArguments
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -56,7 +56,7 @@ $Plugin => ''
</div>
<%INIT>
use RT::Shredder::Plugin;
-my $plugin_obj = new RT::Shredder::Plugin;
+my $plugin_obj = RT::Shredder::Plugin->new;
my ($status, $msg) = $plugin_obj->LoadByName( $Plugin );
die $msg unless $status;
</%INIT>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/PluginHelp b/rt/share/html/Admin/Tools/Shredder/Elements/PluginHelp
index daaa6b74a..7719ec027 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/PluginHelp
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/PluginHelp
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -53,7 +53,7 @@ $Plugin => ''
</div>
<%ONCE>
use RT::Shredder::Plugin;
-my $plugin_obj = new RT::Shredder::Plugin;
+my $plugin_obj = RT::Shredder::Plugin->new;
my %plugins = $plugin_obj->List;
</%ONCE>
<%INIT>
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/SelectObjects b/rt/share/html/Admin/Tools/Shredder/Elements/SelectObjects
index 2488b73f4..7bae913ae 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/SelectObjects
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/SelectObjects
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/share/html/Admin/Tools/Shredder/Elements/SelectPlugin b/rt/share/html/Admin/Tools/Shredder/Elements/SelectPlugin
index 3835e0b99..e55f60eb0 100644
--- a/rt/share/html/Admin/Tools/Shredder/Elements/SelectPlugin
+++ b/rt/share/html/Admin/Tools/Shredder/Elements/SelectPlugin
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -68,6 +68,6 @@ $Plugin => ''
</div>
<%ONCE>
use RT::Shredder::Plugin;
-my $plugin_obj = new RT::Shredder::Plugin;
+my $plugin_obj = RT::Shredder::Plugin->new;
my %plugins = $plugin_obj->List('Search');
</%ONCE>
diff --git a/rt/share/html/Admin/Tools/Shredder/autohandler b/rt/share/html/Admin/Tools/Shredder/autohandler
index e7f31b575..955206420 100644
--- a/rt/share/html/Admin/Tools/Shredder/autohandler
+++ b/rt/share/html/Admin/Tools/Shredder/autohandler
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/share/html/Admin/Tools/Shredder/index.html b/rt/share/html/Admin/Tools/Shredder/index.html
index 8cea3e42c..81b99ec19 100644
--- a/rt/share/html/Admin/Tools/Shredder/index.html
+++ b/rt/share/html/Admin/Tools/Shredder/index.html
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -52,11 +52,7 @@ $Wipeout => ''
@WipeoutObject => ()
</%ARGS>
<& /Admin/Elements/Header, Title => $title &>
-<& /Admin/Elements/ToolTabs,
- current_tab => 'Admin/Tools/Shredder',
- current_subtab => 'Admin/Tools/Shredder',
- Title => $title,
-&>
+<& /Elements/Tabs &>
<form id="shredder-search-form" action="<% RT->Config->Get('WebPath') %>/Admin/Tools/Shredder/" method="GET">
<div id="shredder-select-plugin">
<& /Elements/ListActions, actions => $messages{'Errors'} &>
@@ -96,7 +92,7 @@ my $catch_non_fatals = sub {
if( $Plugin ) { { # use additional block({}) to effectively exit block on errors
use RT::Shredder::Plugin;
- $plugin_obj = new RT::Shredder::Plugin;
+ $plugin_obj = RT::Shredder::Plugin->new;
my( $status, $msg ) = $plugin_obj->LoadByName( $Plugin );
unless( $status ) {
push @{ $messages{Errors} }, $msg;
@@ -128,8 +124,8 @@ if( $Plugin ) { { # use additional block({}) to effectively exit block on errors
my $dump_file = '';
if( $Plugin && $Wipeout ) { { # use additional block({}) to effectively exit block on errors
- my $shredder = new RT::Shredder( force => 1 );
- my $backup_plugin = new RT::Shredder::Plugin;
+ my $shredder = RT::Shredder->new( force => 1 );
+ my $backup_plugin = RT::Shredder::Plugin->new;
my ($status, $msg) = $backup_plugin->LoadByName('SQLDump');
unless( $status ) {
push @{ $messages{Errors} }, $msg;
@@ -172,7 +168,7 @@ if( $Plugin && ( $Search || $Wipeout ) ) { { # use additional block({}) to effec
}
push @{ $messages{Success} }, loc('executed plugin successfuly');
- my $shredder = new RT::Shredder;
+ my $shredder = RT::Shredder->new;
foreach my $o( grep defined, splice @objs ) {
eval { push @objs, $shredder->CastObjectsToRecords( Objects => $o ) };
$catch_non_fatals->() && last if $@;
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>
diff --git a/rt/share/html/Admin/Tools/index.html b/rt/share/html/Admin/Tools/index.html
index 506385bdf..88612704a 100644
--- a/rt/share/html/Admin/Tools/index.html
+++ b/rt/share/html/Admin/Tools/index.html
@@ -2,7 +2,7 @@
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
@@ -45,11 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<%init>
-my $title = loc('System Tools');
-</%init>
-<& /Admin/Elements/Header, Title => $title &>
-<& /Admin/Elements/ToolTabs,
- current_tab => 'Admin/Tools/index.html',
- current_subtab => 'Admin/Tools/Configuration.html',
- Title => $title &>
+<& /Admin/Elements/Header, Title => loc('System Tools') &>
+<& /Elements/Tabs &>
+<& /Elements/ListMenu, menu => Menu()->child('tools')->child('config')->child('tools') &>