diff options
Diffstat (limited to 'rt/share/html/Search')
-rwxr-xr-x | rt/share/html/Search/Bulk.html | 4 | ||||
-rw-r--r-- | rt/share/html/Search/Chart | 136 | ||||
-rw-r--r-- | rt/share/html/Search/Elements/Chart | 34 | ||||
-rw-r--r-- | rt/share/html/Search/Elements/ResultsRSSView | 143 | ||||
-rwxr-xr-x | rt/share/html/Search/Results.html | 11 | ||||
-rw-r--r-- | rt/share/html/Search/Results.rdf | 57 | ||||
-rw-r--r-- | rt/share/html/Search/Results.tsv | 2 |
7 files changed, 253 insertions, 134 deletions
diff --git a/rt/share/html/Search/Bulk.html b/rt/share/html/Search/Bulk.html index fa20e331c..f2ca45c26 100755 --- a/rt/share/html/Search/Bulk.html +++ b/rt/share/html/Search/Bulk.html @@ -163,7 +163,9 @@ <input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" /> <input type="hidden" class="hidden" name="UpdateAttach" value="1" /></td></tr> <tr><td class="labeltop"><&|/l&>Message</&>:</td><td> - <& /Elements/MessageBox, Name=>"UpdateContent"&> +%# Currently, bulk update always starts with Comment not Reply selected, so we check this unconditionally +% my $IncludeSignature = RT->Config->Get('MessageBoxIncludeSignatureOnComment'); +<& /Elements/MessageBox, Name => "UpdateContent", IncludeSignature => $IncludeSignature &> </td></tr> </table> diff --git a/rt/share/html/Search/Chart b/rt/share/html/Search/Chart index 59e9fc6fc..abee3e52a 100644 --- a/rt/share/html/Search/Chart +++ b/rt/share/html/Search/Chart @@ -66,52 +66,11 @@ if ($ChartStyle eq 'pie') { use RT::Report::Tickets; my $tix = RT::Report::Tickets->new( $session{'CurrentUser'} ); + my ($count_name, $value_name) = $tix->SetupGroupings( Query => $Query, GroupBy => $PrimaryGroupBy, ); -my $chart = $chart_class->new( 600 => 400 ); - -my $font = RT->Config->Get('ChartFont') || ['verdana', 'arial', gdMediumBoldFont]; -$chart->set_title_font( $font, 12 ) if $chart->can('set_title_font'); -$chart->set_legend_font( $font, 12 ) if $chart->can('set_legend_font'); -$chart->set_x_label_font( $font, 10 ) if $chart->can('set_x_label_font'); -$chart->set_y_label_font( $font, 10 ) if $chart->can('set_y_label_font'); -$chart->set_label_font( $font, 10 ) if $chart->can('set_label_font'); -$chart->set_x_axis_font( $font, 9 ) if $chart->can('set_x_axis_font'); -$chart->set_y_axis_font( $font, 9 ) if $chart->can('set_y_axis_font'); -$chart->set_values_font( $font, 9 ) if $chart->can('set_values_font'); -$chart->set_value_font( $font, 9 ) if $chart->can('set_value_font'); - -# Pie charts don't like having no input, so we show a special image -# that indicates an error message. Because this is used in an <img> -# context, it can't be a simple error message. Without this check, -# the chart will just be a non-loading image. -if ($tix->Count == 0) { - my $plot = GD::Image->new(600 => 400); - $plot->colorAllocate(255, 255, 255); # background - my $black = $plot->colorAllocate(0, 0, 0); - - require GD::Text::Wrap; - my $error = GD::Text::Wrap->new($plot, - color => $black, - text => loc("No tickets found."), - ); - $error->set_font( $font, 12 ); - $error->draw(0, 0); - - $m->comp( 'SELF:Plot', plot => $plot, %ARGS ); -} - -if ($chart_class eq "GD::Graph::bars") { - $chart->set( - x_label => $tix->Label( $PrimaryGroupBy ), - x_labels_vertical => 1, - y_label => loc('Tickets'), - show_values => 1 - ); -} - my %class = ( Queue => 'RT::Queue', Owner => 'RT::User', @@ -121,15 +80,17 @@ my %class = ( my $class = $class{ $PrimaryGroupBy }; my %data; +my $max_value = 0; +my $max_key_length = 0; while ( my $entry = $tix->Next ) { my $key; if ( $class ) { my $q = $class->new( $session{'CurrentUser'} ); - $q->Load( $entry->__Value( $value_name ) ); + $q->Load( $entry->LabelValue( $value_name ) ); $key = $q->Name; } else { - $key = $entry->__Value($value_name); + $key = $entry->LabelValue($value_name); } $key ||= '(no value)'; @@ -140,26 +101,84 @@ while ( my $entry = $tix->Next ) { $key = loc($key); } $data{ $key } = $value; -} - -# XXX: Convert 1970-01-01 date to the 'Not Set' -# this code should be generalized!!! -if ( $PrimaryGroupBy =~ /(Daily|Monthly|Annually)$/ ) { - my $re; - $re = qr{1970-01-01} if $PrimaryGroupBy =~ /Daily$/; - $re = qr{1970-01} if $PrimaryGroupBy =~ /Monthly$/; - $re = qr{1970} if $PrimaryGroupBy =~ /Annually$/; - foreach my $k (keys %data) { - my $tmp = $k; - $tmp =~ s/^$re/loc('Not Set')/e or next; - $data{$tmp} = delete $data{$k}; - } + $max_value = $value if $max_value < $value; + $max_key_length = length $key if $max_key_length < length $key; } unless (keys %data) { $data{''} = 0; } + +my $chart = $chart_class->new( 600 => 400 ); +$chart->set( pie_height => 60 ) if $chart_class eq 'GD::Graph::pie'; +my %font_config = RT->Config->Get('ChartFont'); +my $font = $font_config{ $session{CurrentUser}->UserObj->Lang || '' } + || $font_config{'others'}; +$chart->set_title_font( $font, 16 ) if $chart->can('set_title_font'); +$chart->set_legend_font( $font, 16 ) if $chart->can('set_legend_font'); +$chart->set_x_label_font( $font, 14 ) if $chart->can('set_x_label_font'); +$chart->set_y_label_font( $font, 14 ) if $chart->can('set_y_label_font'); +$chart->set_label_font( $font, 14 ) if $chart->can('set_label_font'); +$chart->set_x_axis_font( $font, 12 ) if $chart->can('set_x_axis_font'); +$chart->set_y_axis_font( $font, 12 ) if $chart->can('set_y_axis_font'); +$chart->set_values_font( $font, 12 ) if $chart->can('set_values_font'); +$chart->set_value_font( $font, 12 ) if $chart->can('set_value_font'); + +# Pie charts don't like having no input, so we show a special image +# that indicates an error message. Because this is used in an <img> +# context, it can't be a simple error message. Without this check, +# the chart will just be a non-loading image. +if ($tix->Count == 0) { + my $plot = GD::Image->new(600 => 400); + $plot->colorAllocate(255, 255, 255); # background + my $black = $plot->colorAllocate(0, 0, 0); + + require GD::Text::Wrap; + my $error = GD::Text::Wrap->new($plot, + color => $black, + text => loc("No tickets found."), + ); + $error->set_font( $font, 16 ); + $error->draw(0, 0); + + $m->comp( 'SELF:Plot', plot => $plot, %ARGS ); +} + +if ($chart_class eq "GD::Graph::bars") { + $chart->set( + x_label => $tix->Label( $PrimaryGroupBy ), + y_label => loc('Tickets'), + show_values => 1, + bar_spacing => 5, + bargroup_spacing => 10, + x_label_position => 0.6, + y_label_position => 0.6, + values_space => -1, +# the following line to make sure there's enough space for values to show + y_max_value => 5*(int($max_value/5) + 2), +# if there're too many bars or at least one key is too long, use vertical + x_labels_vertical => ( keys(%data) * $max_key_length > 60 ) ? 1 : 0, + ); +} + +# refine values' colors, with both Color::Scheme's help and my own tweak +$chart->{dclrs} = [ + '66cc66', 'ff6666', 'ffcc66', '663399', + '3333cc', + '339933', '993333', '996633', '663399', + '33cc33', 'cc3333', 'cc9933', '6633cc' +]; + +{ + no warnings 'redefine'; + *GD::Graph::pick_data_clr = sub { + my $self = shift; + my $color_hex = $self->{dclrs}[ $_[0] % @{ $self->{dclrs} } - 1 ]; + return map { hex } ( $color_hex =~ /(..)(..)(..)/ ); + }; +} + my $plot = $chart->plot( [ [sort keys %data], [map $data{$_}, sort keys %data] ] ) or die $chart->error; $m->comp( 'SELF:Plot', plot => $plot, %ARGS ); </%init> @@ -170,7 +189,6 @@ $plot => undef </%ARGS> <%INIT> my @types = ('png', 'gif'); - for my $type (@types) { $plot->can($type) or next; diff --git a/rt/share/html/Search/Elements/Chart b/rt/share/html/Search/Elements/Chart index 3db92c4d8..bf4cd0c1c 100644 --- a/rt/share/html/Search/Elements/Chart +++ b/rt/share/html/Search/Elements/Chart @@ -72,28 +72,16 @@ my (@keys, @values); while ( my $entry = $tix->Next ) { if ($class) { my $q = $class->new( $session{'CurrentUser'} ); - $q->Load( $entry->__Value( $value_name ) ); + $q->Load( $entry->LabelValue( $value_name ) ); push @keys, $q->Name; } else { - push @keys, $entry->__Value( $value_name ); + push @keys, $entry->LabelValue( $value_name ); } $keys[-1] ||= loc('(no value)'); push @values, $entry->__Value( $count_name ); } -# XXX: Convert 1970-01-01 date to the 'Not Set' -# this code should be generalized!!! -if ( $PrimaryGroupBy =~ /(Daily|Monthly|Annually)$/ ) { - my $re; - $re = qr{1970-01-01} if $PrimaryGroupBy =~ /Daily$/; - $re = qr{1970-01} if $PrimaryGroupBy =~ /Monthly$/; - $re = qr{1970} if $PrimaryGroupBy =~ /Annually$/; - foreach (@keys) { - s/^$re/loc('Not Set')/e; - } -} - my %data; my %loc_keys; foreach my $key (@keys) { $data{$key} = shift @values; $loc_keys{$key} = loc($key); } @@ -126,7 +114,23 @@ my $query_string = $m->comp('/Elements/QueryString', %ARGS); % $total += $value; <tr class="<%$i%2 ? 'evenline' : 'oddline' %>"> <td class="label collection-as-table"> -<%$key%> +%# TODO sadly we don't have "creator.city is null" or alike support yet +%# so no link if the key is undef for now +% if ( $PrimaryGroupBy !~ /(Hourly|Daily|Monthly|Annually)$/ +% && $key ne loc('(no value)') ) { +% my $group = $PrimaryGroupBy; $group =~ s! !.!; +% my %orig_keys = reverse %loc_keys; +% my $QueryString = $m->comp('/Elements/QueryString', +% Query => "$Query and $group = '$orig_keys{$key}'", +% Format => $ARGS{Format}, +% Rows => $ARGS{Rows}, +% OrderBy => $ARGS{OrderBy}, +% Order => $ARGS{Order}, +% ); +<a href=<% RT->Config->Get('WebURL') %>Search/Results.html?<%$QueryString%>><%$key%></a> +% } else { +<% $key %> +% } </td> <td class="value collection-as-table"> <%$value%> diff --git a/rt/share/html/Search/Elements/ResultsRSSView b/rt/share/html/Search/Elements/ResultsRSSView new file mode 100644 index 000000000..f335411f8 --- /dev/null +++ b/rt/share/html/Search/Elements/ResultsRSSView @@ -0,0 +1,143 @@ +%# BEGIN BPS TAGGED BLOCK {{{ +%# +%# COPYRIGHT: +%# +%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC +%# <jesse@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> +use Encode (); + +my $old_current_user; + +if ( $m->request_comp->path =~ RT->Config->Get('WebNoAuthRegex') ) { + my $path = $m->dhandler_arg; + + my $notfound = sub { + my $mesg = shift; + $r->headers_out->{'Status'} = '404 Not Found'; + $RT::Logger->info("Error encountered in rss generation: $mesg"); + $m->clear_and_abort; + }; + + $notfound->("Invalid path: $path") unless $path =~ m!^([^/]+)/([^/]+)/?!; + + my ( $name, $auth ) = ( $1, $2 ); + + # Unescape parts + $name =~ s/\%([0-9a-z]{2})/chr(hex($1))/gei; + + # convert to perl strings + $name = Encode::decode_utf8($name); + + my $user = RT::User->new($RT::SystemUser); + $user->Load($name); + $notfound->("Invalid user: $user") unless $user->id; + + $notfound->("Invalid authstring") + unless $user->ValidateAuthString( $auth, + $ARGS{Query} . $ARGS{Order} . $ARGS{OrderBy} ); + + $old_current_user = $session{'CurrentUser'}; + my $cu = RT::CurrentUser->new; + $cu->Load($user); + $session{'CurrentUser'} = $cu; +} + +my $Tickets = RT::Tickets->new($session{'CurrentUser'}); +$Tickets->FromSQL($ARGS{'Query'}); +if ($OrderBy =~ /\|/) { + # Multiple Sorts + my @OrderBy = split /\|/,$OrderBy; + my @Order = split /\|/,$Order; + $Tickets->OrderByCols( + map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 + .. $#OrderBy ) );; +} else { + $Tickets->OrderBy(FIELD => $OrderBy, ORDER => $Order); +} +$r->content_type('application/rss+xml'); + + + + # create an RSS 1.0 file (http://purl.org/rss/1.0/) + use XML::RSS; + my $rss = new XML::RSS (version => '1.0'); + $rss->channel( + title => RT->Config->Get('rtname').": Search " . $ARGS{'Query'}, + link => RT->Config->Get('WebURL'), + description => "", + dc => { + }, + generator => "RT v" . $RT::VERSION, + syn => { + updatePeriod => "hourly", + updateFrequency => "1", + updateBase => "1901-01-01T00:00+00:00", + }, + ); + + + while ( my $Ticket = $Tickets->Next()) { + my $creator_str = $m->scomp('/Elements/ShowUser', User => $Ticket->CreatorObj); + $creator_str =~ s/[\r\n]//g; + $rss->add_item( + title => $Ticket->Subject || loc('No Subject'), + link => RT->Config->Get('WebURL')."Ticket/Display.html?id=".$Ticket->id, + description => $Ticket->Transactions->First->Content, + dc => { creator => $creator_str, + date => $Ticket->CreatedObj->RFC2822, + }, + guid => $Ticket->Queue . '_' . $Ticket->id, + ); + } + +$m->out($rss->as_string); +$session{'CurrentUser'} = $old_current_user if $old_current_user; +$m->abort(); +</%INIT> +<%ARGS> +$OrderBy => 'Created' +$Order => 'ASC' +</%ARGS> + diff --git a/rt/share/html/Search/Results.html b/rt/share/html/Search/Results.html index 13252eebb..22858f172 100755 --- a/rt/share/html/Search/Results.html +++ b/rt/share/html/Search/Results.html @@ -84,6 +84,7 @@ </div> <div align="right" class="search-result-actions"> <& Elements/ResultViews, + Collection => $session{'tickets'}, QueryString => $QueryString, Query => $Query, Format => $Format, @@ -118,6 +119,7 @@ if ( !defined($Rows) ) { $Rows = 50; } } +$Page = 1 unless $Page && $Page > 0; my ($title, $ticketcount); $session{'i'}++; @@ -134,6 +136,8 @@ if ($OrderBy =~ /\|/) { } else { $session{'tickets'}->OrderBy(FIELD => $OrderBy, ORDER => $Order); } +$session{'tickets'}->RowsPerPage( $Rows ) if $Rows; +$session{'tickets'}->GotoPage( $Page - 1 ); $session{'CurrentSearchHash'} = { Format => $Format, @@ -142,7 +146,7 @@ $session{'CurrentSearchHash'} = { Order => $Order, OrderBy => $OrderBy, RowsPerPage => $Rows - }; +}; if ( $session{'tickets'}->Query()) { @@ -161,7 +165,10 @@ my $QueryString = "?".$m->comp('/Elements/QueryString', Page => $Page); my $ShortQueryString = "?".$m->comp('/Elements/QueryString', Query => $Query); my $RSSQueryString = "?".$m->comp('/Elements/QueryString', Query => $Query, Order => $Order, OrderBy => $OrderBy); -my $RSSFeedURL = RT->Config->Get('WebPath')."/Search/Results.rdf$RSSQueryString"; +my $RSSPath = join '/', map $m->interp->apply_escapes($_, 'u'), + $session{'CurrentUser'}->UserObj->Name, + $session{'CurrentUser'}->UserObj->GenerateAuthString( $Query.$Order.$OrderBy ); +my $RSSFeedURL = RT->Config->Get('WebPath')."/NoAuth/rss/$RSSPath/$RSSQueryString"; if ($ARGS{'TicketsRefreshInterval'}) { $session{'tickets_refresh_interval'} = $ARGS{'TicketsRefreshInterval'}; diff --git a/rt/share/html/Search/Results.rdf b/rt/share/html/Search/Results.rdf index 0a1943059..c402b6f8e 100644 --- a/rt/share/html/Search/Results.rdf +++ b/rt/share/html/Search/Results.rdf @@ -45,59 +45,4 @@ %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} -<%INIT> - -my $Tickets = RT::Tickets->new($session{'CurrentUser'}); -$Tickets->FromSQL($ARGS{'Query'}); -if ($OrderBy =~ /\|/) { - # Multiple Sorts - my @OrderBy = split /\|/,$OrderBy; - my @Order = split /\|/,$Order; - $Tickets->OrderByCols( - map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 - .. $#OrderBy ) );; -} else { - $Tickets->OrderBy(FIELD => $OrderBy, ORDER => $Order); -} -$r->content_type('application/rss+xml'); - - - - # create an RSS 1.0 file (http://purl.org/rss/1.0/) - use XML::RSS; - my $rss = new XML::RSS (version => '1.0'); - $rss->channel( - title => RT->Config->Get('rtname').": Search" . $ARGS{'Query'}, - link => RT->Config->Get('WebURL'), - description => "", - dc => { - }, - generator => "RT v" . $RT::VERSION, - syn => { - updatePeriod => "hourly", - updateFrequency => "1", - updateBase => "1901-01-01T00:00+00:00", - }, - ); - - - while ( my $Ticket = $Tickets->Next()) { - my $creator_str = $m->scomp('/Elements/ShowUser', User => $Ticket->CreatorObj); - $creator_str =~ s/[\r\n]//g; - $rss->add_item( - title => $Ticket->Subject || loc('No Subject'), - link => RT->Config->Get('WebURL')."Ticket/Display.html?id=".$Ticket->id, - description => $Ticket->Transactions->First->Content, - dc => { creator => $creator_str, - date => $Ticket->CreatedObj->RFC2822, - }, - guid => $Ticket->Queue . '_' . $Ticket->id, - ); - } -$m->out($rss->as_string); -$m->abort(); -</%INIT> -<%ARGS> -$OrderBy => 'Created' -$Order => 'ASC' -</%ARGS> +<& /Search/Elements/ResultsRSSView, %ARGS &> diff --git a/rt/share/html/Search/Results.tsv b/rt/share/html/Search/Results.tsv index bb3ac4482..946a35d02 100644 --- a/rt/share/html/Search/Results.tsv +++ b/rt/share/html/Search/Results.tsv @@ -107,7 +107,7 @@ $r->content_type('application/vnd.ms-excel'); $_ += @header - 1 foreach values %cf_name_to_pos; - foreach my $name ( sort { $cf_name_to_pos{$a} <=> $cf_name_to_pos{$a} } keys %cf_name_to_pos ) { + foreach my $name ( sort { $cf_name_to_pos{$a} <=> $cf_name_to_pos{$b} } keys %cf_name_to_pos ) { push @header, "CF-". $name; } $m->out(join("\t", @header)); |