[Rt-commit] rt branch 5.0/autocomplete-article-inclusion created. rt-5.0.2-53-gc8274d2937

BPS Git Server git at git.bestpractical.com
Tue Feb 8 14:48:32 UTC 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 5.0/autocomplete-article-inclusion has been created
        at  c8274d29376025a7d97dbf712b6260d463bab26c (commit)

- Log -----------------------------------------------------------------
commit c8274d29376025a7d97dbf712b6260d463bab26c
Author: Brian Conry <bconry at bestpractical.com>
Date:   Fri Feb 4 08:18:49 2022 -0600

    Add configurable search for Include Article
    
    Before RT 5.0 the search box to include an article (only shown when the
    number of articles exceeds a configurable limit, default 50) searched on
    multiple fields, but in the update to 5.0 the search was restricted to
    only the Article Name.
    
    This expands on the previous functionality, patterning it after User
    search by making the list of matched fields configurable.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index b0b0c5cc65..9717ac3a75 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2541,6 +2541,23 @@ preference.
 
 Set($AutocompleteQueues, 0);
 
+=item C<$ArticleSearchFields>
+
+Used when searching for an Article to Include.
+
+Specifies which fields of L<RT::Article> to match against and how to match
+each field when autocompleting articles.  Valid match methods are LIKE,
+STARTSWITH, ENDSWITH, =, and !=.  Valid search fields are the core Article
+fields, as well as custom fields, including Content, which are specified as
+"CF.1234" or "CF.Name"
+
+=cut
+
+Set($ArticleSearchFields, {
+    Name         => 'STARTSWITH',
+    Summary      => 'LIKE',
+});
+
 =item C<$UserSearchFields>
 
 Used by the User Autocompleter as well as the User Search.
diff --git a/lib/RT/Articles.pm b/lib/RT/Articles.pm
index 0e569d201e..e87ae4d3c6 100644
--- a/lib/RT/Articles.pm
+++ b/lib/RT/Articles.pm
@@ -875,6 +875,99 @@ sub Search {
     return 1;
 }
 
+=head2 SimpleSearch
+
+Does a 'simple' search of Articles against a specified Term.
+
+This Term is compared to a number of fields using various types of SQL
+comparison operators.
+
+Ensures that the returned collection of Articles will have a value for Return.
+
+This method is passed the following.  You must specify a Term and a Return.
+
+    Queue      - Limit the search to classes applied to this queue
+    Fields     - Hashref of data - defaults to C<$ArticleSearchFields> emulate that if you want to override
+    Term       - String that is in the fields specified by Fields
+    Return     - What field on the Article you want to be sure isn't empty
+    Max        - What to limit this collection to
+
+=cut
+
+sub SimpleSearch {
+    my $self = shift;
+    my %args = (
+        Queue       => undef,
+        Fields      => RT->Config->Get('ArticleSearchFields'),
+        Term        => undef,
+        Return      => undef,
+        Max         => 10,
+        @_
+    );
+
+    return $self unless defined $args{Return}
+                        and defined $args{Term}
+                        and length $args{Term};
+
+    $self->RowsPerPage( $args{Max} );
+
+    if ($args{Queue}) {
+        $self->LimitAppliedClasses( Queue => $args{Queue} );
+    }
+
+    while (my ($name, $op) = each %{$args{Fields}}) {
+        $op = 'STARTSWITH'
+            unless $op =~ /^(?:LIKE|(?:START|END)SWITH|=|!=)$/i;
+
+        if ($name =~ /^CF\.(?:\{(.*)}|(.*))$/) {
+            my $cfname = $1 || $2;
+            my $cf = RT::CustomField->new(RT->SystemUser);
+            my ($ok, $msg) = $cf->LoadByName(
+                Name => $cfname,
+                LookupType => RT::Article->new( $self->CurrentUser )->CustomFieldLookupType,
+            );
+            if ( $ok ) {
+                $self->LimitCustomField(
+                    CUSTOMFIELD     => $cf->Id,
+                    OPERATOR        => $op,
+                    VALUE           => $args{Term},
+                    ENTRYAGGREGATOR => 'OR',
+                    SUBCLAUSE       => 'autocomplete',
+                );
+            } else {
+                RT->Logger->warning("Asked to search custom field $name but unable to load an Article CF with the name $cfname: $msg");
+            }
+        } else {
+            $self->Limit(
+                FIELD           => $name,
+                OPERATOR        => $op,
+                VALUE           => $args{Term},
+                ENTRYAGGREGATOR => 'OR',
+                SUBCLAUSE       => 'autocomplete',
+            );
+        }
+    }
+
+    if ( RT->Config->Get('DatabaseType') eq 'Oracle' ) {
+        $self->Limit(
+            FIELD    => $args{Return},
+            OPERATOR => 'IS NOT',
+            VALUE    => 'NULL',
+        );
+    }
+    elsif ( $args{Return} !~ /^id$/i ) {
+        $self->Limit( FIELD => $args{Return}, OPERATOR => '!=', VALUE => '' );
+        $self->Limit(
+            FIELD           => $args{Return},
+            OPERATOR        => 'IS NOT',
+            VALUE           => 'NULL',
+            ENTRYAGGREGATOR => 'AND'
+        );
+    }
+
+    return $self;
+}
+
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/share/html/Helpers/Autocomplete/Articles b/share/html/Helpers/Autocomplete/Articles
index 825900b7ca..79fc0ce27e 100644
--- a/share/html/Helpers/Autocomplete/Articles
+++ b/share/html/Helpers/Autocomplete/Articles
@@ -50,10 +50,10 @@
 % $m->abort;
 <%ARGS>
 $term       => undef
