[Rt-commit] rtfm branch, search-results-view-refactor, updated. 9e718c882468a5a72682201d74ee71e34997773b

? sunnavy sunnavy at bestpractical.com
Mon Jul 19 22:16:51 EDT 2010


The branch, search-results-view-refactor has been updated
       via  9e718c882468a5a72682201d74ee71e34997773b (commit)
       via  83f6b548fee1f5de1b256a6cf194a3d1baf2802f (commit)
       via  6cfb5721bfcbef931c4e87a8e708a9605c59bbba (commit)
       via  ca97544b376e889b8873b399b18c6c606199a144 (commit)
      from  71c2c88bc201061568e5d89758b3807f8f463e1f (commit)

Summary of changes:
 .../Default => Elements/ShowSearch/ModifySearch}   |   17 +-
 .../ColumnMap                                      |   93 ++++--
 html/RTFM/Article/Elements/ShowSearchResults       |  130 --------
 html/RTFM/Article/Search.html                      |  291 ++----------------
 .../Elements/Tabs/Default => Search/Article.html}  |    7 +-
 html/Search/Elements/Article                       |   63 ++++-
 lib/RT/FM/ArticleCollection.pm                     |  340 ++++++++++++++++++++
 7 files changed, 496 insertions(+), 445 deletions(-)
 copy html/Callbacks/RTFM/{Admin/Global/CustomFields/index.html/Default => Elements/ShowSearch/ModifySearch} (86%)
 copy html/Elements/{RT__FM__ClassCollection => RT__FM__ArticleCollection}/ColumnMap (57%)
 delete mode 100644 html/RTFM/Article/Elements/ShowSearchResults
 copy html/{Callbacks/RTFM/Elements/Tabs/Default => Search/Article.html} (93%)

- Log -----------------------------------------------------------------
commit ca97544b376e889b8873b399b18c6c606199a144
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Jul 20 09:22:32 2010 +0800

    abstract search stuff to ArticleCollection::Search

diff --git a/html/RTFM/Article/Search.html b/html/RTFM/Article/Search.html
index 5b93b0d..8e4ed1a 100644
--- a/html/RTFM/Article/Search.html
+++ b/html/RTFM/Article/Search.html
@@ -106,21 +106,7 @@ my $articles = RT::FM::ArticleCollection->new( $session{'CurrentUser'} );
 # on name and summary.
 if ($ARGS{'q'} && $ARGS{'q'} =~ /^(\d+)$/) {
     return $m->comp("/RTFM/Article/Display.html", id => $1);
-} elsif ($ARGS{'q'}) {
-    $articles->Limit( FIELD => 'Name',
-                      SUBCLAUSE => 'NameOrSummary',
-                      OPERATOR => 'LIKE',
-                      ENTRYAGGREGATOR => 'OR',
-                      CASESENSITIVE => 0,
-                      VALUE => $ARGS{'q'} );
-    $articles->Limit( FIELD => 'Summary',
-                      SUBCLAUSE => 'NameOrSummary',
-                      OPERATOR => 'LIKE',
-                      ENTRYAGGREGATOR => 'OR',
-                      CASESENSITIVE => 0,
-                      VALUE => $ARGS{'q'} );
 }
-
 # }}}
 
 # {{{ Saved search logic
