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

BPS Git Server git at git.bestpractical.com
Fri Feb 4 15:24:37 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  14246e494a2936293746caffd89f80512445def1 (commit)

- Log -----------------------------------------------------------------
commit 14246e494a2936293746caffd89f80512445def1
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 4b113b01092a69791defd0af3d097a801c5588a2
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..99ee6c6f90 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');
+    }
+}
+
 # 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..f67fdea75f 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');
+    }
+}
+
 $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 fb00f36e9d9dcac00bbd667e88fb97f6b83d7a86
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.

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