merging 3.8.7!!!
[freeside.git] / rt / html / Search / Build.html
diff --git a/rt/html/Search/Build.html b/rt/html/Search/Build.html
deleted file mode 100644 (file)
index fa84f42..0000000
+++ /dev/null
@@ -1,832 +0,0 @@
-%# 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,
-    Format => $Format,
-    Query => $Query,
-    Order => $Order,
-    OrderBy => $OrderBy,
-    Rows => $RowsPerPage
-&>
-
-<form method="post" action="Build.html" name="BuildQuery">
-<input type="hidden" class="hidden" name="SearchId" value="<%$SearchId%>" />
-<input type="hidden" class="hidden" name="Query" value="<%$Query%>" />
-<input type="hidden" class="hidden" name="Format" value="<%$Format%>" />
-<table width="100%" border="0" cellpadding="5">
-<tr valign="top">
-<td class="boxcontainer" rowspan="2" width="65%">
-<& Elements/PickCriteria, query => $Query, cfqueues => $queues &>
-<& /Elements/Submit, Caption => loc('Add these terms to your search'), Label => loc('Add'), Name => 'AddClause'&>
-</td>
-
-<td>
-<& Elements/EditQuery,
-    %ARGS,
-    actions => \@actions,
-    optionlist => $optionlist,
-    Description => $Description &>
-<& /Elements/Submit, Label => loc('Add and Search'), Name => 'DoSearch'&>
-</td>
-</tr>
-
-<tr valign="top">
-<td>
-<& Elements/EditSearches, CurrentSearch => $search_hash, Dirty => $dirty, SearchId => $SearchId &>
-</td>
-</tr>
-
-<tr>
-<td colspan="2" class="boxcontainer">
-
-<& Elements/DisplayOptions, %ARGS, Format=> $Format,
-AvailableColumns => $AvailableColumns,  CurrentFormat => $CurrentFormat, RowsPerPage => $RowsPerPage, OrderBy => $OrderBy, Order => $Order &>
-<& /Elements/Submit, Label => loc('Add and Search'), Name => 'DoSearch'&>
-</td>
-</tr>
-</table>
-</form>
-
-<%INIT>
-use RT::Interface::Web::QueryBuilder;
-use RT::Interface::Web::QueryBuilder::Tree;
-
-my $search_hash = {};
-my $search;
-my $title = loc("Query Builder");
-
-# {{{ Clear out unwanted data
-if ( $NewQuery or $ARGS{'Delete'} ) {
-
-    # Wipe all data-carrying variables clear if we want a new
-    # search, or we're deleting an old one..
-    $Query       = '';
-    $Format      = '';
-    $Description = '';
-    $SearchId    = '';
-    $Order       = '';
-    $OrderBy     = '';
-    $RowsPerPage = undef;
-
-    # ($search hasn't been set yet; no need to clear)
-
-    # ..then wipe the session out..
-    undef $session{'CurrentSearchHash'};
-
-    # ..and the search results.
-    $session{'tickets'}->CleanSlate() if defined $session{'tickets'};
-}
-
-# }}}
-
-if (ref $OrderBy eq "ARRAY") {
-    $OrderBy = join("|", @$OrderBy);
-}
-if (ref $Order eq "ARRAY") {
-    $Order = join("|", @$Order);
-}
-
-# {{{ Attempt to load what we can from the session, set defaults
-
-# We don't read or write to the session again until the end
-$search_hash = $session{'CurrentSearchHash'};
-
-# Read from user preferences
-my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
-
-# These variables are what define a search_hash; this is also
-# where we give sane defaults.
-$Query       ||= $search_hash->{'Query'};
-$Format      ||= $search_hash->{'Format'} || $prefs->{'Format'};
-$Description ||= $search_hash->{'Description'};
-$SearchId    ||= $search_hash->{'SearchId'} || 'new';
-$Order       ||= $search_hash->{'Order'} || $prefs->{'Order'} || 'ASC';
-$OrderBy     ||= $search_hash->{'OrderBy'} || $prefs->{'OrderBy'} || 'id';
-
-unless ( defined $RowsPerPage ) {
-    if ( defined $search_hash->{'RowsPerPage'} ) {
-        $RowsPerPage = $search_hash->{'RowsPerPage'};
-    }
-    elsif ( defined $prefs->{'RowsPerPage'} ) {
-        $RowsPerPage = $prefs->{'RowsPerPage'};
-    }
-    else {
-        $RowsPerPage = 50;
-    }
-}
-
-  $search ||= $search_hash->{'Object'};
-
-# }}}
-
-my @actions = ();
-
-# Clean unwanted junk from the format
-$Format = $m->comp( '/Elements/ScrubHTML', Content => $Format ) if ($Format);
-
-# {{{ If we're asked to delete the current search, make it go away and reset the search parameters
-if ( $ARGS{'Delete'} ) {
-
-    # We set $SearchId to 'new' above already, so peek into the %ARGS
-    my ($container_object, $search_id) = _parse_saved_search ($ARGS{'SearchId'});
-    if ($container_object && $container_object->id) {
-       # We have the object the entry is an attribute on; delete the
-       # entry..
-       $container_object->Attributes->DeleteEntry(
-            Name => 'SavedSearch',
-           id   => $search_id
-            );
-    }
-}
-
-# }}}
-
-# {{{ If the user wants to copy a search, uncouple from the one that this was based on, but don't erase the $Query or $Format
-if ( $ARGS{'CopySearch'} ) {
-    $SearchId    = 'new';
-    $search      = undef;
-    $Description = loc( "[_1] copy", $Description );
-}
-
-# }}}
-
-# {{{ if we're asked to revert the current search, we just want to load it
-if ( $ARGS{'Revert'} ) {
-    $ARGS{'LoadSavedSearch'} = $SearchId;
-}
-
-# }}}
-
-# {{{ if we're asked to load a search, load it.
-
-if ( my ($container_object, $search_id ) = _parse_saved_search ($ARGS{'LoadSavedSearch'})) {
-    $search = $container_object->Attributes->WithId($search_id);
-
-    # We have a $search and now; import the others
-    $SearchId    = $ARGS{'LoadSavedSearch'};
-    $Description = $search->Description;
-    $Format      = $search->SubValue('Format');
-    $Query       = $search->SubValue('Query');
-    $Order       = $search->SubValue('Order');
-    $OrderBy     = $search->SubValue('OrderBy');
-    $RowsPerPage = $search->SubValue('RowsPerPage');
-}
-
-# }}}
-
-# {{{ if we're asked to save the current search, save it
-if ( $ARGS{'Save'} ) {
-    if ( $search && $search->id ) {
-       # permission check
-       if ($search->Object->isa('RT::System')) {
-           unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
-               Abort("No permission to save system-wide searches");
-           }
-       }
-
-        # This search is based on a previously loaded search -- so
-        # just update the current search object with new values
-        $search->SetSubValues(
-            Format      => $Format,
-            Query       => $Query,
-            Order       => $Order,
-            OrderBy     => $OrderBy,
-            RowsPerPage => $RowsPerPage,
-        );
-        $search->SetDescription($Description);
-
-    }
-    elsif ( $SearchId eq 'new' ) {
-        my $saved_search = RT::SavedSearch->new( $session{'CurrentUser'} );
-        my ( $ok, $search_msg ) = $saved_search->Save(
-            Privacy      => $ARGS{'Owner'},
-            Name         => $Description,
-            SearchParams => {
-                Format      => $Format,
-                Query       => $Query,
-                Order       => $Order,
-                OrderBy     => $OrderBy,
-                RowsPerPage => $RowsPerPage } );
-
-       if ($ok) {
-           $search = $session{'CurrentUser'}->UserObj->Attributes->WithId($saved_search->Id);
-           # Build new SearchId
-           $SearchId =
-                   ref( $session{'CurrentUser'}->UserObj ) . '-'
-                       . $session{'CurrentUser'}->UserObj->Id
-                       . '-SavedSearch-'
-                       . $search->Id;
-       }
-       else {
-           push @actions, [ loc("Can't find a saved search to work with").': '.loc($search_msg), 0 ];
-       }
-    }
-    else {
-        push @actions, [ loc("Can't save this search"), 0 ];
-    }
-
-}
-
-# }}}
-
-
-# {{{ Parse the query
-use Regexp::Common qw /delimited/;
-
-# States
-use constant VALUE   => 1;
-use constant AGGREG  => 2;
-use constant OP      => 4;
-use constant PAREN   => 8;
-use constant KEYWORD => 16;
-
-my $_match = sub {
-
-    # Case insensitive equality
-    my ( $y, $x ) = @_;
-    return 1 if $x =~ /^$y$/i;
-
-    #  return 1 if ((lc $x) eq (lc $y)); # Why isnt this equiv?
-    return 0;
-};
-
-my $ParseQuery = sub {
-    my $string  = shift;
-    my $tree    = shift;
-    my $actions = shift;
-    my $want    = KEYWORD | PAREN;
-    my $last    = undef;
-
-    my $depth = 1;
-
-    # make a tree root
-    $$tree = RT::Interface::Web::QueryBuilder::Tree->new;
-    my $root       = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $$tree );
-    my $parentnode = $root;
-
-    # on new searches, we're passed undef but still need to construct the
-    # RT::Interface::Web::QueryBuilder::Tree.  Quiet warning
-    return unless defined $string;
-
-    # get the FIELDS from Tickets_Overlay
-    my $tickets = new RT::Tickets( $session{'CurrentUser'} );
-    my %FIELDS  = %{ $tickets->FIELDS };
-
-    # Lower Case version of FIELDS, for case insensitivity
-    my %lcfields = map { ( lc($_) => $_ ) } ( keys %FIELDS );
-
-    my @tokens     = qw[VALUE AGGREG OP PAREN KEYWORD];
-    my $re_aggreg  = qr[(?i:AND|OR)];
-    my $re_value   = qr[$RE{delimited}{-delim=>qq{\'\"}}|\d+];
-    my $re_keyword = qr[$RE{delimited}{-delim=>qq{\'\"}}|(?:\{|\}|\w|\.)+];
-    my $re_op      =
-      qr[=|!=|>=|<=|>|<|(?i:IS NOT)|(?i:IS)|(?i:NOT LIKE)|(?i:LIKE)]
-      ;    # long to short
-    my $re_paren = qr'\(|\)';
-
-    # assume that $ea is AND if it is not set
-    my ( $ea, $key, $op, $value ) = ( "AND", "", "", "" );
-
-    # order of matches in the RE is important.. op should come early,
-    # because it has spaces in it.  otherwise "NOT LIKE" might be parsed
-    # as a keyword or value.
-
-    while (
-        $string =~ /(
-                      $re_aggreg
-                      |$re_op
-                      |$re_keyword
-                      |$re_value
-                      |$re_paren
-                     )/igx
-      )
-    {
-        my $val     = $1;
-        my $current = 0;
-
-        # Highest priority is last
-        $current = OP    if $_match->( $re_op,    $val );
-        $current = VALUE if $_match->( $re_value, $val );
-        $current = KEYWORD
-          if $_match->( $re_keyword, $val ) && ( $want & KEYWORD );
-        $current = AGGREG if $_match->( $re_aggreg, $val );
-        $current = PAREN  if $_match->( $re_paren,  $val );
-
-        unless ( $current && $want & $current ) {
-
-            # Error
-            # FIXME: I will only print out the highest $want value
-            my $token = $tokens[ ( ( log $want ) / ( log 2 ) ) ];
-            push @$actions,
-              [
-                loc("Error near ->[_1]<- expecting a [_2] in '[_3]'",
-                                  $val,              $token, $string ),
-                -1
-              ];
-        }
-
-        # State Machine:
-        my $parentdepth = $depth;
-
-        # Parens are highest priority
-        if ( $current & PAREN ) {
-            if ( $val eq "(" ) {
-                $depth++;
-
-                # make a new node that the clauses can be children of
-                $parentnode = RT::Interface::Web::QueryBuilder::Tree->new( $ea, $parentnode );
-            }
-            else {
-                $depth--;
-                $parentnode = $parentnode->getParent();
-            }
-
-            $want = KEYWORD | PAREN | AGGREG;
-        }
-        elsif ( $current & AGGREG ) {
-            $ea   = $val;
-            $parentnode->setNodeValue($ea);
-            $want = KEYWORD | PAREN;
-        }
-        elsif ( $current & KEYWORD ) {
-            $key  = $val;
-            $want = OP;
-        }
-        elsif ( $current & OP ) {
-            $op   = $val;
-            $want = VALUE;
-        }
-        elsif ( $current & VALUE ) {
-            $value = $val;
-
-            # Remove surrounding quotes from $key, $val
-            # (in future, simplify as for($key,$val) { action on $_ })
-            if ( $key =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
-                substr( $key, 0,  1 ) = "";
-                substr( $key, -1, 1 ) = "";
-            }
-            if ( $val =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
-                substr( $val, 0,  1 ) = "";
-                substr( $val, -1, 1 ) = "";
-            }
-
-            # Unescape escaped characters
-            $key =~ s!\\(.)!$1!g;
-            $val =~ s!\\(.)!$1!g;
-
-            my $class;
-
-            my ($key_base, $subkey)  = split(/\./,$key,2);
-            $key_base =~ s/\..*$//; # Strip off .EmailAddress, for example
-
-            if ( exists $lcfields{lc $key_base } ) {
-                $key   = $lcfields{lc $key_base } . (defined $subkey ? '.'.$subkey : '');
-                $class = $FIELDS{$key_base}->[0];
-            }
-            elsif ( $key =~ /^C(?:ustom)?F(?:ield)?\.{(.*)}$/i ) {
-                $class = $FIELDS{'CF'}->[0];
-            }
-
-            if ( $class ne 'INT' ) {
-                $val = "'$val'";
-            }
-
-            push @$actions, [ loc("Unknown field: [_1]", $key), -1 ] unless $class;
-
-            $want = PAREN | AGGREG;
-        }
-        else {
-            push @$actions, [ loc("I'm lost"), -1 ];
-        }
-
-        if ( $current & VALUE ) {
-            if ( $key =~ /^CF./ ) {
-                $key = "'" . $key . "'";
-            }
-            my $clause = {
-                Key   => $key,
-                Op    => $op,
-                Value => $val
-            };
-
-            # explicity add a child to it
-            RT::Interface::Web::QueryBuilder::Tree->new( $clause, $parentnode );
-
-            ( $ea, $key, $op, $value ) = ( "", "", "", "" );
-
-        }
-
-        $last = $current;
-    }    # while
-
-    push @$actions, [ loc("Incomplete query"), -1 ]
-      unless ( ( $want | PAREN ) || ( $want | KEYWORD ) );
-
-    push @$actions, [ loc("Incomplete Query"), -1 ]
-      unless ( $last && ( $last | PAREN ) || ( $last || VALUE ) );
-
-    # This will never happen, because the parser will complain
-    push @$actions, [ loc("Mismatched parentheses"), -1 ]
-      unless $depth == 1;
-};
-
-my $tree;
-{
-    my @parsing_errors;
-    $ParseQuery->( $Query, \$tree, \@parsing_errors );
-
-    # if parsing went poorly, send them to the edit page
-    # to fix it
-    if ( @parsing_errors ) {
-        return $m->comp(
-            "Edit.html",
-            Query   => $Query,
-            actions => \@parsing_errors
-        );
-    }
-}
-
-$Query  = "";
-
-my @options = $tree->GetDisplayedNodes;
-
-my @current_values = grep { defined } @options[@clauses];
-
-# {{{ Move things around
-if ( $ARGS{"Up"} ) {
-    if (@current_values) {
-        foreach my $value (@current_values) {
-            my $index = $value->getIndex();
-            if ( $value->getIndex() > 0 ) {
-                my $parent = $value->getParent();
-                $parent->removeChild($index);
-                $parent->insertChild( $index - 1, $value );
-                $value = $parent->getChild( $index - 1 );
-            }
-            else {
-                push( @actions, [ loc("error: can't move up"), -1 ] );
-            }
-        }
-    }
-    else {
-        push( @actions, [ loc("error: nothing to move"), -1 ] );
-    }
-}
-elsif ( $ARGS{"Down"} ) {
-    if (@current_values) {
-        foreach my $value (@current_values) {
-            my $index  = $value->getIndex();
-            my $parent = $value->getParent();
-            if ( $value->getIndex() < ( $parent->getChildCount - 1 ) ) {
-                $parent->removeChild($index);
-                $parent->insertChild( $index + 1, $value );
-                $value = $parent->getChild( $index + 1 );
-            }
-            else {
-                push( @actions, [ loc("error: can't move down"), -1 ] );
-            }
-        }
-    }
-    else {
-        push( @actions, [ loc("error: nothing to move"), -1 ] );
-    }
-}
-elsif ( $ARGS{"Left"} ) {
-    if (@current_values) {
-        foreach my $value (@current_values) {
-            my $parent      = $value->getParent();
-            my $grandparent = $parent->getParent();
-            if ( !$grandparent->isRoot ) {
-                my $index = $parent->getIndex();
-                $parent->removeChild($value);
-                $grandparent->insertChild( $index, $value );
-                if ( $parent->isLeaf() ) {
-                    $grandparent->removeChild($parent);
-                }
-            }
-            else {
-                push( @actions, [ loc("error: can't move left"), -1 ] );
-            }
-        }
-    }
-    else {
-        push( @actions, [ loc("error: nothing to move"), -1 ] );
-    }
-}
-elsif ( $ARGS{"Right"} ) {
-    if (@current_values) {
-        foreach my $value (@current_values) {
-            my $parent = $value->getParent();
-            my $index  = $value->getIndex();
-            my $newparent;
-            if ( $index > 0 ) {
-                my $sibling = $parent->getChild( $index - 1 );
-                if ( ref( $sibling->getNodeValue ) ) {
-                    $parent->removeChild($value);
-                    my $newtree = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $parent );
-                    $newtree->addChild($value);
-                }
-                else {
-                    $parent->removeChild($index);
-                    $sibling->addChild($value);
-                }
-            }
-            else {
-                $parent->removeChild($value);
-                $newparent = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $parent );
-                $newparent->addChild($value);
-            }
-        }
-    }
-    else {
-        push( @actions, [ loc("error: nothing to move"), -1 ] );
-    }
-}
-elsif ( $ARGS{"DeleteClause"} ) {
-    if (@current_values) {
-        $_->getParent()->removeChild($_) for @current_values;
-               @current_values = ();
-    }
-    else {
-        push( @actions, [ loc("error: nothing to delete"), -1 ] );
-    }
-}
-elsif ( $ARGS{"Toggle"} ) {
-    my $ea;
-    if (@current_values) {
-        foreach my $value (@current_values) {
-            my $parent = $value->getParent();
-
-            if ( $parent->getNodeValue eq 'AND' ) {
-                $parent->setNodeValue('OR');
-            }
-            else {
-                $parent->setNodeValue('AND');
-            }
-        }
-    }
-    else {
-        push( @actions, [ loc("error: nothing to toggle"), -1 ] );
-    }
-}
-
-# {{{ Try to find if we're adding a clause
-foreach my $arg ( keys %ARGS ) {
-    if (
-            $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 ( $keyword, $op, $value );
-
-        #figure out if it's a grouping
-        if ( $ARGS{ $field . "Field" } ) {
-            $keyword = $ARGS{ $field . "Field" };
-        }
-        else {
-            $keyword = $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 .. @ops - 1 ) {
-            my ( $op, $value ) = ( $ops[$i], $values[$i] );
-            next if $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 = "'$value'";
-            }
-
-            my $clause = {
-                Key   => $keyword,
-                Op    => $op,
-                Value => $value
-            };
-
-            my $newnode = RT::Interface::Web::QueryBuilder::Tree->new($clause);
-            if (@current_values) {
-                foreach my $value (@current_values) {
-                    my $newindex = $value->getIndex() + 1;
-                    $value->insertSibling( $newindex, $newnode );
-                    $value = $newnode;
-                }
-            }
-            else {
-                $tree->getChild(0)->addChild($newnode);
-                @current_values = $newnode;
-            }
-            $newnode->getParent()->setNodeValue( $ARGS{'AndOr'} );
-        }
-    }
-}
-
-# }}}
-
-$tree->PruneChildlessAggregators;
-
-# }}}
-
-# {{{ Rebuild $Query based on the additions / movements
-$Query      = "";
-my $optionlist_arrayref;
-
-($Query, $optionlist_arrayref) = $tree->GetQueryAndOptionList(\@current_values);
-  
-my $optionlist = join "\n", map { qq(<option value="$_->{INDEX}" $_->{SELECTED}>) 
-                                  . ("&nbsp;" x (5 * $_->{DEPTH}))
-                                  . $m->interp->apply_escapes($_->{TEXT}, 'h') . qq(</option>) } @$optionlist_arrayref;
-
-
-
-
-# }}}
-
-# }}}
-
-my $queues = $tree->GetReferencedQueues;
-
-# {{{ Deal with format changes
-my ( $AvailableColumns, $CurrentFormat );
-( $Format, $AvailableColumns, $CurrentFormat ) = $m->comp(
-    'Elements/BuildFormatString',
-    cfqueues => $queues,
-    %ARGS, Format => $Format
-);
-
-# }}}
-
-# {{{ If we're modifying an old query, check if it has changed
-my $dirty = 0;
-$dirty = 1
-  if defined $search
-  and ($search->SubValue('Format') ne $Format
-    or $search->SubValue('Query')       ne $Query
-    or $search->SubValue('Order')       ne $Order
-    or $search->SubValue('OrderBy')     ne $OrderBy
-    or $search->SubValue('RowsPerPage') ne $RowsPerPage );
-
-# }}}
-
-# {{{ Push the updates into the session so we don't loose 'em
-$search_hash->{'SearchId'}    = $SearchId;
-$search_hash->{'Format'}      = $Format;
-$search_hash->{'Query'}       = $Query;
-$search_hash->{'Description'} = $Description;
-$search_hash->{'Object'}      = $search;
-$search_hash->{'Order'}       = $Order;
-$search_hash->{'OrderBy'}     = $OrderBy;
-$search_hash->{'RowsPerPage'} = $RowsPerPage;
-
-$session{'CurrentSearchHash'} = $search_hash;
-
-# }}}
-
-# {{{ Show the results, if we were asked.
-if ( $ARGS{"DoSearch"}) {
-    $m->comp(
-        "Results.html",
-        Query   => $Query,
-        Format  => $Format,
-        Order   => $Order,
-        OrderBy => $OrderBy,
-        Rows    => $RowsPerPage
-    );
-    $m->comp('/Elements/Footer');
-    $m->abort();
-}
-
-# }}}
-
-# {{{ Build a querystring for the tabs
-
-my $QueryString;
-if ($NewQuery) {
-    $QueryString = '?NewQuery=1';
-}
-else {
-    $QueryString = '?'
-      . $m->comp(
-        '/Elements/QueryString',
-        Query   => $Query,
-        Format  => $Format,
-        Order   => $Order,
-        OrderBy => $OrderBy,
-        Rows    => $RowsPerPage
-      )
-      if ($Query);
-}
-
-# }}}
-
-</%INIT>
-
-<%ARGS>
-$NewQuery => 0
-$SearchId => undef
-$Query => undef
-$Format => undef 
-$Description => undef
-$Order => undef
-$OrderBy => undef
-$RowsPerPage => undef
-$HideResults => 0
-@clauses => ()
-</%ARGS>
-