diff options
Diffstat (limited to 'rt/share/html/Search/Build.html')
-rw-r--r-- | rt/share/html/Search/Build.html | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/rt/share/html/Search/Build.html b/rt/share/html/Search/Build.html new file mode 100644 index 000000000..0fd79742a --- /dev/null +++ b/rt/share/html/Search/Build.html @@ -0,0 +1,323 @@ +%# 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 }}} +%# +%# Data flow here: +%# The page receives a Query from the previous page, and maybe arguments +%# corresponding to actions. (If it doesn't get a Query argument, it pulls +%# one out of the session hash. Also, it could be getting just a raw query from +%# Build/Edit.html (Advanced).) +%# +%# After doing some stuff with default arguments and saved searches, the ParseQuery +%# function (which is similar to, but not the same as, _parser in RT/Tickets_Overlay_SQL) +%# converts the Query into a RT::Interface::Web::QueryBuilder::Tree. This mason file +%# then adds stuff to or modifies the tree based on the actions that had been requested +%# by clicking buttons. It then calls GetQueryAndOptionList on the tree to generate +%# the SQL query (which is saved as a hidden input) and the option list for the Clauses +%# box in the top right corner. +%# +%# Worthwhile refactoring: the tree manipulation code for the actions could use some cleaning +%# up. The node-adding code is different in the "add" actions from in ParseQuery, which leads +%# to things like ParseQuery correctly not quoting numbers in numerical fields, while the "add" +%# action does quote it (this breaks SQLite). +%# +<& /Elements/Header, Title => $title &> +<& /Ticket/Elements/Tabs, + current_tab => "Search/Build.html?".$QueryString, + Title => $title, + %query, + SavedSearchId => $saved_search{'Id'}, + SavedChartSearchId => $ARGS{SavedChartSearchId}, +&> + +<form method="post" action="Build.html" name="BuildQuery"> +<input type="hidden" class="hidden" name="SavedSearchId" value="<% $saved_search{'Id'} %>" /> +<input type="hidden" class="hidden" name="SavedChartSearchId" value="<% $ARGS{'SavedChartSearchId'} %>" /> +<input type="hidden" class="hidden" name="Query" value="<% $query{'Query'} %>" /> +<input type="hidden" class="hidden" name="Format" value="<% $query{'Format'} %>" /> + + + + +<div id="pick-criteria"> + <& Elements/PickCriteria, query => $query{'Query'}, cfqueues => $queues &> +</div> +<& /Elements/Submit, Label => loc('Add these terms'), Name => 'AddClause'&> +<& /Elements/Submit, Label => loc('Add these terms and Search'), Name => 'DoSearch'&> + + +<div id="editquery"> +<& Elements/EditQuery, + %ARGS, + actions => \@actions, + optionlist => $optionlist, + Description => $saved_search{'Description'}, + &> +</div> +<div id="editsearches"> + <& Elements/EditSearches, %saved_search, CurrentSearch => \%query &> +</div> + +<span id="display-options"> +<& Elements/DisplayOptions, + %ARGS, %query, + AvailableColumns => $AvailableColumns, + CurrentFormat => $CurrentFormat, +&> +<& /Elements/Submit, Label => loc('Update format and Search'), Name => 'DoSearch', id=>"formatbuttons"&> +</span> +</form> + +<%INIT> +use RT::Interface::Web::QueryBuilder; +use RT::Interface::Web::QueryBuilder::Tree; + +$ARGS{SavedChartSearchId} ||= 'new'; + +my $title = loc("Query Builder"); + +my %query; +for( qw(Query Format OrderBy Order RowsPerPage) ) { + $query{$_} = $ARGS{$_}; +} + +my %saved_search; +my @actions = $m->comp( 'Elements/EditSearches:Init', %ARGS, Query => \%query, SavedSearch => \%saved_search); + +if ( $NewQuery ) { + + # Wipe all data-carrying variables clear if we want a new + # search, or we're deleting an old one.. + %query = (); + %saved_search = ( Id => 'new' ); + + # ..then wipe the session out.. + delete $session{'CurrentSearchHash'}; + + # ..and the search results. + $session{'tickets'}->CleanSlate if defined $session{'tickets'}; +} + +{ # Attempt to load what we can from the session and preferences, set defaults + + my $current = $session{'CurrentSearchHash'}; + my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {}; + my $default = { Query => '', Format => '', OrderBy => 'id', Order => 'ASC', RowsPerPage => 50 }; + + for( qw(Query Format OrderBy Order RowsPerPage) ) { + $query{$_} = $current->{$_} unless defined $query{$_}; + $query{$_} = $prefs->{$_} unless defined $query{$_}; + $query{$_} = $default->{$_} unless defined $query{$_}; + } + + for( qw(Order OrderBy) ) { + if (ref $query{$_} eq "ARRAY") { + $query{$_} = join( '|', @{ $query{$_} } ); + } + } + if ( $query{'Format'} ) { + # Clean unwanted junk from the format + $query{'Format'} = $m->comp( '/Elements/ScrubHTML', Content => $query{'Format'} ); + } +} + +my $ParseQuery = sub { + my ($string, $results) = @_; + + my $tree = RT::Interface::Web::QueryBuilder::Tree->new('AND'); + @$results = $tree->ParseSQL( Query => $string, CurrentUser => $session{'CurrentUser'} ); + + return $tree; +}; + +my @parse_results; +my $tree = $ParseQuery->( $query{'Query'}, \@parse_results ); + +# if parsing went poorly, send them to the edit page to fix it +if ( @parse_results ) { + return $m->comp( "Edit.html", Query => $query{'Query'}, actions => \@actions ); +} + +my @options = $tree->GetDisplayedNodes; +my @current_values = grep defined, @options[@clauses]; +my @new_values = (); + +# {{{ Try to find if we're adding a clause +foreach my $arg ( keys %ARGS ) { + next unless $arg =~ m/^ValueOf(\w+|'CF.{.*?}')$/ + && ( ref $ARGS{$arg} eq "ARRAY" + ? grep $_ ne '', @{ $ARGS{$arg} } + : $ARGS{$arg} ne '' ); + + # We're adding a $1 clause + my $field = $1; + + my ($op, $value); + + #figure out if it's a grouping + my $keyword = $ARGS{ $field . "Field" } || $field; + + my ( @ops, @values ); + if ( ref $ARGS{ 'ValueOf' . $field } eq "ARRAY" ) { + # we have many keys/values to iterate over, because there is + # more than one CF with the same name. + @ops = @{ $ARGS{ $field . 'Op' } }; + @values = @{ $ARGS{ 'ValueOf' . $field } }; + } + else { + @ops = ( $ARGS{ $field . 'Op' } ); + @values = ( $ARGS{ 'ValueOf' . $field } ); + } + $RT::Logger->error("Bad Parameters passed into Query Builder") + unless @ops == @values; + + for ( my $i = 0; $i < @ops; $i++ ) { + my ( $op, $value ) = ( $ops[$i], $values[$i] ); + next if !defined $value || $value eq ''; + + if ( $value eq 'NULL' && $op =~ /=/ ) { + if ( $op eq '=' ) { + $op = "IS"; + } + elsif ( $op eq '!=' ) { + $op = "IS NOT"; + } + + # This isn't "right", but... + # It has to be this way until #5182 is fixed + $value = "'NULL'"; + } + else { + $value =~ s/'/\\'/g; + $value = "'$value'" unless $value =~ /^\d+$/; + } + + my $clause = { + Key => $keyword, + Op => $op, + Value => $value + }; + + push @new_values, RT::Interface::Web::QueryBuilder::Tree->new($clause); + } +} + +# }}} + +push @actions, $m->comp('Elements/EditQuery:Process', + %ARGS, + Tree => $tree, + Selected => \@current_values, + New => \@new_values, +); + +# {{{ Rebuild $Query based on the additions / movements + +my $optionlist_arrayref; +($query{'Query'}, $optionlist_arrayref) = $tree->GetQueryAndOptionList(\@current_values); + +my $optionlist = join "\n", map { qq(<option value="$_->{INDEX}" $_->{SELECTED}>) + . (" " x (5 * $_->{DEPTH})) + . $m->interp->apply_escapes($_->{TEXT}, 'h') . qq(</option>) } @$optionlist_arrayref; + +# }}} + +my $queues = $tree->GetReferencedQueues; + +# {{{ Deal with format changes +my ( $AvailableColumns, $CurrentFormat ); +( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp( + 'Elements/BuildFormatString', + %ARGS, + cfqueues => $queues, + Format => $query{'Format'}, +); + +# }}} + +# if we're asked to save the current search, save it +push @actions, $m->comp( 'Elements/EditSearches:Save', %ARGS, Query => \%query, SavedSearch => \%saved_search); + +# {{{ Push the updates into the session so we don't loose 'em + +$session{'CurrentSearchHash'} = { + %query, + SearchId => $saved_search{'Id'}, + Object => $saved_search{'Object'}, + Description => $saved_search{'Description'}, +}; + +# }}} + +# {{{ Show the results, if we were asked. + +if ( $ARGS{'DoSearch'} ) { + $m->comp( 'Results.html', %query, SavedChartSearchId => $ARGS{'SavedChartSearchId'}, ); + $m->comp( '/Elements/Footer' ); + $m->abort; +} + +# }}} + +# {{{ Build a querystring for the tabs + +my $QueryString = ''; +if ($NewQuery) { + $QueryString = 'NewQuery=1'; +} +elsif ( $query{'Query'} ) { + $QueryString = $m->comp('/Elements/QueryString', %query ); +} + +# }}} + +</%INIT> + +<%ARGS> +$NewQuery => 0 +@clauses => () +</%ARGS> |