summaryrefslogtreecommitdiff
path: root/rt/share/html/Search/Elements
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-07-09 22:18:55 -0700
committerIvan Kohler <ivan@freeside.biz>2015-07-09 22:18:55 -0700
commit1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (patch)
tree96922ad4459eda1e649327fd391d60c58d454c53 /rt/share/html/Search/Elements
parent4f5619288413a185e9933088d9dd8c5afbc55dfa (diff)
RT 4.2.11, ticket#13852
Diffstat (limited to 'rt/share/html/Search/Elements')
-rw-r--r--rt/share/html/Search/Elements/BuildFormatString13
-rw-r--r--rt/share/html/Search/Elements/Chart105
-rw-r--r--rt/share/html/Search/Elements/ChartTable119
-rw-r--r--rt/share/html/Search/Elements/ConditionRow8
-rw-r--r--rt/share/html/Search/Elements/EditFormat13
-rw-r--r--rt/share/html/Search/Elements/EditSearches20
-rw-r--r--rt/share/html/Search/Elements/EditSort2
-rw-r--r--rt/share/html/Search/Elements/PickBasics26
-rw-r--r--rt/share/html/Search/Elements/PickCFs21
-rw-r--r--rt/share/html/Search/Elements/PickCriteria2
-rw-r--r--rt/share/html/Search/Elements/PickObjectCFs76
-rw-r--r--rt/share/html/Search/Elements/PickTicketCFs5
-rw-r--r--rt/share/html/Search/Elements/ResultsRSSView122
-rw-r--r--rt/share/html/Search/Elements/SearchPrivacy6
-rw-r--r--rt/share/html/Search/Elements/SearchesForObject4
-rw-r--r--rt/share/html/Search/Elements/SelectAndOr4
-rw-r--r--rt/share/html/Search/Elements/SelectChartFunction79
-rw-r--r--rt/share/html/Search/Elements/SelectChartType3
-rw-r--r--rt/share/html/Search/Elements/SelectGroup2
-rw-r--r--rt/share/html/Search/Elements/SelectGroupBy26
-rw-r--r--rt/share/html/Search/Elements/SelectLinks19
-rw-r--r--rt/share/html/Search/Elements/SelectPersonType4
22 files changed, 457 insertions, 222 deletions
diff --git a/rt/share/html/Search/Elements/BuildFormatString b/rt/share/html/Search/Elements/BuildFormatString
index 9a3ba1e75..10ac1afbe 100644
--- a/rt/share/html/Search/Elements/BuildFormatString
+++ b/rt/share/html/Search/Elements/BuildFormatString
@@ -115,10 +115,12 @@ $m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fi
my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
foreach my $id (keys %queues) {
- # Gotta load up the $queue object, since queues get stored by name now. my $id
+ # Gotta load up the $queue object, since queues get stored by name now.
my $queue = RT::Queue->new($session{'CurrentUser'});
$queue->Load($id);
- $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
+ next unless $queue->Id;
+ $CustomFields->LimitToQueue($queue->Id);
+ $CustomFields->SetContextObject( $queue ) if keys %queues == 1;
}
$CustomFields->LimitToGlobal;
@@ -140,8 +142,7 @@ foreach my $field (@format) {
if ( $RemoveCol ) {
# we do this regex match to avoid a non-numeric warning
- my ($index) = $CurrentDisplayColumns =~ /^(\d+)/;
- my $column = $seen[$index];
+ my ($index) = ($CurrentDisplayColumns // '') =~ /^(\d+)/;
if ( defined($index) ) {
delete $seen[$index];
my @temp = @seen;
@@ -206,7 +207,7 @@ elsif ( $AddCol ) {
}
}
elsif ( $ColUp ) {
- my $index = $CurrentDisplayColumns;
+ my ($index) = ($CurrentDisplayColumns // '') =~ /^(\d+)/;
if ( defined $index && ( $index - 1 ) >= 0 ) {
my $column = $seen[$index];
$seen[$index] = $seen[ $index - 1 ];
@@ -215,7 +216,7 @@ elsif ( $ColUp ) {
}
}
elsif ( $ColDown ) {
- my $index = $CurrentDisplayColumns;
+ my ($index) = ($CurrentDisplayColumns // '') =~ /^(\d+)/;
if ( defined $index && ( $index + 1 ) < scalar @seen ) {
my $column = $seen[$index];
$seen[$index] = $seen[ $index + 1 ];
diff --git a/rt/share/html/Search/Elements/Chart b/rt/share/html/Search/Elements/Chart
index 38c15f6ef..6285fac2b 100644
--- a/rt/share/html/Search/Elements/Chart
+++ b/rt/share/html/Search/Elements/Chart
@@ -47,107 +47,44 @@
%# END BPS TAGGED BLOCK }}}
<%args>
$Query => "id > 0"
-$PrimaryGroupBy => 'Queue'
-$ChartStyle => 'bar'
+@GroupBy => ()
+$ChartStyle => 'bar+table+sql'
+@ChartFunction => 'COUNT'
</%args>
<%init>
use RT::Report::Tickets;
-$PrimaryGroupBy ||= 'Queue'; # make sure PrimaryGroupBy is not undef
-my $tix = RT::Report::Tickets->new( $session{'CurrentUser'} );
-my %AllowedGroupings = reverse $tix->Groupings( Query => $Query );
-$PrimaryGroupBy = 'Queue' unless exists $AllowedGroupings{$PrimaryGroupBy};
-my ($count_name, $value_name) = $tix->SetupGroupings(
- Query => $Query, GroupBy => $PrimaryGroupBy,
-);
+my $report = RT::Report::Tickets->new( $session{'CurrentUser'} );
-my %class = (
- Queue => 'RT::Queue',
- Owner => 'RT::User',
- Creator => 'RT::User',
- LastUpdatedBy => 'RT::User',
+my %columns = $report->SetupGroupings(
+ Query => $Query,
+ GroupBy => \@GroupBy,
+ Function => \@ChartFunction,
);
-my $class = $class{ $PrimaryGroupBy };
-
-my (@keys, @values);
-while ( my $entry = $tix->Next ) {
- if ($class) {
- my $q = $class->new( $session{'CurrentUser'} );
- $q->Load( $entry->LabelValue( $value_name ) );
- push @keys, $q->Name;
- }
- else {
- push @keys, $entry->LabelValue( $value_name );
- }
- $keys[-1] ||= loc('(no value)');
- push @values, $entry->__Value( $count_name );
-}
-my %data;
-my %loc_keys;
-foreach my $key (@keys) { $data{$key} = shift @values; $loc_keys{$key} = loc($key); }
-my @sorted_keys = map { $loc_keys{$_}} sort { $loc_keys{$a} cmp $loc_keys{$b} } keys %loc_keys;
-my @sorted_values = map { $data{$_}} sort { $loc_keys{$a} cmp $loc_keys{$b} } keys %loc_keys;
-my $query_string = $m->comp('/Elements/QueryString', %ARGS);
+$report->SortEntries;
-my ($i,$total);
+my $query_string = $m->comp('/Elements/QueryString', %ARGS, GroupBy => \@GroupBy );
</%init>
<div class="chart-wrapper">
-<span class="chart image">
+% if ( ($ChartStyle || '') =~ /\b(pie|bar)\b/ ) {
+<span class="chart image <% $1 %>">
% if (RT->Config->Get('DisableGD')) {
<% loc('Graphical charts are not available.') %><br />
% } else {
-<img src="<%RT->Config->Get('WebPath')%>/Search/Chart?<%$query_string|n%>" />
+% my $key = Digest::MD5::md5_hex( rand(1024) );
+% $session{'charts_cache'}{$key} = { columns => \%columns, report => $report->Serialize };
+% $session{'i'}++;
+<img src="<% RT->Config->Get('WebPath') %>/Search/Chart?Cache=<% $key |un %>&<% $query_string |n %>" />
% }
</span>
-<table class="collection-as-table chart">
-<tr>
-<th class="collection-as-table"><% loc($tix->Label($PrimaryGroupBy)) %>
-</th>
-<th class="collection-as-table"><&|/l&>Tickets</&>
-</th>
-</tr>
-<%perl>
- while (my $key = shift @sorted_keys) {
- $i++;
- my $value = shift @sorted_values;
- $total += $value;
-</%perl>
-<tr class="<% $i%2 ? 'evenline' : 'oddline' %>">
-<%perl>
-# 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},
- );
-</%perl>
-<td class="label collection-as-table">
-<a href=<% RT->Config->Get('WebPath') %>/Search/Results.html?<%$QueryString%>><%$key%></a>
-</td>
-<td class="value collection-as-table">
-<a href=<% RT->Config->Get('WebPath') %>/Search/Results.html?<%$QueryString%>><%$value%></a>
-</td>
-% } else {
-<td class="label collection-as-table"><% $key %></td>
-<td class="value collection-as-table"><% $value %></td>
-% }
-</tr>
% }
-%$i++;
-<tr class="<%$i%2 ? 'evenline' : 'oddline' %> total">
-<td class="label collection-as-table"><%loc('Total')%></td>
-<td class="value collection-as-table"><%$total||'0'%></td>
-</tr>
+% if ( ($ChartStyle || '') =~ /\btable\b/ ) {
+<& ChartTable, %ARGS, Table => { $report->FormatTable( %columns ) } &>
+% }
-</table>
+% if ( ($ChartStyle || '') =~ /\bsql\b/ ) {
<div class="query"><span class="label"><% loc('Query') %>:</span><span class="value"><% $Query %></span></div>
+% }
</div>
diff --git a/rt/share/html/Search/Elements/ChartTable b/rt/share/html/Search/Elements/ChartTable
new file mode 100644
index 000000000..045653ae8
--- /dev/null
+++ b/rt/share/html/Search/Elements/ChartTable
@@ -0,0 +1,119 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 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>
+%Table => ()
+$Query => undef
+</%ARGS>
+<%INIT>
+
+my $base_query = $m->comp('/Elements/QueryString',
+ Format => $ARGS{Format},
+ Rows => $ARGS{Rows},
+ OrderBy => $ARGS{OrderBy},
+ Order => $ARGS{Order},
+);
+
+my $interp = $m->interp;
+my $eh = sub { $interp->apply_escapes( @_, 'h' ) };
+my $eu = sub { $interp->apply_escapes( @_, 'u' ) };
+
+$m->out('<table class="collection-as-table chart">'. "\n");
+foreach my $section (qw(thead tbody tfoot)) {
+ next unless $Table{ $section } && @{ $Table{ $section } };
+
+ $m->out("<$section>\n");
+ foreach my $row ( @{ $Table{ $section } } ) {
+ $m->out(' <tr');
+ $m->out(' class="'. ($row->{'even'}? 'evenline' : 'oddline') .'"')
+ if defined $row->{'even'};
+ $m->out(">");
+
+ foreach my $cell ( @{ $row->{'cells'} } ) {
+ my $tag = $cell->{'type'} eq 'value'? 'td' : 'th';
+ $m->out("<$tag");
+
+ my @class = ('collection-as-table');
+ push @class, ($cell->{'type'}) unless $cell->{'type'} eq 'head';
+ push @class, $cell->{'even'} ? 'evenline' : 'oddline'
+ if defined $cell->{'even'};
+ $m->out(' class="'. $eh->( join ' ', @class ) .'"');
+
+ foreach my $dir ( grep $cell->{$_}, qw(rowspan colspan) ) {
+ my $value = int $cell->{ $dir };
+ $m->out(qq{ $dir="$value"});
+ }
+ $m->out(' style="background-color: #'. $m->interp->apply_escapes($cell->{color}) .'"')
+ if $cell->{color};
+
+ $m->out('>');
+ if ( defined $cell->{'value'} ) {
+ if ( my $q = $cell->{'query'} ) {
+ $m->out(
+ '<a href="'. $eh->(RT->Config->Get('WebPath')) .'/Search/Results.html'
+ .'?Query='. $eu->(join ' AND ', map "($_)", grep defined && length, $Query, $q)
+ . $eh->('&') . $base_query
+ . '">'
+ );
+ $m->out( $eh->( $cell->{'value'} ) );
+ $m->out('</a>');
+ }
+ else {
+ $m->out( $eh->( $cell->{'value'} ) );
+ }
+ }
+ else {
+ $m->out('&nbsp;');
+ }
+ $m->out("</$tag>");
+ }
+ $m->out("</tr>\n");
+ }
+ $m->out("</$section>\n\n");
+}
+$m->out("</table>");
+</%INIT>
diff --git a/rt/share/html/Search/Elements/ConditionRow b/rt/share/html/Search/Elements/ConditionRow
index edf738107..80ecd97b4 100644
--- a/rt/share/html/Search/Elements/ConditionRow
+++ b/rt/share/html/Search/Elements/ConditionRow
@@ -74,9 +74,11 @@ $handle_block = sub {
return $m->scomp( $box->{'Path'}, %{ $box->{'Arguments'} }, Name => $name );
}
if ( $box->{'Type'} eq 'text' ) {
- my $default = $box->{'Default'} || '';
- my $size = $box->{'Size'}? qq{size="$box->{'Size'}"} : '';
- return qq{<input id="$name" name="$name" value="$default" $size />};
+ $box->{id} ||= $box->{name} ||= $name;
+ $box->{value} ||= delete($box->{Default}) || '';
+ return "<input ".join(" ", map{$m->interp->apply_escapes(lc($_),'h')
+ .q{="}.$m->interp->apply_escapes($box->{$_},'h').q{"}}
+ sort keys %$box)." />";
}
if ( $box->{'Type'} eq 'select' ) {
my $res = '';
diff --git a/rt/share/html/Search/Elements/EditFormat b/rt/share/html/Search/Elements/EditFormat
index a78fa0574..fffec5c9d 100644
--- a/rt/share/html/Search/Elements/EditFormat
+++ b/rt/share/html/Search/Elements/EditFormat
@@ -59,7 +59,8 @@
<td valign="top"><select size="6" name="SelectDisplayColumns" multiple="multiple">
% my %seen;
% foreach my $field ( grep !$seen{lc $_}++, @$AvailableColumns) {
-<option value="<% $field %>"><% loc($field) %></option>
+<option value="<% $field %>" <% $selected{$field} ? 'selected="selected"' : '' |n%>>\
+<% $field =~ /^(?:CustomField|CF)\./ ? $field : loc($field) %></option>
% }
</select></td>
<td>
@@ -105,8 +106,10 @@
<td valign="top">
<select size="4" name="CurrentDisplayColumns">
% my $i=0;
+% my $current = $ARGS{CurrentDisplayColumns} || ''; $current =~ s/^\d+>//;
% foreach my $field ( @$CurrentFormat ) {
-<option value="<% $i++ %>><% $field->{Column} %>"><% loc( $field->{Column} ) %></option>
+<option value="<% $i++ %>><% $field->{Column} %>" <% $field->{Column} eq $current ? 'selected="selected"' : '' |n%>>\
+<% $field->{Column} =~ /^(?:CustomField|CF)\./ ? $field->{Column} : loc( $field->{Column} ) %></option>
% }
</select>
<br />
@@ -120,6 +123,12 @@
</tr>
</table>
+<%init>
+my $selected = $ARGS{SelectDisplayColumns};
+$selected = [ $selected ] unless ref $selected;
+my %selected;
+$selected{$_}++ for grep {defined} @{ $selected };
+</%init>
<%ARGS>
$CurrentFormat => undef
$AvailableColumns => undef
diff --git a/rt/share/html/Search/Elements/EditSearches b/rt/share/html/Search/Elements/EditSearches
index f5be486b8..0a55e0dff 100644
--- a/rt/share/html/Search/Elements/EditSearches
+++ b/rt/share/html/Search/Elements/EditSearches
@@ -146,7 +146,7 @@ $SavedSearch => {}
$SavedSearch->{'Id'} = ( $ARGS{Type} && $ARGS{Type} eq 'Chart' ?
$ARGS{'SavedChartSearchId'} : $ARGS{'SavedSearchId'} ) || 'new';
-$SavedSearch->{'Description'} = $ARGS{'SavedSearchDescription'} || undef;
+$SavedSearch->{'Description'} = $ARGS{'SavedSearchDescription'} || '';
$SavedSearch->{'Privacy'} = $ARGS{'SavedSearchOwner'} || undef;
my @results;
@@ -158,7 +158,8 @@ if ( $ARGS{'SavedSearchRevert'} ) {
if ( $ARGS{'SavedSearchLoad'} ) {
my ($container, $id ) = _parse_saved_search ($ARGS{'SavedSearchLoad'});
if ( $container ) {
- my $search = $container->Attributes->WithId( $id );
+ my $search = RT::Attribute->new( $session{'CurrentUser'} );
+ $search->Load( $id );
$SavedSearch->{'Id'} = $ARGS{'SavedSearchLoad'};
$SavedSearch->{'Object'} = $search;
$SavedSearch->{'Description'} = $search->Description;
@@ -194,7 +195,8 @@ elsif ( $ARGS{'SavedSearchDelete'} ) {
}
elsif ( $ARGS{'SavedSearchCopy'} ) {
my ($container, $id ) = _parse_saved_search( $ARGS{'SavedSearchId'} );
- $SavedSearch->{'Object'} = $container->Attributes->WithId( $id );
+ $SavedSearch->{'Object'} = RT::Attribute->new( $session{'CurrentUser'} );
+ $SavedSearch->{'Object'}->Load( $id );
if ( $ARGS{'SavedSearchDescription'} && $ARGS{'SavedSearchDescription'} ne $SavedSearch->{'Object'}->Description ) {
$SavedSearch->{'Description'} = $ARGS{'SavedSearchDescription'};
} else {
@@ -208,7 +210,8 @@ if ( $SavedSearch->{'Id'} && $SavedSearch->{'Id'} ne 'new'
&& !$SavedSearch->{'Object'} )
{
my ($container, $id ) = _parse_saved_search( $ARGS{'SavedSearchId'} );
- $SavedSearch->{'Object'} = $container->Attributes->WithId( $id );
+ $SavedSearch->{'Object'} = RT::Attribute->new( $session{'CurrentUser'} );
+ $SavedSearch->{'Object'}->Load( $id );
$SavedSearch->{'Description'} ||= $SavedSearch->{'Object'}->Description;
}
@@ -290,7 +293,7 @@ if ( $obj && $obj->id ) {
}
push @results, loc('Updated saved search "[_1]"', $desc);
}
-elsif ( $id eq 'new' ) {
+elsif ( $id eq 'new' and defined $desc and length $desc ) {
my $saved_search = RT::SavedSearch->new( $session{'CurrentUser'} );
my ($status, $msg) = $saved_search->Save(
Privacy => $privacy,
@@ -300,8 +303,8 @@ elsif ( $id eq 'new' ) {
);
if ( $status ) {
- $SavedSearch->{'Object'} =
- $session{'CurrentUser'}->UserObj->Attributes->WithId( $saved_search->Id );
+ $SavedSearch->{'Object'} = RT::Attribute->new( $session{'CurrentUser'} );
+ $SavedSearch->{'Object'}->Load( $saved_search->Id );
# Build new SearchId
$SavedSearch->{'Id'} =
ref( $session{'CurrentUser'}->UserObj ) . '-'
@@ -313,6 +316,9 @@ elsif ( $id eq 'new' ) {
push @results, loc("Can't find a saved search to work with").': '.loc($msg);
}
}
+elsif ( $id eq 'new' ) {
+ push @results, loc("Can't save a search without a Description");
+}
else {
push @results, loc("Can't save this search");
}
diff --git a/rt/share/html/Search/Elements/EditSort b/rt/share/html/Search/Elements/EditSort
index de5d2d818..43ae7292d 100644
--- a/rt/share/html/Search/Elements/EditSort
+++ b/rt/share/html/Search/Elements/EditSort
@@ -68,7 +68,7 @@
% if (defined $OrderBy[$o] and $fieldval eq $OrderBy[$o]) {
selected="selected"
% }
-><% loc($field) %></option>
+><% $field =~ /^(?:CustomField|CF)\./ ? $field : loc($field) %></option>
% }
</select>
<select name="Order">
diff --git a/rt/share/html/Search/Elements/PickBasics b/rt/share/html/Search/Elements/PickBasics
index 3aae96589..29eea7e3f 100644
--- a/rt/share/html/Search/Elements/PickBasics
+++ b/rt/share/html/Search/Elements/PickBasics
@@ -70,10 +70,10 @@ my @lines = (
Type => 'component',
Path => '/Elements/SelectBoolean',
Arguments => {
- True => loc("matches"),
- False => loc("doesn't match"),
- TrueVal => 'LIKE',
- FalseVal => 'NOT LIKE',
+ True => loc("matches"),
+ False => loc("doesn't match"),
+ TrueVal => 'LIKE',
+ FalseVal => 'NOT LIKE',
},
},
Value => { Type => 'text', Size => 20 },
@@ -89,7 +89,7 @@ my @lines = (
Value => {
Type => 'component',
Path => '/Elements/SelectQueue',
- Arguments => { NamedValues => 1, CheckQueueRight => 'ShowTicket' },
+ Arguments => { NamedValues => 1, },
},
},
{
@@ -102,7 +102,7 @@ my @lines = (
},
Value => {
Type => 'component',
- Path => '/Elements/SelectStatus',
+ Path => '/Ticket/Elements/SelectStatus',
Arguments => { SkipDeleted => 1, Queues => \%queues },
},
},
@@ -114,6 +114,7 @@ my @lines = (
Owner => loc('Owner'),
Creator => loc('Creator'),
LastUpdatedBy => loc('Last updated by'),
+ UpdatedBy => loc('Updated by'),
],
},
Op => {
@@ -141,6 +142,19 @@ my @lines = (
Value => { Type => 'text', Size => 20 }
},
{
+ Name => 'WatcherGroup',
+ Field => {
+ Type => 'component',
+ Path => 'SelectPersonType',
+ Arguments => { Default => 'Owner', Suffix => 'Group' },
+ },
+ Op => {
+ Type => 'select',
+ Options => [ '=' => loc('is') ],
+ },
+ Value => { Type => 'text', Size => 20, "data-autocomplete" => "Groups" }
+ },
+ {
Name => 'Date',
Field => {
Type => 'component',
diff --git a/rt/share/html/Search/Elements/PickCFs b/rt/share/html/Search/Elements/PickCFs
index cf8c92a27..e8d9c71e2 100644
--- a/rt/share/html/Search/Elements/PickCFs
+++ b/rt/share/html/Search/Elements/PickCFs
@@ -58,7 +58,7 @@ $m->callback(
my @lines;
while ( my $CustomField = $CustomFields->Next ) {
my %line;
- $line{'Name'} = "'$TicketSQLField.{" . $CustomField->Name . "}'";
+ $line{'Name'} = "$TicketSQLField.{" . $CustomField->Name . "}";
$line{'Field'} = $CustomField->Name;
# Op
@@ -88,20 +88,11 @@ while ( my $CustomField = $CustomFields->Next ) {
}
# Value
- if ($CustomField->Type =~ /^Date(Time)?$/) {
- my $is_datetime = $1 ? 1 : 0;
- $line{'Value'} = {
- Type => 'component',
- Path => '/Elements/SelectDate',
- Arguments => { $is_datetime ? (ShowTime => 1) : (ShowTime => 0), },
- };
- } else {
- $line{'Value'} = {
- Type => 'component',
- Path => '/Elements/SelectCustomFieldValue',
- Arguments => { CustomField => $CustomField },
- };
- }
+ $line{'Value'} = {
+ Type => 'component',
+ Path => '/Elements/SelectCustomFieldValue',
+ Arguments => { CustomField => $CustomField },
+ };
push @lines, \%line;
}
diff --git a/rt/share/html/Search/Elements/PickCriteria b/rt/share/html/Search/Elements/PickCriteria
index b2e84cae9..e55e27085 100644
--- a/rt/share/html/Search/Elements/PickCriteria
+++ b/rt/share/html/Search/Elements/PickCriteria
@@ -54,6 +54,8 @@
<& PickBasics &>
<& PickCustomerFields &>
<& PickTicketCFs, queues => \%queues &>
+<& PickObjectCFs, Class => 'Transaction', queues => \%queues &>
+<& PickObjectCFs, Class => 'Queue', queues => \%queues &>
% $m->callback( %ARGS, CallbackName => "AfterCFs" );
<tr class="separator"><td colspan="3"><hr /></td></tr>
diff --git a/rt/share/html/Search/Elements/PickObjectCFs b/rt/share/html/Search/Elements/PickObjectCFs
new file mode 100644
index 000000000..1a67338c5
--- /dev/null
+++ b/rt/share/html/Search/Elements/PickObjectCFs
@@ -0,0 +1,76 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 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>
+$Class
+%queues => ()
+</%ARGS>
+<%init>
+my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'} );
+$CustomFields->ApplySortOrder;
+$CustomFields->LimitToLookupType( "RT::$Class"->CustomFieldLookupType );
+$CustomFields->LimitToObjectId(0);
+
+foreach my $name (keys %queues) {
+ my $queue = RT::Queue->new($session{'CurrentUser'});
+ $queue->Load($name);
+ next unless $queue->Id;
+ $CustomFields->LimitToObjectId($queue->Id);
+ $CustomFields->SetContextObject( $queue ) if keys %queues == 1;
+}
+
+my $has_cf = $CustomFields->First ? 1 : 0;
+$CustomFields->GotoFirstItem;
+</%init>
+% if ($has_cf) {
+<tr class="separator">
+ <td colspan="3">
+ <hr><em><% loc("[_1] CFs", loc($Class)) %></em>
+ </td>
+</tr>
+% }
+<& PickCFs, %ARGS, TicketSQLField => "${Class}CF", CustomFields => $CustomFields &>
diff --git a/rt/share/html/Search/Elements/PickTicketCFs b/rt/share/html/Search/Elements/PickTicketCFs
index ac52049c3..ae3a4a286 100644
--- a/rt/share/html/Search/Elements/PickTicketCFs
+++ b/rt/share/html/Search/Elements/PickTicketCFs
@@ -54,8 +54,11 @@ foreach my $id (keys %queues) {
# Gotta load up the $queue object, since queues get stored by name now.
my $queue = RT::Queue->new($session{'CurrentUser'});
$queue->Load($id);
- $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
+ next unless $queue->Id;
+ $CustomFields->LimitToQueue($queue->Id);
+ $CustomFields->SetContextObject( $queue ) if keys %queues == 1;
}
$CustomFields->LimitToGlobal;
+$CustomFields->OrderBy( FIELD => 'Name', ORDER => 'ASC' );
</%init>
<& PickCFs, %ARGS, TicketSQLField => 'CF', CustomFields => $CustomFields &>
diff --git a/rt/share/html/Search/Elements/ResultsRSSView b/rt/share/html/Search/Elements/ResultsRSSView
index 0bce7ec45..f392369a8 100644
--- a/rt/share/html/Search/Elements/ResultsRSSView
+++ b/rt/share/html/Search/Elements/ResultsRSSView
@@ -46,41 +46,7 @@
%#
%# END BPS TAGGED BLOCK }}}
<%INIT>
-my $current_user = $session{CurrentUser};
-
-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;
-
- # Decode from bytes to characters
- $name = Encode::decode( "UTF-8", $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} );
-
- $current_user = RT::CurrentUser->new;
- $current_user->Load($user);
-}
-
-my $Tickets = RT::Tickets->new($current_user);
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
$Tickets->FromSQL($ARGS{'Query'});
if ($OrderBy =~ /\|/) {
# Multiple Sorts
@@ -92,48 +58,58 @@ if ($OrderBy =~ /\|/) {
} else {
$Tickets->OrderBy(FIELD => $OrderBy, ORDER => $Order);
}
-$r->content_type('application/rss+xml');
-
-
+$r->content_type('application/rss+xml; charset=utf-8');
- # create an RSS 1.0 file (http://purl.org/rss/1.0/)
- use XML::RSS;
- my $rss = XML::RSS->new(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",
- },
- );
+use XML::RSS;
+my $rss = XML::RSS->new(version => '1.0');
+my $url;
+if ( RT->Config->Get('CanonicalizeURLsInFeeds') ) {
+ $url = RT->Config->Get('WebURL');
+} else {
+ $url = RT::Interface::Web::GetWebURLFromRequest();
+}
- while ( my $Ticket = $Tickets->Next()) {
- my $creator_str = $m->scomp('/Elements/ShowUser', User => $Ticket->CreatorObj);
- $creator_str =~ s/[\r\n]//g;
-
- # Get the plain-text content; it is interpreted as HTML by RSS
- # readers, so it must be escaped (and is escaped _again_ when
- # inserted into the XML).
- my $content = $Ticket->Transactions->First->Content;
- $content = $m->interp->apply_escapes( $content, 'h');
- $rss->add_item(
- title => $Ticket->Subject || loc('No Subject'),
- link => RT->Config->Get('WebURL')."Ticket/Display.html?id=".$Ticket->id,
- description => $content,
- dc => { creator => $creator_str,
- date => $Ticket->CreatedObj->RFC2822,
- },
- guid => $Ticket->Queue . '_' . $Ticket->id,
- );
- }
+my $base_date = RT::Date->new( RT->SystemUser );
+$base_date->SetToNow;
+$base_date->SetToMidnight;
+
+$rss->channel(
+ title => RT->Config->Get('rtname').": Search " . $ARGS{'Query'},
+ link => $url,
+ description => "",
+ dc => { },
+ generator => "RT v" . $RT::VERSION,
+ syn => {
+ updatePeriod => "hourly",
+ updateFrequency => "1",
+ updateBase => $base_date->W3CDTF,
+ },
+);
+
+
+while ( my $Ticket = $Tickets->Next()) {
+ my $creator_str = $Ticket->CreatorObj->Format;
+ $creator_str =~ s/[\r\n]//g;
+
+ # Get the plain-text content; it is interpreted as HTML by RSS
+ # readers, so it must be escaped (and is escaped _again_ when
+ # inserted into the XML).
+ my $content = $Ticket->Transactions->First->Content;
+ $content = $m->interp->apply_escapes( $content, 'h');
+
+ $rss->add_item(
+ title => $Ticket->Subject || loc('No Subject'),
+ link => $url . "Ticket/Display.html?id=".$Ticket->id,
+ description => $content,
+ dc => {
+ creator => $creator_str,
+ date => $Ticket->CreatedObj->W3CDTF,
+ },
+ guid => $Ticket->Queue . '_' . $Ticket->id,
+ );
+}
$m->out($rss->as_string);
$m->abort();
diff --git a/rt/share/html/Search/Elements/SearchPrivacy b/rt/share/html/Search/Elements/SearchPrivacy
index dd7ef3b2f..1e43dfd39 100644
--- a/rt/share/html/Search/Elements/SearchPrivacy
+++ b/rt/share/html/Search/Elements/SearchPrivacy
@@ -53,9 +53,9 @@ my $label;
if (ref($Object) eq 'RT::User') {
$label = $Object->id == $session{'CurrentUser'}->Id
? loc("My saved searches")
- : loc("[_1]'s saved searches", $m->scomp('/Elements/ShowUser', User => $Object));
+ : loc("[_1]'s saved searches", $Object->Format);
} else {
- $label = loc("[_1]'s saved searches", $m->interp->apply_escapes($Object->Name, 'h'));
+ $label = loc("[_1]'s saved searches", $Object->Name);
}
</%init>
-<% $label |n %>\
+<% $label %>\
diff --git a/rt/share/html/Search/Elements/SearchesForObject b/rt/share/html/Search/Elements/SearchesForObject
index 397a0d95c..f58752d00 100644
--- a/rt/share/html/Search/Elements/SearchesForObject
+++ b/rt/share/html/Search/Elements/SearchesForObject
@@ -55,10 +55,10 @@ my @result;
while (my $search = $Object->Attributes->Next) {
my $desc;
if ($search->Name eq 'SavedSearch') {
- push @result, [$search->Description, $search->Description, $search];
+ push @result, [$search->Description, $search->Description, $search];
}
elsif ($search->Name =~ m/^Search - (.*)/) {
- push @result, [$1, loc($1), $search];
+ push @result, [$1, loc($1), $search];
}
}
return @result;
diff --git a/rt/share/html/Search/Elements/SelectAndOr b/rt/share/html/Search/Elements/SelectAndOr
index cbea34fc2..d506ef785 100644
--- a/rt/share/html/Search/Elements/SelectAndOr
+++ b/rt/share/html/Search/Elements/SelectAndOr
@@ -45,8 +45,8 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<input type="radio" class="radio" name="<%$Name%>" checked="checked" value="AND" /><&|/l&>AND</&>
-<input type="radio" class="radio" name="<%$Name%>" value="OR" /><&|/l&>OR</&>
+<label><input type="radio" class="radio" name="<%$Name%>" checked="checked" value="AND" /><&|/l&>AND</&></label>
+<label><input type="radio" class="radio" name="<%$Name%>" value="OR" /><&|/l&>OR</&></label>
<%ARGS>
$Name => "Operator"
diff --git a/rt/share/html/Search/Elements/SelectChartFunction b/rt/share/html/Search/Elements/SelectChartFunction
new file mode 100644
index 000000000..dad6b781a
--- /dev/null
+++ b/rt/share/html/Search/Elements/SelectChartFunction
@@ -0,0 +1,79 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 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 %>" class="cascade-by-optgroup">
+% if ( $ShowEmpty ) {
+<option value="">&nbsp;</option>
+% }
+<%perl>
+my $in_optgroup = "";
+while ( my ($value, $display) = splice @functions, 0, 2 ) {
+ my $optgroup = $value =~ /\((.+)\)$/ ? $1 : $display;
+ if ($in_optgroup ne $optgroup) {
+ $m->out("</optgroup>\n") if $in_optgroup;
+
+ my $name = $m->interp->apply_escapes(loc($optgroup), 'h');
+ $m->out(qq[<optgroup label="$name">\n]);
+
+ $in_optgroup = $optgroup;
+ }
+</%perl>
+<option value="<% $value %>"<% $value eq $Default ? qq[ selected="selected"] : '' |n %>><% loc( $display ) %></option>
+% }
+% if ($in_optgroup) {
+ </optgroup>
+% }
+</select>
+<%ARGS>
+$Name => 'ChartFunction'
+$Default => 'COUNT'
+$ShowEmpty => 0
+</%ARGS>
+<%INIT>
+my @functions = RT::Report::Tickets->Statistics;
+$Default = '' unless defined $Default;
+</%INIT>
diff --git a/rt/share/html/Search/Elements/SelectChartType b/rt/share/html/Search/Elements/SelectChartType
index 266885f0d..c4d95d00c 100644
--- a/rt/share/html/Search/Elements/SelectChartType
+++ b/rt/share/html/Search/Elements/SelectChartType
@@ -50,9 +50,10 @@ $Name => 'ChartType'
$Default => 'bar'
</%args>
<select id="<%$Name%>" name="<%$Name%>">
-% foreach my $option (qw(bar pie)) {
+% foreach my $option ('bar', 'pie', 'table') {
% # 'bar' # loc
% # 'pie' # loc
+% # 'table' # loc
<option value="<%$option%>"<% $option eq $Default ? qq[ selected="selected"] : '' |n %>><%loc($option)%></option>
% }
</select>
diff --git a/rt/share/html/Search/Elements/SelectGroup b/rt/share/html/Search/Elements/SelectGroup
index 907c88e27..27d6a762e 100644
--- a/rt/share/html/Search/Elements/SelectGroup
+++ b/rt/share/html/Search/Elements/SelectGroup
@@ -56,7 +56,7 @@
<%INIT>
my $groups = RT::Groups->new($session{'CurrentUser'});
-$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain);
+$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain, CASESENSITIVE => 0);
</%INIT>
<%ARGS>
diff --git a/rt/share/html/Search/Elements/SelectGroupBy b/rt/share/html/Search/Elements/SelectGroupBy
index 8daab6daa..99f0f47eb 100644
--- a/rt/share/html/Search/Elements/SelectGroupBy
+++ b/rt/share/html/Search/Elements/SelectGroupBy
@@ -49,11 +49,29 @@
$Name => 'GroupBy'
$Default => 'Status'
$Query => ''
+$ShowEmpty => 0
</%args>
-<select id="<% $Name %>" name="<% $Name %>">
-% while (@options) {
-% my ($text, $value) = (shift @options, shift @options);
-<option value="<% $value %>" <% $value eq $Default ? 'selected="selected"' : '' |n%>><% $text %></option>
+<select name="<% $Name %>" class="cascade-by-optgroup">
+% if ( $ShowEmpty ) {
+<option value="">&nbsp;</option>
+% }
+<%perl>
+my $in_optgroup = "";
+while ( my ($label, $value) = splice @options, 0, 2 ) {
+ my ($optgroup, $text) = @$label;
+ if ($in_optgroup ne $optgroup) {
+ $m->out("</optgroup>\n") if $in_optgroup;
+
+ my $name = $m->interp->apply_escapes(loc($optgroup), 'h');
+ $m->out(qq[<optgroup label="$name">\n]);
+
+ $in_optgroup = $optgroup;
+ }
+</%perl>
+<option value="<% $value %>" <% $value eq ($Default||'') ? 'selected="selected"' : '' |n %>><% loc($text) %></option>
+% }
+% if ($in_optgroup) {
+ </optgroup>
% }
</select>
<%init>
diff --git a/rt/share/html/Search/Elements/SelectLinks b/rt/share/html/Search/Elements/SelectLinks
index 3759a58d1..1b8509461 100644
--- a/rt/share/html/Search/Elements/SelectLinks
+++ b/rt/share/html/Search/Elements/SelectLinks
@@ -47,7 +47,7 @@
%# END BPS TAGGED BLOCK }}}
<select name="<%$Name%>">
% foreach (@fields) {
-<option value="<%$_%>"><% loc($_) %></option>
+<option value="<%$_->[0]%>"><% $_->[1] %></option>
% }
</select>
<%ARGS>
@@ -55,12 +55,13 @@ $Name => 'LinksField'
</%ARGS>
<%INIT>
-my @fields = ('HasMember',
- 'MemberOf',
- 'DependsOn',
- 'DependedOnBy',
- 'RefersTo',
- 'ReferredToBy',
- 'LinkedTo',
- );
+my @fields = (
+ [ HasMember => loc("Child") ],
+ [ MemberOf => loc("Parent") ],
+ [ DependsOn => loc("Depends on") ],
+ [ DependedOnBy => loc("Depended on by") ],
+ [ RefersTo => loc("Refers to") ],
+ [ ReferredToBy => loc("Referred to by") ],
+ [ LinkedTo => loc("Links to") ],
+);
</%INIT>
diff --git a/rt/share/html/Search/Elements/SelectPersonType b/rt/share/html/Search/Elements/SelectPersonType
index 7ec875a8d..0fc541b07 100644
--- a/rt/share/html/Search/Elements/SelectPersonType
+++ b/rt/share/html/Search/Elements/SelectPersonType
@@ -51,7 +51,7 @@
% }
% for my $option (@types) {
% if ($Suffix) {
-<option value="<% $option %><% $Suffix %>"<%$option eq $Default && qq[ selected="selected"] |n %> ><%loc($option)%></option>
+<option value="<% $option %><% $Suffix %>"<%$option eq $Default && qq[ selected="selected"] |n %> ><% loc($option) %> <% loc('Group') %></option>
% next;
% }
% foreach my $subtype (@subtypes) {
@@ -66,7 +66,7 @@ if ($Scope =~ /queue/) {
@types = qw(Cc AdminCc);
}
elsif ($Suffix eq 'Group') {
- @types = qw(Requestor Cc AdminCc Watcher);
+ @types = qw(Owner Requestor Cc AdminCc Watcher);
}
else {
@types = qw(Requestor Cc AdminCc Watcher Owner QueueCc QueueAdminCc QueueWatcher);