%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC %# %# %# (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 &>

Customize the RT theme

  1. <&|/l&>Select a color for the section:
    % if ($colors) {
    % for (@$colors) { % my $fg = $_->{l} >= $text_threshold ? 'black' : 'white'; % }
    % }

Custom CSS (Advanced)


<%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']], ); <%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; } <%ARGS> $user_css => ''