%# BEGIN LICENSE BLOCK %# %# Copyright (c) 1996-2003 Jesse Vincent %# %# (Except where explictly superceded by other copyright notices) %# %# 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. %# %# Unless otherwise specified, all modifications, corrections or %# extensions to this work which alter its source code become the %# property of Best Practical Solutions, LLC when submitted for %# inclusion in the work. %# %# %# END LICENSE BLOCK <& /Elements/Header, Title => $title &> <& /Ticket/Elements/Tabs, current_tab => "Search/Build.html".$QueryString, Title => $title, Format => $Format, Query => $Query, Rows => $ARGS{'Rows'} &>
<& Elements/PickCriteria, query => $Query, cfqueues => \%queues, %ARGS &> <& /Elements/Submit, Caption => "Add additional criteria", Label => loc('Add'), Name => 'AddClause'&>
<& /Elements/TitleBoxStart, title => loc("Query") . ": " .$search_hash->{'Description'} &> <& Elements/NewListActions, actions => \@actions &>

%#
<& /Elements/TitleBoxEnd &>
<& Elements/EditSearches, CurrentSearch => $search_hash &>
<& Elements/DisplayOptions, Format=> ($Format||$search_hash->{'Format'}), AvailableColumns => $AvailableColumns, CurrentFormat => $CurrentFormat, %ARGS &>
<%INIT> my $search_hash = {}; my $search; my $title = loc("Query Builder"); if ($NewQuery) { $Query = ''; $Format = ''; $Description = ''; undef $session{'CurrentSearchHash'}; } else { $search_hash = $session{'CurrentSearchHash'}; $Query ||= $search_hash->{'Query'}; $Format ||= $search_hash->{'Format'}; $Description ||= $search_hash->{'Description'}; } my @actions = (); my %queues; $ARGS{'Format'} = $Format = $m->comp('/Elements/ScrubHTML', Content => $Format) if ($Format); if ( $ARGS{"DoSearch"} ) { $m->comp( "Results.html", Query => $Query, Format => $Format, OrderBy => $ARGS{OrderBy}, Rows => $ARGS{RowsPerPage}); $m->abort(); } # If the user wants a new search, clobber the search id if ( $ARGS{'CopySearch'} ) { $ARGS{'SearchId'} = 'new'; } # {{{ if we're asked to revert the current search, we just want to load it if ( $ARGS{'Revert'} ) { $ARGS{'LoadSavedSearch'} = $ARGS{'SearchId'}; $Format = undef; $Query = undef; } # }}} # {{{ if we're asked to load a search, load it. if ( $ARGS{'LoadSavedSearch'} =~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) { my $obj_type = $1; my $obj_id = $2; my $search_id = $3; # We explicitly list out the available types and # don't trust user input here if ( ( $obj_type eq 'RT::User' ) && ( $obj_id == $session{'CurrentUser'}->id ) ) { $search = $session{'CurrentUser'}->UserObj->Attributes->WithId($search_id); } elsif ($obj_type eq 'RT::Group') { my $group = RT::Group->new($session{'CurrentUser'}); $group->Load($obj_id); $search = $group->Attributes->WithId($search_id); } $search_hash->{'SearchId'} = $ARGS{'LoadSavedSearch'}; $ARGS{'Format'} = $Format = $search->SubValue('Format'); $ARGS{'Query'} = $Query = $search->SubValue('Query'); $SearchId = $ARGS{'LoadSavedSearch'}; } # }}} # if we've gotten to this point, without a $search, the user is doing _something_ other than # manipulating a search's saved status. whcih means we don't have a loaded # saved search. # we want to load the search they're working on and compare it to the currently # loaded search. this will let us present the right buttons. if ( !$search && $ARGS{'SearchId'} =~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) { my $obj_type = $1; my $obj_id = $2; my $search_id = $3; # XXX TODO This will only let users save personal searches # We explicitly list out the available types and # don't trust user input here if ( ( $obj_type eq 'RT::User' ) && ( $obj_id == $session{'CurrentUser'}->id ) ) { $search = $session{'CurrentUser'}->UserObj->Attributes->WithId($search_id); $search_hash->{'SearchId'} = $ARGS{'SearchId'}; } elsif ($obj_type eq 'RT::Group') { my $group = RT::Group->new($session{'CurrentUser'}); $group->Load($obj_id); $search = $group->Attributes->WithId($search_id); $search_hash->{'SearchId'} = $ARGS{'SearchId'}; } else { push @actions, [loc("Searches can't be associated with that kind of object"), 0]; } } if ($search) { $search_hash->{Description} = ( $search->Description() || loc('Untitled search') ); $search_hash->{Format} = $search->SubValue('Format'); $search_hash->{Query} = $search->SubValue('Query'); $search_hash->{Object} = $search; } else { $search_hash->{Description} = loc('Untitled search'); $search_hash->{Format} = ''; $search_hash->{Query} = ''; } # {{{ Parse the query my $items = ParseQuery( $Query, \@actions ); # if parsing went poorly, send them to the edit page to fix it if ( $actions[0] ) { $m->comp( "Edit.html", Query => $Query, actions => \@actions ); $m->abort(); } my @options; $Query = ""; %queues = (); build_array( \$Query, $items, "", "", 0, \@options, \%queues ); my $currentkey = ""; $currentkey = $options[$ARGS{clauses}]->{Key} if $ARGS{clauses}; # We can't check for the addclause button because hitting return in # a criterion will get lost otherwise if (1) { my ($key, $top); if ($currentkey) { $key = $currentkey; my ( $prefix, $depth, $num ) = parsekey($key); $top = 1 if $depth == 1; } else { $key = "{" . '0.0' . "}{Subkey}{1.0}"; $top = 1; } while ( keyexists( $items, $key ) ) { $key = nextkey($key); } foreach my $arg ( keys %ARGS ) { if ( $arg =~ m/ValueOf(.+)/ && $ARGS{$arg} ) { my $field = $1; my $keyword; #figure out if it's a grouping if ( $ARGS{ $field . "Field" } ) { $keyword = $ARGS{ $field . "Field" }; } else { $keyword = $field; } my $clause = { Key => $keyword, Op => $ARGS{ $field . 'Op' }, Value => "'$ARGS{'ValueOf' . $field}'" }; setvalue($items, $key, $clause); setaggregator( $items, $key, $ARGS{'AndOr'} ); if ($top) { # if the next item is a subkey, put it there my $newkey = appendsubkey( $items, $key ); my $oldkey = $key; movecurrent( $items, $newkey, \$oldkey ); } $currentkey = $key; $key = nextkey($key); } } } # {{{ Move things around if ( $ARGS{"Up"} ) { if ($currentkey) { # we can only move it up if it's not at the top my $prev = prevkey($currentkey); if ( swap( $items, $prev, $currentkey ) ) { $currentkey = "$prev"; } else { push( @actions, [ "error: can't move up", -1 ] ); } } else { push( @actions, [ "error: nothing to move", -1 ] ); } } elsif ( $ARGS{"Down"} ) { if ($currentkey) { # we can only move it down if it's not at the bottom my $newkey = nextkey($currentkey); if ( swap( $items, $newkey, $currentkey ) ) { $currentkey = "$newkey"; } else { push( @actions, [ "error: can't move down", -1 ] ); } } else { push( @actions, [ "error: nothing to move", -1 ] ); } } elsif ( $ARGS{"Left"} ) { if ($currentkey) { # we can only move it left if...what? my $parent = parentkey($currentkey); if ( $parent =~ m/^{0.0}.*/ && $parent ne "{0.0}" ) { my $newkey = appendkey( $items, $parent ); movecurrent( $items, $newkey, \$currentkey ); # if there was an empty group left behind, delete it my $subhash = gethash($items, $parent . "{Subkey}"); if ( !( keys %{$subhash} ) ) { deletevalue($items, $parent); reworkkeys( $items, $parent, \$currentkey ); } } else { push( @actions, [ "error: can't move left", -1 ] ); } } else { push( @actions, [ "error: nothing to move", -1 ] ); } } elsif ( $ARGS{"Right"} ) { if ($currentkey) { # you can't move right if you leave no siblings behind if ( keyexists( $items, nextkey($currentkey) ) || keyexists( $items, prevkey($currentkey) ) ) { # if the next item is a subkey, put it there my $newkey = appendsubkey( $items, $currentkey ); movecurrent( $items, $newkey, \$currentkey ); } else { push( @actions, [ "error: can't move right", -1 ] ); } } else { push( @actions, [ "error: nothing to move", -1 ] ); } } elsif ( $ARGS{"DeleteClause"} ) { if ($currentkey) { deletevalue($items, $currentkey); reworkkeys( $items, $currentkey, \$currentkey ); } else { push( @actions, [ "error: nothing to delete", -1 ] ); } } elsif ( $ARGS{"Toggle"} ) { my $ea = getaggregator( $items, $currentkey); if ($ea eq 'AND') { setaggregator( $items, $currentkey, 'OR' ); } else { setaggregator( $items, $currentkey, 'AND' ); } } elsif ( $ARGS{"Clear"} ) { $items = (); } # }}} # {{{ Query building magic $Query = ""; @options = (); %queues = (); build_array( \$Query, $items, "", "", 0, \@options, \%queues ); my $ea; my $i = 0; my $optionlist = ""; while ( my $val = shift @options ) { last if ( !$val->{Value} ); my $key = $val->{Key}; my ( $prefix, $depth, $num ) = parsekey($key); my $selected; next unless ( $val->{Value}->{Key} || $val->{Value}->{Subkey} ); $ea = $val->{EA}; if ( $depth > 0 ) { if ( $key eq $currentkey ) { $selected = "SELECTED"; } else { $selected = ""; } $optionlist .= "\n"; } $i++; } sub build_array { my $Query = shift; my $items = shift; my $parentkey = shift; my $ea = shift; my $i = shift; my ($keys, $queues) = @_; while ( my $item = $items->{ $i + 0 } ) { my $j = 0; while ( my $item = $items->{ $i + ( $j / 10 ) } ) { my $depth = $i; my $subkey = $item->{Subkey}; if ( defined $subkey && scalar keys %$subkey ) { my $sendkey = ""; if ($parentkey) { $sendkey = $parentkey . "{Subkey}{$i.$j}"; } else { $sendkey = "{$i.$j}"; } push @$keys, { Key => $sendkey, Value => $item, EA => $ea }; # no aggregator if it's first $$Query .= " " . $ea . " " if ($j > 0 && $$Query); $$Query .= "(" if $depth > 0; my $x = $i + 1; build_array( $Query, $subkey, $sendkey, $item->{EA}, $x, $keys, , $queues ); $$Query .= ")" if $depth > 0; } else { if ( $depth >= 1 ) { my $mykey; if ($parentkey) { $mykey = $parentkey . "{Subkey}{$i.$j}"; } else { $mykey = " " . $i . $j . " "; } $$Query .= " " . $ea . " " if ( $j > 0 ); $$Query .= "$item->{Key} $item->{Op} $item->{Value}" if $item->{Key}; push @$keys, { Key => $mykey, Value => $item, EA => $ea }; if ( $item->{Key} eq "Queue" ) { $queues->{ $item->{Value} } = 1; } } } $j++; } $i++; } } 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; sub ParseQuery { my $string = shift; my @actions = shift; my $want = KEYWORD | PAREN; my $last = undef; my $depth = 1; my $query = {}; my %depths; my %items; setvalue($query, "{0.0}", {}); setvalue($query, "{0.0}{Subkey}", {}); setvalue($query, "{0.0}{Subkey}{1.0}", {}); setvalue($query, "{0.0}{Subkey}{1.0}{Subkey}", {}); # 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'\(|\)'; my ( $ea, $key, $op, $value ) = ( "", "", "", "" ); # 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. my $num = 0; $depths{1} = 0; 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, [ "current: $current, want $want, Error near ->$val<- expecting a " . $token . " in '$string'\n", -1 ]; } # State Machine: my $parentdepth = $depth; # Parens are highest priority if ( $current & PAREN ) { if ( $val eq "(" ) { $num = 0; $depth++; $depths{$depth} = 0; my $hashkey; my @keys; $hashkey = "$depth" . "." . "$depths{$depth}" . ""; my $keystring = ""; while ( $parentdepth >= 1 ) { $hashkey = $parentdepth . ".$depths{$parentdepth}"; push @keys, $hashkey; $parentdepth--; } # build up the keystring from the top $keystring .= "{" . ( pop @keys ) . "}"; while ( my $k = pop @keys ) { $keystring .= "{Subkey}{$k}"; } $keystring = "{0.0}{Subkey}" . $keystring; if (!gethash($query, parentkey($keystring))) { setvalue($query, parentkey($keystring), {}); } setaggregator( $query, $keystring, $ea ); } else { $depth--; $num = $depths{$depth} + 1; } $want = KEYWORD | PAREN | AGGREG; } elsif ( $current & AGGREG ) { $ea = $val; $depths{$depth}++ if ($last & PAREN); $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; if ( exists $lcfields{ lc $key } ) { $key = $lcfields{ lc $key }; $class = $FIELDS{$key}->[0]; } if ( $class ne 'INT' ) { $val = "'$val'"; } push @actions, [ "Unknown field: $key", -1 ] unless $class; $want = PAREN | AGGREG; } else { push @actions, [ "I'm lost", -1 ]; } if ( $current & VALUE ) { $depths{$depth}++; my $keystring = ""; $keystring = "{Subkey}{" . $parentdepth . ".$num" . "}"; $parentdepth--; while ( $parentdepth > 0 ) { $keystring = "{Subkey}{" . $parentdepth . ".$depths{$parentdepth}" . "}" . $keystring; $parentdepth--; } $ea = 'AND' if !$ea; $keystring = "{0.0}" . $keystring; if ( $key =~ /^CF./ ) { $key = "'" . $key . "'"; } my $clause = { Key => $key, Op => $op, Value => $val }; setvalue($query, $keystring, $clause); setaggregator( $query, $keystring, $ea ); $num++; ( $ea, $key, $op, $value ) = ( "", "", "", "" ); } $last = $current; } # while push @actions, [ "Incomplete query", -1 ] unless ( ( $want | PAREN ) || ( $want | KEYWORD ) ); push @actions, [ "Incomplete Query", -1 ] unless ( $last && ( $last | PAREN ) || ( $last || VALUE ) ); # This will never happen, because the parser will complain push @actions, [ "Mismatched parentheses", -1 ] unless $depth == 1; return $query; } sub _match { # 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; } sub keyexists { my $hash = shift; my $key = shift; my ($prefix, $depth, $num) = parsekey($key); return exists $hash->{0.0} if ($depth == 0); my $myhash = gethash($hash, $prefix); return exists ($myhash->{$depth + ($num/10)}); } sub subkeyexists { my $hash = shift; my $key = shift; my $myhash = gethash($hash, $key); return exists $myhash->{Subkey}; } sub appendsubkey { my $hash = shift; my $key = shift; my $newkey; # if the next key exists, get the last key my $nextkey = nextkey($key); if ( subkeyexists( $hash, $nextkey ) ) { $newkey = appendkey( $hash, subkey( $nextkey ) ); } else { $newkey = subkey($key); } return $newkey; } sub prevkey { my $key = shift; my ( $prefix, $depth, $num ) = parsekey($key); my $k = "$depth." . ( $num - 1 ); return $prefix . "{$k}"; } sub nextkey { my $key = shift; my ( $prefix, $depth, $num ) = parsekey($key); my $k = "$depth." . ( $num + 1 ); return $prefix . "{$k}"; } sub subkey { my $key = shift; my ( $prefix, $depth, $num ) = parsekey($key); return $prefix . "{" . "$depth.$num" . "}{Subkey}{" . ( $depth + 1 ) . ".0}"; } sub parentkey { my $key = shift; my ( $prefix, $depth, $num ) = parsekey($key); if ( $depth > 0 ) { $prefix =~ s/(.*){Subkey}/$1/; } else { $prefix = ""; } return $prefix; } sub parsekey { my $key = shift; # pull apart the key $key =~ m/(.*){(.*)}$/; my $prefix = $1; my $depth = 0; my $num = 0; my $final = $2; if ($final =~ m/(\d).(\d)/) { $depth = $1; $num = $2; } else { $depth = $final; } return ( $prefix, $depth, $num ); } sub reworkkeys { my $hash = shift; my $key = shift; my $currentkey = shift; # if our parent doesn't exist, return immediately if ( !keyexists( $hash, parentkey($key) ) ) { return; } fixsubkeys( $hash, $key ); # don't try to rework keys if the first one exists if ( keyexists( $hash, $key ) || subkeyexists( $hash, $key ) ) { return; } my $nextkey = nextkey($key); while ( keyexists( $hash, $nextkey ) ) { if ( keyexists( $hash, $nextkey ) ) { my $temp = gethash($hash, $nextkey); setvalue($hash, $key, $temp); $$currentkey = $key . $2 if $$currentkey =~ m/($nextkey)(.*)/; } # set this so that we can return it; $key = $nextkey; if ( subkeyexists( $hash, $key ) ) { my $subkey = subkey($key); reworkkeys( $hash, subkey($key), $currentkey ); } $nextkey = nextkey($key); } deletevalue($hash, $key); } sub fixsubkeys { my $hash = shift; my $key = shift; if ( subkeyexists( $hash, $key ) ) { my ( $prefix, $depth, $num ) = parsekey($key); my $item = gethash($hash, $key . "{Subkey}"); my %temp = %$item; foreach my $i ( keys %temp ) { my $num = $i - int($i); my $new = int($depth) + 1 + $num; if ( $i != $new ) { setvalue($hash, $key . "{Subkey}{$new}", $temp{$i}); deletevalue($hash, $key . "{Subkey}{$i}"); fixsubkeys( $hash, $key . "{Subkey}{$new}" ); } $num++; } } } sub getaggregator { my $hash = shift; my $key = shift; my $parent = parentkey($key); my $ea = gethash($hash, $parent . "{EA}"); return $ea; } sub setaggregator { my $hash = shift; my $key = shift; my $ea = shift; my $parentkey = parentkey($key); my $parenthash = gethash($hash, parentkey($key)); $parenthash->{EA} = $ea; } sub appendkey { my $hash = shift; my $key = shift; my $newkey; if ($key) { $newkey = $key; } else { $newkey = "{" . "0.0" . "}{Subkey}{1.0}"; } while ( keyexists( $hash, $newkey ) ) { $newkey = nextkey($newkey); } return $newkey; } sub movecurrent { my $hash = shift; my $newkey = shift; my $currentkey = shift; my $origkey = $$currentkey; my $old = gethash($hash, $origkey); my %temp = %$old; deletevalue($hash, $origkey); my $orignewkey = $newkey; setvalue($hash, $newkey, \%temp); $$currentkey = $orignewkey; reworkkeys( $hash, $orignewkey, $currentkey ); reworkkeys( $hash, $origkey, $currentkey ); return 1; } sub swap { my $hash = shift; my $key1 = shift; my $key2 = shift; return 0 if !( keyexists( $hash, $key1 ) && keyexists( $hash, $key2 ) ); # store the value temporarily my $val1 = gethash($hash, $key1); my %temp = %$val1; my $val2 = gethash($hash, $key2); setvalue($hash, $key2, \%temp); setvalue($hash, $key1, $val2); return 1; } sub gethash { my $hash = shift; my $key = shift; my $returnhash; $key =~ s/^{0.0}(.*)$/$1/; $key =~ s/^{(.*)}$/$1/; my @keys = split(/\}\{/, $key); $returnhash = $hash->{0.0}; foreach my $k (@keys) { if (exists $returnhash->{$k}) { $returnhash = $returnhash->{$k}; } else { $returnhash = $returnhash->{$k+0}; } } return $returnhash; } sub setvalue { my $hash = shift; my $key = shift; my $value = shift; my ($prefix, $depth, $num) = parsekey($key); my $newhash; if ($prefix) { my $parentkey = parentkey($key); my $parent; if (!gethash($hash, $parentkey)) { setvalue($hash, $parentkey, {}); $parent = gethash($hash, $parentkey); } else { $parent = gethash($hash, $parentkey); } if (!exists $parent->{Subkey}) { $parent->{Subkey} = {}; } $newhash = gethash($hash, $prefix); } else { $newhash = $hash; } $newhash->{$depth + ($num / 10)} = $value; # if there isn't an aggregator, set it to AND my $ea = getaggregator( $hash, $key); if (!$ea) { setaggregator( $hash, $key, 'AND' ); } } sub deletevalue { my $hash = shift; my $key = shift; my ($prefix, $depth, $num) = parsekey($key); my $parent = gethash($hash, $prefix); delete $parent->{$depth + ($num / 10)}; } sub debug { my $message = shift; $m->print($message . "
"); } # }}} # }}} # {{{ Deal with format changes my ($AvailableColumns, $CurrentFormat); ($Format, $AvailableColumns, $CurrentFormat) = $m->comp('Elements/BuildFormatString', cfqueues => \%queues, %ARGS); $ARGS{'Format'} = $Format; $ARGS{'Query'} = $Query; # }}} # {{{ if we're asked to save the current search, save it if ( $ARGS{'Save'} ) { if ($search && $search->id) { # if the search data or metadata has changed, change it $search->SetSubValues( Format => $ARGS{'Format'}, Query => $ARGS{'Query'} ); $search->SetDescription( $ARGS{'Description'} ); # if the associated object has changed, change which object this search # applies to $search_hash->{'SearchId'} = $ARGS{'SearchId'}; $search_hash->{'Object'} = $search; $search_hash->{'Format'} = $ARGS{'Format'}; $search_hash->{'Query'} = $ARGS{'Query'}; $search_hash->{'Description'} = $ARGS{'Description'}; } elsif ( $ARGS{'SearchId'} eq 'new' && $ARGS{'Owner'} =~ /^(.*?)-(\d+)$/ ) { my $obj_type = $1; my $obj_id = $2; my $container_object; if ( $obj_type eq 'RT::User' && $obj_id == $session{'CurrentUser'}->Id) { $container_object = $session{'CurrentUser'}->UserObj; } elsif ($obj_type eq 'RT::Group') { $container_object = RT::Group->new($session{'CurrentUser'}); $container_object->Load($obj_id); } if ($container_object->id ) { my ( $search_id, $search_msg ) = $container_object->AddAttribute( Name => 'SavedSearch', Description => $ARGS{'Description'}, Content => { Format => $ARGS{'Format'}, Query => $Query } ); $search = $session{'CurrentUser'}->UserObj->Attributes->WithId($search_id); $search_hash->{'SearchId'} = ref( $session{'CurrentUser'}->UserObj ) . '-' . $session{'CurrentUser'}->UserObj->Id . '-SavedSearch-' . $search->Id; } unless ($search->id) { push @actions, [loc("Can't find a saved search to work with"), 0]; } $search_hash->{Description} = ( $search->Description() || loc('Untitled search') ); $search_hash->{Format} = $search->SubValue('Format'); $search_hash->{Query} = $search->SubValue('Query'); $search_hash->{Object} = $search; } else { push @actions, [loc("Can't save this search"), 0]; } } # }}} # {{{ If we're asked to delete the current search, make it go away and reset # the search parameters if ( $ARGS{'Delete'} ) { if ( $ARGS{'SearchId'} =~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) { my $obj_type = $1; my $obj_id = $2; my $search_id = $3; my $container_object; if ( $obj_type eq 'RT::User' && $obj_id == $session{'CurrentUser'}->Id) { $container_object = $session{'CurrentUser'}->UserObj; } elsif ($obj_type eq 'RT::Group') { $container_object = RT::Group->new($session{'CurrentUser'}); $container_object->Load($obj_id); } if ($container_object->id ) { $container_object->Attributes->DeleteEntry( Name => 'SavedSearch', id => $search_id); $Format = ''; $Query = ''; } delete $ARGS{'SearchId'}; } } # }}} # Build a querystring for the tabs my $QueryString; if ($NewQuery) { $QueryString = '?NewQuery=1'; } else { $QueryString = '?' . $m->comp('/Elements/QueryString', Query => $Query, Format => $Format, Rows =>$ARGS{'Rows'}) if ($Query); } if (!$search) { $search_hash->{'SearchId'} = $SearchId; $search_hash->{'Format'} = $Format; $search_hash->{'Query'} = $Query; $search_hash->{'Description'} = $Description || $ARGS{'Descripton'}; } $session{'CurrentSearchHash'} = $search_hash; $ARGS{'SearchId'} =$search_hash->{'SearchId'}; <%ARGS> $NewQuery => 0 $SearchId => 'new' $Query => undef $Format => undef $Description => undef $HideResults => 0