-$max        => 10
-$op         => 'STARTSWITH'
+$max        => undef
+$op         => undef
 $right      => undef
-$return     => 'Name'
+$return     => ''
 $queue      => undef
 </%ARGS>
 <%INIT>
@@ -65,24 +65,18 @@ $m->abort unless defined $return
              and defined $term
              and length $term;
 
-# Sanity check the operator
-$op = 'STARTSWITH' unless $op =~ /^(?:LIKE|(?:START|END)SWITH|=|!=)$/i;
-
 $m->callback( CallbackName => 'ModifyMaxResults', max => \$max );
+$max //= 10;
 
 my $articles = RT::Articles->new( $session{CurrentUser} );
-if( $queue ) {
-    $articles->LimitAppliedClasses( Queue => $queue );
-}
-
-$articles->RowsPerPage( $max );
-$articles->Limit(
-    FIELD           => 'Name',
-    OPERATOR        => $op,
-    VALUE           => $term,
-    ENTRYAGGREGATOR => 'OR',
-    CASESENSITIVE   => 0,
+$articles->SimpleSearch(
+    Queue           => $queue,
+    Return          => $return,
+    Term            => $term,
+    Max             => $max,
+    $op ? ( Fields => { $return => $op } ) : (),
 );
+$m->callback( CallbackName => "ModifyArticlesLimit", Articles => $articles, Term => $term, ARGSRef => \%ARGS );
 
 my @suggestions;
 while (my $a = $articles->Next) {

commit 06f5cd1753a4f32d3acc82276695f3c30e71407f
Author: Brian Conry <bconry at bestpractical.com>
Date:   Fri Feb 4 08:09:02 2022 -0600

    Notify user when unable to include an article
    
    When there are enough articles that the "Include Article" dropdown is
    replaced with a textbox for search (default 50, but configurable) it is
    possible to submit the Ticket Create and Update pages with text that
    can't be resolved to an article to be included.
    
    Previously the user wasn't notified of this, and in the case of ticket
    updates the update was committed.  Now the user will be told that the
    article couldn't be loaded and the create/update will not be committed.

diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index bded7f6e57..5ae39fd4e5 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -493,6 +493,17 @@ if ( $ARGS{SubmitTicket} ) {
     $checks_failure = 1 unless $status;
 }
 
+if ($ARGS{IncludeArticleId}) {
+    my $article = RT::Article->new($session{'CurrentUser'});
+    my ($ret, $msg) = $article->Load( $ARGS{IncludeArticleId} );
+
+    if (!$ret) {
+        # Make sure we don't finalize the ticket if we won't be able to include the article
+        $checks_failure = 1;
+        push @results, loc('Unable to load article "[_1]"', $ARGS{IncludeArticleId});
+    }
+}
+
 # check email addresses for RT's
 {
     foreach my $field ( qw(Requestors Cc AdminCc) ) {
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index 21bb8ed522..c1b7f38cf0 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -341,6 +341,17 @@ my $TicketObj = LoadTicket($id);
 
 my @results;
 
+if ($ARGS{IncludeArticleId}) {
+    my $article = RT::Article->new($session{'CurrentUser'});
+    my ($ret, $msg) = $article->Load( $ARGS{IncludeArticleId} );
+
+    if (!$ret) {
+        # Make sure we don't finalize the update if we won't be able to include the article
+        $checks_failure = 1;
+        push @results, loc('Unable to load article "[_1]"', $ARGS{IncludeArticleId});
+    }
+}
+
 $m->callback( Ticket => $TicketObj, ARGSRef => \%ARGS, checks_failure => \$checks_failure, results => \@results, CallbackName => 'Initial' );
 $m->scomp( '/Articles/Elements/SubjectOverride', Ticket => $TicketObj, ARGSRef => \%ARGS, results => \@results );
 

commit b57dbf94781c5c1701f82f141dbb0ecd4eb261f1
Author: Brian Conry <bconry at bestpractical.com>
Date:   Fri Feb 4 08:01:00 2022 -0600

    Add RT::Article::Load
    
    This cleanly handles being called with a non-numeric "id", using it as a
    Name instead.  This prevents some database drivers from throwing errors
    when they can't convert the string to a number.
    
    Specifically, the article search box on ticket create and update can
    accept a string value that doesn't autocomplete to an existing article.
    This string value can be passed to Load and this previously resulted in
    DB errors in the logs.

diff --git a/lib/RT/Article.pm b/lib/RT/Article.pm
index be56bf0641..2402c857b1 100644
--- a/lib/RT/Article.pm
+++ b/lib/RT/Article.pm
@@ -887,6 +887,18 @@ sub PostInflate {
     $self->__Set( Field => 'URI', Value => $self->URI );
 }
 
+sub Load {
+    my $self = shift;
+    my $id = shift || '';
+
+    if ($id and $id =~ /^\d+$/) {
+        return $self->LoadById( $id );
+    }
+    else {
+        return $self->LoadByCols( Name => $id );
+    }
+}
+
 RT::Base->_ImportOverlays();
 
 1;

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


hooks/post-receive
-- 
rt


More information about the rt-commit mailing list