X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FInterface%2FWeb%2FQueryBuilder%2FTree.pm;h=e672d8e4c0b4ac124b3e5cb73cbcdab130a68467;hb=624b2d44625f69d71175c3348cae635d580c890b;hp=467627313ead8dfdc2e034304b765397bb6f60ab;hpb=ef20b2b6b1feb47ad02b5ff7525f1a0fd11d0fa4;p=freeside.git diff --git a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm index 467627313..e672d8e4c 100755 --- a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm +++ b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm @@ -1,8 +1,8 @@ # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: -# -# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC +# +# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -24,7 +24,7 @@ # 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/copyleft/gpl.html. +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: @@ -45,11 +45,13 @@ # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} + package RT::Interface::Web::QueryBuilder::Tree; use strict; use warnings; +use Tree::Simple qw/use_weak_refs/; use base qw/Tree::Simple/; =head1 NAME @@ -77,13 +79,15 @@ on the root node passed to it.) sub TraversePrePost { my ($self, $prefunc, $postfunc) = @_; - $prefunc->($self); - + # XXX: if pre or post action changes siblings (delete or adds) + # we could have problems + $prefunc->($self) if $prefunc; + foreach my $child ($self->getAllChildren()) { $child->TraversePrePost($prefunc, $postfunc); } - $postfunc->($self); + $postfunc->($self) if $postfunc; } =head2 GetReferencedQueues @@ -103,10 +107,11 @@ sub GetReferencedQueues { my $node = shift; return if $node->isRoot; + return unless $node->isLeaf; my $clause = $node->getNodeValue(); - - if ( ref($clause) and $clause->{Key} eq 'Queue' ) { + + if ( $clause->{Key} eq 'Queue' ) { $queues->{ $clause->{Value} } = 1; }; } @@ -133,55 +138,13 @@ sub GetQueryAndOptionList { my $self = shift; my $selected_nodes = shift; - my $optionlist = []; - - my $i = 0; - - $self->TraversePrePost( - sub { # This is called before recursing to the node's children. - my $node = shift; - - return if $node->isRoot or $node->getParent->isRoot; - - my $clause = $node->getNodeValue(); - my $str = ' '; - my $aggregator_context = $node->getParent()->getNodeValue(); - $str = $aggregator_context . " " if $node->getIndex() > 0; - - if ( ref($clause) ) { # ie, it's a leaf - $str .= - $clause->{Key} . " " . $clause->{Op} . " " . $clause->{Value}; - } - - unless ($node->getParent->getParent->isRoot) { - # used to check !ref( $parent->getNodeValue() ) ) - if ( $node->getIndex() == 0 ) { - $str = '( ' . $str; - } - } - - push @$optionlist, { - TEXT => $str, - INDEX => $i, - SELECTED => (grep { $_ == $node } @$selected_nodes) ? 'SELECTED' : '', - DEPTH => $node->getDepth() - 1, - }; - - $i++; - }, sub { - # This is called after recursing to the node's children. - my $node = shift; - - return if $node->isRoot or $node->getParent->isRoot or $node->getParent->getParent->isRoot; - - # Only do this for the rightmost child. - return unless $node->getIndex == $node->getParent->getChildCount - 1; - - $optionlist->[-1]{TEXT} .= ' )'; - } - ); + my $list = $self->__LinearizeTree; + foreach my $e( @$list ) { + $e->{'DEPTH'} = $e->{'NODE'}->getDepth; + $e->{'SELECTED'} = (grep $_ == $e->{'NODE'}, @$selected_nodes)? qq[ selected="selected"] : ''; + } - return (join ' ', map { $_->{TEXT} } @$optionlist), $optionlist; + return (join ' ', map $_->{'TEXT'}, @$list), $list; } =head2 PruneChildLessAggregators @@ -195,23 +158,18 @@ sub PruneChildlessAggregators { my $self = shift; $self->TraversePrePost( - sub { - }, + undef, sub { my $node = shift; + return unless $node->isLeaf; - return if $node->isRoot or $node->getParent->isRoot; - # We're only looking for aggregators (AND/OR) return if ref $node->getNodeValue; - - return if $node->getChildCount != 0; - + + return if $node->isRoot; + # OK, this is a childless aggregator. Remove self. - $node->getParent->removeChild($node); - - # Deal with circular refs $node->DESTROY; } ); @@ -226,18 +184,106 @@ In fact, it's all of them but the root and its child. =cut sub GetDisplayedNodes { + return map $_->{NODE}, @{ (shift)->__LinearizeTree }; +} + + +sub __LinearizeTree { my $self = shift; - my @lines; - $self->traverse(sub { + my ($list, $i) = ([], 0); + + $self->TraversePrePost( sub { my $node = shift; + return if $node->isRoot; + + my $str = ''; + if( $node->getIndex > 0 ) { + $str .= " ". $node->getParent->getNodeValue ." "; + } - push @lines, $node unless $node->isRoot or $node->getParent->isRoot; + unless( $node->isLeaf ) { + $str .= '( '; + } else { + + my $clause = $node->getNodeValue; + $str .= $clause->{Key}; + $str .= " ". $clause->{Op}; + $str .= " ". $clause->{Value}; + + } + $str =~ s/^\s+|\s+$//; + + push @$list, { + NODE => $node, + TEXT => $str, + INDEX => $i, + }; + + $i++; + }, sub { + my $node = shift; + return if $node->isRoot; + return if $node->isLeaf; + $list->[-1]->{'TEXT'} .= ' )'; }); - return @lines; + return $list; } +sub ParseSQL { + my $self = shift; + my %args = ( + Query => '', + CurrentUser => '', #XXX: Hack + @_ + ); + my $string = $args{'Query'}; + + my @results; + + my %field = %{ RT::Tickets->new( $args{'CurrentUser'} )->FIELDS }; + my %lcfield = map { ( lc($_) => $_ ) } keys %field; + + my $node = $self; + + my %callback; + $callback{'OpenParen'} = sub { + $node = __PACKAGE__->new( 'AND', $node ); + }; + $callback{'CloseParen'} = sub { $node = $node->getParent }; + $callback{'EntryAggregator'} = sub { $node->setNodeValue( $_[0] ) }; + $callback{'Condition'} = sub { + my ($key, $op, $value) = @_; + + my ($main_key) = split /[.]/, $key; + + my $class; + if ( exists $lcfield{ lc $main_key } ) { + $class = $field{ $main_key }->[0]; + $key =~ s/^[^.]+/ $lcfield{ lc $main_key } /e; + } + unless( $class ) { + push @results, [ $args{'CurrentUser'}->loc("Unknown field: [_1]", $key), -1 ] + } + + $value =~ s/'/\\'/g; + if ( lc $op eq 'is' || lc $op eq 'is not' ) { + $value = 'NULL'; # just fix possible mistakes here + } elsif ( $value !~ /^[+-]?[0-9]+$/ ) { + $value = "'$value'"; + } + $key = "'$key'" if $key =~ /^CF./; + + my $clause = { Key => $key, Op => $op, Value => $value }; + $node->addChild( __PACKAGE__->new( $clause ) ); + }; + $callback{'Error'} = sub { push @results, @_ }; + + require RT::SQL; + RT::SQL::Parse($string, \%callback); + return @results; +} eval "require RT::Interface::Web::QueryBuilder::Tree_Vendor"; die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder/Tree_Vendor.pm});