X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Fshare%2Fhtml%2FAdmin%2FTools%2FTheme.html;h=e548b1130e3ea535918e769197577da7beca13f4;hb=187086c479a09629b7d180eec513fb7657f4e291;hp=11888cac575394c04b03511129905caf5b7db991;hpb=f3c4966ed1f6ec3db7accd6dcdd3a5a3821d72a7;p=freeside.git diff --git a/rt/share/html/Admin/Tools/Theme.html b/rt/share/html/Admin/Tools/Theme.html index 11888cac5..e548b1130 100644 --- a/rt/share/html/Admin/Tools/Theme.html +++ b/rt/share/html/Admin/Tools/Theme.html @@ -2,7 +2,7 @@ %# %# COPYRIGHT: %# -%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +%# This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) @@ -51,7 +51,7 @@ <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &> - +
-

Customize the RT theme

+

<&|/l&>Customize the RT theme

  1. -
    <&|/l&>Select a color for the section:
    +
    + <&|/l&>Select a color for the section: + +
    % if ($colors) {
    % for (@$colors) { @@ -93,27 +98,29 @@
    % }
    +
-

Custom CSS (Advanced)

- +

<&|/l&>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' => ['body', 'div#body']], + ['Menu bar' => ['div#quickbar', '#main-navigation #app-nav.sf-shadow > li, #main-navigation #app-nav.sf-shadow > li > a, #prefs-menu > li, #prefs-menu > li > a, #logo .rtname']], + ['Title bar' => ['div#header']], ['Page title' => ['div#header h1']], ['Page content' => ['div#body']], ['Buttons' => ['input[type="reset"], input[type="submit"], input[class="button"]']], @@ -131,14 +138,24 @@ jQuery(function($) { .text(v[0])); }); - $("style#sitecss").text($('#user_css').val()); + function update_sitecss(text) { + if (!text) + text = $('#user_css').val(); + + // IE 8 doesn't let us update the innerHTML of ").appendTo('head'); + } + + update_sitecss(); $('#try').click(function() { - $("style#sitecss").text($('#user_css').val()); + update_sitecss(); }); $('#reset').click(function() { setTimeout(function() { - $("style#sitecss").text($('#user_css').val()); + update_sitecss(); }, 1000); }); @@ -163,7 +180,7 @@ jQuery(function($) { newcss += "; border: none;" /* Page title's text color is the selected color */ - if (applying[name].match(/#header/)) + if (applying[name].match(/h1/)) newcss = "color: " + bg; /* Nav doesn't need a background, but it wants text color */ @@ -174,7 +191,7 @@ jQuery(function($) { } } $('#user_css').val(css); - $("style#sitecss").text(css); + update_sitecss(css); } $('#color-picker').farbtastic(function(color){ change_color(color, this.hsl[2] > <% $text_threshold %> ? '#000' : '#fff') }); @@ -183,7 +200,35 @@ jQuery(function($) { change_color($(this).css('background-color'), $(this).css('color')); }); - + // Setup the canvas color picker + $("#logo-theme-editor img").load(function() { + var logo = $(this); + var canvas = $("#logo-color-picker"); + var el_canvas = canvas.get(0); + + if (!el_canvas.getContext) return; + + var context = el_canvas.getContext("2d"); + el_canvas.width = logo.width(); + el_canvas.height = logo.height(); + context.drawImage(logo.get(0), 0, 0); + + logo.hide().after(canvas); + canvas.show().click(function(ev) { + ev.preventDefault(); + var R = 0, + G = 1, + B = 2, + A = 3; + var pixel = this.getContext("2d").getImageData(ev.offsetX, ev.offsetY, 1, 1).data; + // Farbtastic expects values in the range of 0..1 + var rgba = $.makeArray(pixel).map(function(v,i) { return v / 255 }); + var wheel = $.farbtastic("#color-picker"); + wheel.setHSL( wheel.RGBToHSL( rgba.slice(R,A) ) ); + // XXX TODO factor in the alpha channel too + }); + $('#logo-picker-hint').show(); + }); }); <%INIT> @@ -197,14 +242,82 @@ my $text_threshold = 0.6; my @results; my $imgdata; +my $colors; +my $valid_image_types; +if (not RT->Config->Get('DisableGD') and Convert::Color->require) { + require GD; + + # Always find out what GD can read... + my %gd_can; + 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); +} + +my $analyze_img = sub { + return undef unless $valid_image_types; + + my $imgdata = shift; + return undef unless $imgdata; + + # ...but only analyze the image if we have data + my $img = GD::Image->new($imgdata); + unless ($img) { + # 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); + return undef; + } + + my %colors; + + my @wsamples; + my @hsamples; + if ($img->width > 200) { + @wsamples = map { int($img->width*($_/200)) } (0..199); + } else { + @wsamples = ( 0 .. $img->width - 1 ); + } + if ($img->height > 200) { + @hsamples = map { int($img->height*($_/200)) } (0..199); + } else { + @hsamples = ( 0 .. $img->height - 1 ); + } + for my $i (@wsamples) { + for my $j (@hsamples) { + 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; + $colors{$c} ||= { h => $hsl->hue, s => $hsl->saturation, l => $hsl->lightness, cnt => 0, c => $c}; + $colors{$c}->{cnt}++; + } + } + + for (values %colors) { + $_->{rank} = $_->{s} * $_->{cnt}; + } + my @top5 = grep { defined and $_->{'l'} and $_->{'c'} } + (sort { $b->{rank} <=> $a->{rank} } values %colors)[0..5]; + return \@top5; +}; + 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}), - } ); + $colors = $analyze_img->($file_hash->{LargeContent}); + + my $my_system = RT::System->new( $session{CurrentUser} ); + my ( $id, $msg ) = $my_system->SetAttribute( + Name => "UserLogo", + Description => "User-provided logo", + Content => { + type => $file_hash->{ContentType}, + data => $file_hash->{LargeContent}, + hash => md5_hex($file_hash->{LargeContent}), + colors => $colors, + }, + ); + push @results, loc("Unable to set UserLogo: [_1]", $msg) unless $id; $imgdata = $file_hash->{LargeContent}; @@ -217,6 +330,19 @@ else { my $content = $attr->Content; if (ref($content) eq 'HASH') { $imgdata = $content->{data}; + $colors = $content->{colors}; + unless ($colors) { + # No colors cached; attempt to generate them + $colors = $content->{colors} = $analyze_img->($content->{data}); + if ($content->{colors}) { + # Found colors; update the attribute + RT->System->SetAttribute( + Name => "UserLogo", + Description => "User-provided logo", + Content => $content, + ); + } + } } else { RT->System->DeleteAttribute('UserLogo'); @@ -246,63 +372,6 @@ if (!$user_css) { } @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 => ''