@@ -235,264 +221,12 @@ if ($ARGS{'Save'}) {
 
 # }}}
 
-
-# Don't want to search for a null class when there is no class specced
+    # Don't want to search for a null class when there is no class specced
 my $customfields = RT::CustomFields->new( $session{'CurrentUser'} );
-$customfields->LimitToLookupType(RT::FM::Article->new($session{'CurrentUser'})->CustomFieldLookupType);
-if ( $ARGS{'Class'} ) {
-    my @Classes =
-      ( ref $ARGS{'Class'} eq 'ARRAY' )
-      ? @{ $ARGS{'Class'} }
-      : ( $ARGS{'Class'} );
-    foreach my $class (@Classes) {
-        $customfields->LimitToGlobalOrObjectId($class);
-    }
-}
-else {
-    $customfields->LimitToGlobalOrObjectId();
-}
-
 my %dates;
-foreach my $date qw(Created< Created> LastUpdated< LastUpdated>) {
-    next unless ( $ARGS{$date} );
-    my $seconds = parsedate( $ARGS{$date}, FUZZY => 1, PREFER_PAST => 1 );
-    my $date_obj = RT::Date->new( $session{'CurrentUser'} );
-    $date_obj->Set( Format => 'unix', Value => $seconds );
-    $dates{$date} = $date_obj;
-
-    if ( $date =~ /^(.*?)<$/i ) {
-        $articles->Limit( FIELD           => $1, OPERATOR        => "<=", ENTRYAGGREGATOR => "AND", VALUE           => $date_obj->ISO );
-    }
-
-    if ( $date =~ /^(.*?)>$/i ) {
-        $articles->Limit( FIELD           => $1, OPERATOR        => ">=", ENTRYAGGREGATOR => "AND", VALUE           => $date_obj->ISO );
-    }
-
-}
-
-if ($RefersTo) {
-    foreach my $link ( split ( /\s+/, $RefersTo ) ) {
-        next unless ($link);
-        $articles->LimitRefersTo($link);
-    }
-}
-
-if ($ReferredToBy) {
-    foreach my $link ( split ( /\s+/, $ReferredToBy ) ) {
-        next unless ($link);
-        $articles->LimitReferredToBy($link);
-    }
-}
-
-if ($ARGS{'Topics'}) {
-    my @Topics =
-      ( ref $ARGS{'Topics'} eq 'ARRAY' )
-      ? @{ $ARGS{'Topics'} }
-      : ( $ARGS{'Topics'} );
-    @Topics = map {split} @Topics;
-    if ($ARGS{'ExpandTopics'}) {
-        my %topics;
-        while (@Topics) {
-            my $id = shift @Topics;
-            next if $topics{$id};
-            my $Topics = RT::FM::TopicCollection->new($session{'CurrentUser'});
-            $Topics->Limit(FIELD => 'Parent', VALUE => $id);
-            push @Topics, $_->Id while $_ = $Topics->Next;
-            $topics{$id}++;
-        }
-        @Topics = keys %topics;
-        $ARGS{'Topics'} = \@Topics;
-    }
-    $articles->LimitTopics(@Topics);
-}
-
-my %cfs;
-while ( my $cf = $customfields->Next ) {
-    $cfs{ $cf->Name } = $cf->Id;
-}
-# reset the iterator because we use this to build the UI
-$customfields->GotoFirstItem;
-
-foreach my $field ( keys %cfs ) {
-   
-   my @MatchLike = (ref $ARGS{ $field."~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."~" } } : ( $ARGS{$field."~" } );
-   my @NoMatchLike = (ref $ARGS{ $field."!~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!~" } } : ( $ARGS{$field."!~" } );
-
-   my @Match = (ref $ARGS{ $field } eq 'ARRAY' ) ? @{ $ARGS{ $field } } : ( $ARGS{$field } );
-   my @NoMatch = (ref $ARGS{ $field."!" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!" } } : ( $ARGS{$field."!" } );
-
-    foreach my $val (@MatchLike) {
-        next unless $val;
-        push @Match, "~".$val;
-    }
-
-    foreach my $val (@NoMatchLike) {
-        next unless $val;
-        push @NoMatch, "~".$val;
-    }
-
-
-    foreach my $value (@Match) {
-        next unless $value;
-        my $op;
-        if ( $value =~ /^~(.*)$/ ) {
-            $value = "%$1%";
-            $op    = 'LIKE';
-        }
-        else {
-            $op = '=';
-        }
-        $articles->LimitCustomField( FIELD           => $cfs{$field},
-                                     VALUE           => $value,
-                                     CASESENSITIVE   => 0,
-                                     ENTRYAGGREGATOR => 'OR',
-                                     OPERATOR        => $op );
-    }
-    foreach my $value (@NoMatch) {
-        next unless $value;
-        my $op;
-        if ( $value =~ /^~(.*)$/ ) {
-            $value = "%$1%";
-            $op    = 'NOT LIKE';
-        }
-        else {
-            $op = '!=';
-        }
-        $articles->LimitCustomField( FIELD           => $cfs{$field},
-                                     VALUE           => $value,
-                                     CASESENSITIVE   => 0,
-                                     ENTRYAGGREGATOR => 'OR',
-                                     OPERATOR        => $op );
-    }
-}
-
-### Searches for any field
-
-if ($ARGS{'Article~'}) {
-    $articles->LimitCustomField( VALUE => $ARGS{'Article~'},
-                                 ENTRYAGGREGATOR => 'OR',
-                                 OPERATOR => 'LIKE',
-                                 CASESENSITIVE => 0,
-                                 SUBCLAUSE => 'SearchAll' );
-    $articles->Limit( SUBCLAUSE => 'SearchAll',
-                      FIELD => "Name",
-                      VALUE => $ARGS{'Article~'},
-                      ENTRYAGGREGATOR => 'OR',
-                      CASESENSITIVE => 0,
-                      OPERATOR => 'LIKE' );
-    $articles->Limit( SUBCLAUSE => 'SearchAll',
-                      FIELD => "Summary",
-                      VALUE => $ARGS{'Article~'},
-                      ENTRYAGGREGATOR => 'OR',
-                      CASESENSITIVE => 0,
-                      OPERATOR => 'LIKE' );
-}
-
-if ($ARGS{'Article!~'}) {
-    $articles->LimitCustomField( VALUE => $ARGS{'Article!~'},
-                                 OPERATOR => 'NOT LIKE',
-                                 CASESENSITIVE => 0,
-                                 SUBCLAUSE => 'SearchAll' );
-    $articles->Limit( SUBCLAUSE => 'SearchAll',
-                      FIELD => "Name",
-                      VALUE => $ARGS{'Article!~'},
-                      ENTRYAGGREGATOR => 'AND',
-                      CASESENSITIVE => 0,
-                      OPERATOR => 'NOT LIKE' );
-    $articles->Limit( SUBCLAUSE => 'SearchAll',
-                      FIELD => "Summary",
-                      VALUE => $ARGS{'Article!~'},
-                      ENTRYAGGREGATOR => 'AND',
-                      CASESENSITIVE => 0,
-                      OPERATOR => 'NOT LIKE' );
-}
-
-foreach my $field qw(Name Summary Class) {
-
-   my @MatchLike = (ref $ARGS{ $field."~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."~" } } : ( $ARGS{$field."~" } );
-   my @NoMatchLike = (ref $ARGS{ $field."!~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!~" } } : ( $ARGS{$field."!~" } );
-
-   my @Match = (ref $ARGS{ $field } eq 'ARRAY' ) ? @{ $ARGS{ $field } } : ( $ARGS{$field } );
-   my @NoMatch = (ref $ARGS{ $field."!" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!" } } : ( $ARGS{$field."!" } );
-
-    foreach my $val (@MatchLike) {
-        next unless $val;
-        push @Match, "~".$val;
-    }
-
-    foreach my $val (@NoMatchLike) {
-        next unless $val;
-        push @NoMatch, "~".$val;
-    }
-
-    my $op;
-    foreach my $value (@Match) {
-        if ( $value && $value =~ /^~(.*)$/ ) {
-            $value = "%$1%";
-            $op    = 'LIKE';
-        }
-        else {
-            $op = '=';
-        }
-
-        # preprocess Classes, so we can search on class
-        if ( $field eq 'Class' && $value ) {
-            my $class = RT::FM::Class->new($RT::SystemUser);
-            $class->Load($value);
-            $value = $class->Id;
-        }
-
-        # now that we've pruned the value, get out if it's different.
-        next unless $value;
-
-        $articles->Limit( SUBCLAUSE       => $field . 'Match',
-                          FIELD           => $field,
-                          OPERATOR        => $op,
-                          CASESENSITIVE   => 0,
-                          VALUE           => $value,
-                          ENTRYAGGREGATOR => 'OR' );
-
-    }
-    foreach my $value (@NoMatch) {
-
-        # preprocess Classes, so we can search on class
-        if ( $value && $value =~ /^~(.*)/ ) {
-            $value = "%$1%";
-            $op    = 'NOT LIKE';
-        }
-        else {
-            $op = '!=';
-        }
-        if ( $field eq 'Class' ) {
-            my $class = RT::FM::Class->new($RT::SystemUser);
-            $class->Load($value);
-            $value = $class->Id;
-        }
-
-        # now that we've pruned the value, get out if it's different.
-        next unless $value;
-
-        $articles->Limit( SUBCLAUSE       => $field . 'NoMatch',
-                          OPERATOR        => $op,
-                          VALUE           => $value,
-                          CASESENSITIVE   => 0,
-                          FIELD           => $field,
-                          ENTRYAGGREGATOR => 'AND' );
-
-    }
-}
-
-if ( @OrderBy ) {
-    if ( $OrderBy[0] && $OrderBy[0] =~ /\|/ ) {
-        @OrderBy = split '|', @OrderBy;
-        @Order   = split '|', @Order;
-    }
-    my @tmp = map
-        {{ FIELD => $OrderBy[$_], ORDER => $Order[$_] }}
-        0..$#OrderBy;
-    $articles->OrderByCols( @tmp );
-}
 
+$articles->Search( %ARGS, CustomFields => $customfields, Dates => \%dates,
+        OrderBy => \@OrderBy );
 
 $m->comp('/Elements/Callback', %ARGS, _Search => $articles);
 
diff --git a/lib/RT/FM/ArticleCollection.pm b/lib/RT/FM/ArticleCollection.pm
index 0b41eef..14ca5bc 100644
--- a/lib/RT/FM/ArticleCollection.pm
+++ b/lib/RT/FM/ArticleCollection.pm
@@ -103,6 +103,346 @@ sub NewItem {
     return(RT::FM::Article->new($self->CurrentUser));
 }
 
+sub Search {
+    my $self     = shift;
+    my %args     = @_;
+    my $customfields = $args{CustomFields}
+      || RT::CustomFields->new( $self->CurrentUser );
+    my $dates = $args{Dates} || {};
+    my $order_by = $args{OrderBy};
+    if ( $args{'q'} ) {
+        $self->Limit(
+            FIELD           => 'Name',
+            SUBCLAUSE       => 'NameOrSummary',
+            OPERATOR        => 'LIKE',
+            ENTRYAGGREGATOR => 'OR',
+            CASESENSITIVE   => 0,
+            VALUE           => $args{'q'}
+        );
+        $self->Limit(
+            FIELD           => 'Summary',
+            SUBCLAUSE       => 'NameOrSummary',
+            OPERATOR        => 'LIKE',
+            ENTRYAGGREGATOR => 'OR',
+            CASESENSITIVE   => 0,
+            VALUE           => $args{'q'}
+        );
+    }
+
+
+    require Time::ParseDate;
+    foreach my $date qw(Created< Created> LastUpdated< LastUpdated>) {
+        next unless ( $args{$date} );
+        my $seconds = Time::ParseDate::parsedate( $args{$date}, FUZZY => 1, PREFER_PAST => 1 );
+        my $date_obj = RT::Date->new( $self->CurrentUser );
+        $date_obj->Set( Format => 'unix', Value => $seconds );
+        $dates->{$date} = $date_obj;
+
+        if ( $date =~ /^(.*?)<$/i ) {
+            $self->Limit(
+                FIELD           => $1,
+                OPERATOR        => "<=",
+                ENTRYAGGREGATOR => "AND",
+                VALUE           => $date_obj->ISO
+            );
+        }
+
+        if ( $date =~ /^(.*?)>$/i ) {
+            $self->Limit(
+                FIELD           => $1,
+                OPERATOR        => ">=",
+                ENTRYAGGREGATOR => "AND",
+                VALUE           => $date_obj->ISO
+            );
+        }
+
+    }
+
+    if ($RefersTo) {
+        foreach my $link ( split( /\s+/, $RefersTo ) ) {
+            next unless ($link);
+            $self->LimitRefersTo($link);
+        }
+    }
+
+    if ($ReferredToBy) {
+        foreach my $link ( split( /\s+/, $ReferredToBy ) ) {
+            next unless ($link);
+            $self->LimitReferredToBy($link);
+        }
+    }
+
+    if ( $args{'Topics'} ) {
+        my @Topics =
+          ( ref $args{'Topics'} eq 'ARRAY' )
+          ? @{ $args{'Topics'} }
+          : ( $args{'Topics'} );
+        @Topics = map { split } @Topics;
+        if ( $args{'ExpandTopics'} ) {
+            my %topics;
+            while (@Topics) {
+                my $id = shift @Topics;
+                next if $topics{$id};
+                my $Topics =
+                  RT::FM::TopicCollection->new( $self->CurrentUser );
+                $Topics->Limit( FIELD => 'Parent', VALUE => $id );
+                push @Topics, $_->Id while $_ = $Topics->Next;
+                $topics{$id}++;
+            }
+            @Topics = keys %topics;
+            $args{'Topics'} = \@Topics;
+        }
+        $self->LimitTopics(@Topics);
+    }
+
+    my %cfs;
+    $customfields->LimitToLookupType(
+        RT::FM::Article->new( $self->CurrentUser )
+          ->CustomFieldLookupType );
+    if ( $ARGS{'Class'} ) {
+        my @Classes =
+          ( ref $ARGS{'Class'} eq 'ARRAY' )
+          ? @{ $ARGS{'Class'} }
+          : ( $ARGS{'Class'} );
+        foreach my $class (@Classes) {
+            $customfields->LimitToGlobalOrObjectId($class);
+        }
+    }
+    else {
+        $customfields->LimitToGlobalOrObjectId();
+    }
+    while ( my $cf = $customfields->Next ) {
+        $cfs{ $cf->Name } = $cf->Id;
+    }
+
+    # reset the iterator because we use this to build the UI
+    $customfields->GotoFirstItem;
+
+    foreach my $field ( keys %cfs ) {
+
+        my @MatchLike =
+          ( ref $args{ $field . "~" } eq 'ARRAY' )
+          ? @{ $args{ $field . "~" } }
+          : ( $args{ $field . "~" } );
+        my @NoMatchLike =
+          ( ref $args{ $field . "!~" } eq 'ARRAY' )
+          ? @{ $args{ $field . "!~" } }
+          : ( $args{ $field . "!~" } );
+
+        my @Match =
+          ( ref $args{$field} eq 'ARRAY' )
+          ? @{ $args{$field} }
+          : ( $args{$field} );
+        my @NoMatch =
+          ( ref $args{ $field . "!" } eq 'ARRAY' )
+          ? @{ $args{ $field . "!" } }
+          : ( $args{ $field . "!" } );
+
+        foreach my $val (@MatchLike) {
+            next unless $val;
+            push @Match, "~" . $val;
+        }
+
+        foreach my $val (@NoMatchLike) {
+            next unless $val;
+            push @NoMatch, "~" . $val;
+        }
+
+        foreach my $value (@Match) {
+            next unless $value;
+            my $op;
+            if ( $value =~ /^~(.*)$/ ) {
+                $value = "%$1%";
+                $op    = 'LIKE';
+            }
+            else {
+                $op = '=';
+            }
+            $self->LimitCustomField(
+                FIELD           => $cfs{$field},
+                VALUE           => $value,
+                CASESENSITIVE   => 0,
+                ENTRYAGGREGATOR => 'OR',
+                OPERATOR        => $op
+            );
+        }
+        foreach my $value (@NoMatch) {
+            next unless $value;
+            my $op;
+            if ( $value =~ /^~(.*)$/ ) {
+                $value = "%$1%";
+                $op    = 'NOT LIKE';
+            }
+            else {
+                $op = '!=';
+            }
+            $self->LimitCustomField(
+                FIELD           => $cfs{$field},
+                VALUE           => $value,
+                CASESENSITIVE   => 0,
+                ENTRYAGGREGATOR => 'OR',
+                OPERATOR        => $op
+            );
+        }
+    }
+
+### Searches for any field
+
+    if ( $args{'Article~'} ) {
+        $self->LimitCustomField(
+            VALUE           => $args{'Article~'},
+            ENTRYAGGREGATOR => 'OR',
+            OPERATOR        => 'LIKE',
+            CASESENSITIVE   => 0,
+            SUBCLAUSE       => 'SearchAll'
+        );
+        $self->Limit(
+            SUBCLAUSE       => 'SearchAll',
+            FIELD           => "Name",
+            VALUE           => $args{'Article~'},
+            ENTRYAGGREGATOR => 'OR',
+            CASESENSITIVE   => 0,
+            OPERATOR        => 'LIKE'
+        );
+        $self->Limit(
+            SUBCLAUSE       => 'SearchAll',
+            FIELD           => "Summary",
+            VALUE           => $args{'Article~'},
+            ENTRYAGGREGATOR => 'OR',
+            CASESENSITIVE   => 0,
+            OPERATOR        => 'LIKE'
+        );
+    }
+
+    if ( $args{'Article!~'} ) {
+        $self->LimitCustomField(
+            VALUE         => $args{'Article!~'},
+            OPERATOR      => 'NOT LIKE',
+            CASESENSITIVE => 0,
+            SUBCLAUSE     => 'SearchAll'
+        );
+        $self->Limit(
+            SUBCLAUSE       => 'SearchAll',
+            FIELD           => "Name",
+            VALUE           => $args{'Article!~'},
+            ENTRYAGGREGATOR => 'AND',
+            CASESENSITIVE   => 0,
+            OPERATOR        => 'NOT LIKE'
+        );
+        $self->Limit(
+            SUBCLAUSE       => 'SearchAll',
+            FIELD           => "Summary",
+            VALUE           => $args{'Article!~'},
+            ENTRYAGGREGATOR => 'AND',
+            CASESENSITIVE   => 0,
+            OPERATOR        => 'NOT LIKE'
+        );
+    }
+
+    foreach my $field qw(Name Summary Class) {
+
+        my @MatchLike =
+          ( ref $args{ $field . "~" } eq 'ARRAY' )
+          ? @{ $args{ $field . "~" } }
+          : ( $args{ $field . "~" } );
+        my @NoMatchLike =
+          ( ref $args{ $field . "!~" } eq 'ARRAY' )
+          ? @{ $args{ $field . "!~" } }
+          : ( $args{ $field . "!~" } );
+
+        my @Match =
+          ( ref $args{$field} eq 'ARRAY' )
+          ? @{ $args{$field} }
+          : ( $args{$field} );
+        my @NoMatch =
+          ( ref $args{ $field . "!" } eq 'ARRAY' )
+          ? @{ $args{ $field . "!" } }
+          : ( $args{ $field . "!" } );
+
+        foreach my $val (@MatchLike) {
+            next unless $val;
+            push @Match, "~" . $val;
+        }
+
+        foreach my $val (@NoMatchLike) {
+            next unless $val;
+            push @NoMatch, "~" . $val;
+        }
+
+        my $op;
+        foreach my $value (@Match) {
+            if ( $value && $value =~ /^~(.*)$/ ) {
+                $value = "%$1%";
+                $op    = 'LIKE';
+            }
+            else {
+                $op = '=';
+            }
+
+            # preprocess Classes, so we can search on class
+            if ( $field eq 'Class' && $value ) {
+                my $class = RT::FM::Class->new($RT::SystemUser);
+                $class->Load($value);
+                $value = $class->Id;
+            }
+
+            # now that we've pruned the value, get out if it's different.
+            next unless $value;
+
+            $self->Limit(
+                SUBCLAUSE       => $field . 'Match',
+                FIELD           => $field,
+                OPERATOR        => $op,
+                CASESENSITIVE   => 0,
+                VALUE           => $value,
+                ENTRYAGGREGATOR => 'OR'
+            );
+
+        }
+        foreach my $value (@NoMatch) {
+
+            # preprocess Classes, so we can search on class
+            if ( $value && $value =~ /^~(.*)/ ) {
+                $value = "%$1%";
+                $op    = 'NOT LIKE';
+            }
+            else {
+                $op = '!=';
+            }
+            if ( $field eq 'Class' ) {
+                my $class = RT::FM::Class->new($RT::SystemUser);
+                $class->Load($value);
+                $value = $class->Id;
+            }
+
+            # now that we've pruned the value, get out if it's different.
+            next unless $value;
+
+            $self->Limit(
+                SUBCLAUSE       => $field . 'NoMatch',
+                OPERATOR        => $op,
+                VALUE           => $value,
+                CASESENSITIVE   => 0,
+                FIELD           => $field,
+                ENTRYAGGREGATOR => 'AND'
+            );
+
+        }
+    }
+
+    if ($order_by && @$order_by) {
+        if ( $order_by[0] && $order_by[0] =~ /\|/ ) {
+            @OrderBy = split '|', @OrderBy;
+            @Order   = split '|', @Order;
+        }
+        my @tmp =
+          map { { FIELD => $order_by[$_], ORDER => $Order[$_] } } 0 .. $#OrderBy;
+        $self->OrderByCols(@tmp);
+    }
+
+    return 1;
+}
+
         eval "require RT::FM::ArticleCollection_Overlay";
         if ($@ && $@ !~ /^Can't locate/) {
             die $@;

commit 6cfb5721bfcbef931c4e87a8e708a9605c59bbba
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Jul 20 09:23:19 2010 +0800

    ColumnMap for Article

diff --git a/html/Elements/RT__FM__ArticleCollection/ColumnMap b/html/Elements/RT__FM__ArticleCollection/ColumnMap
new file mode 100644
index 0000000..03b2e03
--- /dev/null
+++ b/html/Elements/RT__FM__ArticleCollection/ColumnMap
@@ -0,0 +1,107 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%# 
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+%#                                          <jesse at 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>
+$Name => undef
+$Attr => undef
+</%ARGS>
+
+
+<%ONCE>
+my $COLUMN_MAP;
+
+$COLUMN_MAP = {
+    Name => {
+        attribute => 'Name',
+        title     => 'Name',                   # loc
+        value     => sub { $_[0]->Name },
+    },
+    Summary => {
+        attribute => 'Summary',
+        title     => 'Summary',                   # loc
+        value     => sub { $_[0]->Summary },
+    },
+    Class => {
+        attribute => 'Class',
+        title     => 'Class id',                   # loc
+        value     => sub { $_[0]->Class },
+    },
+    ClassName => {
+        attribute => 'Class',
+        title     => 'Class',                               # loc
+        value     => sub { $_[0]->ClassObj->Name },
+    },
+    CreatedRelative => {
+        attribute => 'Created',
+        title => 'Created', # loc
+        value => sub { $_[0]->CreatedObj->AgeAsString },
+    },
+    LastUpdatedRelative => {
+        attribute => 'LastUpdated',
+        title => 'LastUpdated', # loc
+        value => sub { $_[0]->LastUpdatedObj->AgeAsString },
+    },
+    Topics => {
+        title => 'Topics',                                  # loc
+        value => sub {
+            my $topics = '';
+            my $Topics = $_[0]->Topics;
+            while ( my $t = $Topics->Next ) {
+                $topics .=
+                  $m->scomp( '/RTFM/Elements/ShowTopic', topic => $t->TopicObj )
+                  . '<br />';
+            }
+            return \$topics;
+        },
+    }
+};
+
+</%ONCE>
+<%init>
+$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
+return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
+</%init>

commit 83f6b548fee1f5de1b256a6cf194a3d1baf2802f
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Jul 20 09:25:10 2010 +0800

    use CollectionList, no more ShowSearchResults

diff --git a/html/RTFM/Article/Elements/ShowSearchResults b/html/RTFM/Article/Elements/ShowSearchResults
deleted file mode 100644
index b9a0a58..0000000
--- a/html/RTFM/Article/Elements/ShowSearchResults
+++ /dev/null
@@ -1,130 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%# 
-%# COPYRIGHT:
-%#  
-%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC 
-%#                                          <sales at 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/copyleft/gpl.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 }}}
-% # THIS IS A HORRIBLE HACK TO SEE IF WE TRIED TO SEARCH
-% # IT CAN GO AWAY WHEN SEARCHBUILDER'S _isLimited IS 
-% # PROMOTED TO A PUBLIC API XXX TODO
-% if ($articles->BuildSelectCountQuery =~ /WHERE/i) {
-<h2><&|/l&>Search results</&></h2>
-% if ($articles->Count){
-
-<table width="100%" border="0" cellpadding="2" cellspacing="0">
-<tr class="collection-as-table">
-<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('id'), 'id') |n %></th>
-<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Name'), 'Name') |n %></th>
-<th style="text-align: left" class="collection-as-table"><&|/l&>Class</&></th>
-<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Created'), 'Created') |n %></th>
-<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Modified'), 'LastUpdated') |n %></th>
-</tr>
-<tr class="collection-as-table">
-<th class="collection-as-table"></th>
-<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Summary'), 'Summary') |n %></th>
-<th style="text-align: left" class="collection-as-table" colspan="3"><&|/l&>Topics</&></th>
-</tr>
-
-% while (my $article = $articles->Next) {
-%$i++;
-<tr \
-% if ($i%2) {
-class="oddline" \
-% } else {
-class="evenline" \
-% }
->
-<td rowspan="2"><b><a href="Display.html?id=<%$article->Id%>">#<%$article->id%></a></b></td>
-<td>            <b><a href="Display.html?id=<%$article->Id%>"><%$article->Name || loc('(no name)')%></a></b></td>
-<td>            <%$article->ClassObj->Name%></td>
-<td><small>     <%$article->CreatedObj->AgeAsString || '-'%></small></td>
-<td><small>     <%$article->LastUpdatedObj->AgeAsString || '-'%></small></td>
-</tr>
-<tr \
-% if ($i%2) {
-class="oddline" \
-% } else {
-class="evenline" \
-% }
->
-<td><small>     <%$article->Summary%></small></td>
-<td colspan="3"><small>
-% my $Topics = $article->Topics;
-% while (my $t = $Topics->Next) {
-<& /RTFM/Elements/ShowTopic, topic => $t->TopicObj &><br />
-% }
-</small></td>
-</tr>
-% }
-</table>
-% }
-% else {
-<i><&|/l&>No articles found.</&></i>
-% }
-% }
-<%init>
-my $i;
-
-my @OrderBy = $query{'OrderBy'}? @{ $query{'OrderBy'} }: ();
-my @Order = $query{'Order'}? @{ $query{'Order'} }: ();
-
-my $sort_link = sub {
-    my ($title, $column) = @_;
-    my $res = qq{<a href="$RT::WebPath/RTFM/Article/Search.html?};
-    my $order;
-    for ( grep $OrderBy[$_] eq $column, 0..$#OrderBy ) {
-        $order = 'DESC' if !$Order[$_] || $Order[$_] =~ /^ASC/i;
-    }
-    $res .= $m->comp('/Elements/QueryString', %query, OrderBy => $column, Order => ($order || 'ASC'));
-
-    $res .= q{">} . $m->interp->apply_escapes($title, 'h')
-         . q{</a>};
-    return $res;
-};
-</%init>
-<%args>
-$articles => undef
-$did_search => 1
-%query => ()
-</%args>
diff --git a/html/RTFM/Article/Search.html b/html/RTFM/Article/Search.html
index 8e4ed1a..854cc49 100644
--- a/html/RTFM/Article/Search.html
+++ b/html/RTFM/Article/Search.html
@@ -76,7 +76,16 @@
 <& /Elements/ListActions, actions => \@results &>
 <div style="float: right"><a href="#criteria"><&|/l&>Modify this search...</&></a></div>
 
-<& Elements/ShowSearchResults, query => \%filtered, articles => $articles &>
+% if ($articles->BuildSelectCountQuery =~ /WHERE/i) {
+<h2><&|/l&>Search results</&></h2>
+<& /Elements/CollectionList, %ARGS, 
+    Collection => $articles, 
+    AllowSorting => 1,
+    Class => 'RT::FM::ArticleCollection',
+    Format => $format,
+    GenericQueryArgs => { %filtered, Format => $format, },
+    &>
+% }
 
 <br />
 <br />
@@ -99,6 +108,14 @@
 use RT::SavedSearch;
 my @results;
 my $articles = RT::FM::ArticleCollection->new( $session{'CurrentUser'} );
+my $format = q{
+    '<a href="/RTFM/Article/Display.html?id=__id__">__id__</a>/TITLE:#',
+    '<a href="/RTFM/Article/Display.html?id=__id__">__Name__</a>/TITLE:Name',
+    '__ClassName__',
+    '__CreatedRelative__',
+    '__LastUpdatedRelative__',
+    '__Summary__',
+    '__Topics__', };
 
 # {{{ Quicksearch logic
 

commit 9e718c882468a5a72682201d74ee71e34997773b
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Jul 20 09:26:49 2010 +0800

    saved search support for dashboards

diff --git a/html/Callbacks/RTFM/Elements/ShowSearch/ModifySearch b/html/Callbacks/RTFM/Elements/ShowSearch/ModifySearch
new file mode 100644
index 0000000..a1f1b3e
--- /dev/null
+++ b/html/Callbacks/RTFM/Elements/ShowSearch/ModifySearch
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC 
+%#                                          <sales at 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/copyleft/gpl.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 }}}
+<%init>
+if ( $OriginalSearch->{SearchType} eq 'Article' && $OriginalSearch->{args} 
+    && ref $OriginalSearch->{args} eq 'HASH' ) {
+    %$OriginalSearch = ( %$OriginalSearch, %{$OriginalSearch->{args}} );
+    delete $OriginalSearch->{args};
+
+    $OriginalSearch->{LoadSavedSearch} ||= $ARGS{SavedSearch};
+}
+
+</%init>
+
+<%args>
+$OriginalSearch => undef
+</%args>
diff --git a/html/Search/Article.html b/html/Search/Article.html
new file mode 100644
index 0000000..7ac159a
--- /dev/null
+++ b/html/Search/Article.html
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC 
+%#                                          <sales at 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/copyleft/gpl.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 }}}
+
+<%init>
+RT::Interface::Web::Redirect(RT->Config->Get('WebURL').  'RTFM/Article/Search.html?' . $m->comp( '/Elements/QueryString', %ARGS, Load => 'Load' ) );
+</%init>
diff --git a/html/Search/Elements/Article b/html/Search/Elements/Article
index b4f22af..732bcae 100644
--- a/html/Search/Elements/Article
+++ b/html/Search/Elements/Article
@@ -1,5 +1,64 @@
-<&|/l&>RTFM Article searches cannot be displayed inline, you can view the results</&>
-<a href="<%$RT::WebPath%>/RTFM/Article/Search.html<%$QueryString%>"><&|/l&>here</&></a>
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC 
+%#                                          <sales at 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/copyleft/gpl.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 }}}
+<& /Elements/CollectionList, %ARGS, 
+    Collection => $articles, 
+    Class => 'RT::FM::ArticleCollection',
+    Format => q{
+    '<a href="/RTFM/Article/Display.html?id=__id__">__id__</a>/TITLE:#',
+    '<a href="/RTFM/Article/Display.html?id=__id__">__Name__</a>/TITLE:Name',
+    '__ClassName__',
+    '__CreatedRelative__',
+    '__LastUpdatedRelative__',
+    '__Summary__',
+    '__Topics__', }
+    &>
 <%INIT>
 my $QueryString = "?".$m->comp('/Elements/QueryString', %{$ARGS{args}});
+my $articles = RT::FM::ArticleCollection->new( $session{CurrentUser} );
+$articles->Search( %{$ARGS{args}} );
 </%INIT>

-----------------------------------------------------------------------


More information about the Rt-commit mailing list