]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 534057: Auto-completion no longer works in email_in.pl
authorFrédéric Buclin <LpSolit@gmail.com>
Mon, 1 Feb 2010 21:11:53 +0000 (13:11 -0800)
committerMax Kanat-Alexander <mkanat@bugzilla.org>
Mon, 1 Feb 2010 21:11:53 +0000 (13:11 -0800)
Patch by Frédéric Buclin <LpSolit@gmail.com> r/a=mkanat

Bugzilla/Flag.pm
Bugzilla/User.pm
editcomponents.cgi
editwhines.cgi
email_in.pl
post_bug.cgi
process_bug.cgi
request.cgi
template/en/default/global/user-error.html.tmpl
userprefs.cgi

index 2fa4c8dedb29dd9b12e096a9d3bb21c3fefff469..d33b14d316db9d1f72a0a66fd6b4167d0621b29c 100644 (file)
@@ -790,9 +790,9 @@ sub extract_flags_from_cgi {
     my ($class, $bug, $attachment, $vars, $skip) = @_;
     my $cgi = Bugzilla->cgi;
 
-    my $match_status = Bugzilla::User::match_field($cgi, {
+    my $match_status = Bugzilla::User::match_field({
         '^requestee(_type)?-(\d+)$' => { 'type' => 'multi' },
-    }, $skip);
+    }, undef, $skip);
 
     $vars->{'match_field'} = 'requestee';
     if ($match_status == USER_MATCH_FAILED) {
index 75a4fcf1de620974023625fea848ba81b2dc4455..59324383f86698ecc7043726076173e5391dcd81 100644 (file)
@@ -1124,57 +1124,15 @@ sub match {
     return \@users;
 }
 
-# match_field() is a CGI wrapper for the match() function.
-#
-# Here's what it does:
-#
-# 1. Accepts a list of fields along with whether they may take multiple values
-# 2. Takes the values of those fields from the first parameter, a $cgi object 
-#    and passes them to match()
-# 3. Checks the results of the match and displays confirmation or failure
-#    messages as appropriate.
-#
-# The confirmation screen functions the same way as verify-new-product and
-# confirm-duplicate, by rolling all of the state information into a
-# form which is passed back, but in this case the searched fields are
-# replaced with the search results.
-#
-# The act of displaying the confirmation or failure messages means it must
-# throw a template and terminate.  When confirmation is sent, all of the
-# searchable fields have been replaced by exact fields and the calling script
-# is executed as normal.
-#
-# You also have the choice of *never* displaying the confirmation screen.
-# In this case, match_field will return one of the three USER_MATCH 
-# constants described in the POD docs. To make match_field behave this
-# way, pass in MATCH_SKIP_CONFIRM as the third argument.
-#
-# match_field must be called early in a script, before anything external is
-# done with the form data.
-#
-# In order to do a simple match without dealing with templates, confirmation,
-# or globals, simply calling Bugzilla::User::match instead will be
-# sufficient.
-
-# How to call it:
-#
-# Bugzilla::User::match_field($cgi, {
-#   'field_name'    => { 'type' => fieldtype },
-#   'field_name2'   => { 'type' => fieldtype },
-#   [...]
-# });
-#
-# fieldtype can be either 'single' or 'multi'.
-#
-
 sub match_field {
-    my $cgi          = shift;   # CGI object to look up fields in
     my $fields       = shift;   # arguments as a hash
+    my $data         = shift || Bugzilla->input_params; # hash to look up fields in
     my $behavior     = shift || 0; # A constant that tells us how to act
     my $matches      = {};      # the values sent to the template
     my $matchsuccess = 1;       # did the match fail?
     my $need_confirm = 0;       # whether to display confirmation screen
     my $match_multiple = 0;     # whether we ever matched more than one user
+    my @non_conclusive_fields;  # fields which don't have a unique user.
 
     my $params = Bugzilla->params;
 
@@ -1192,7 +1150,8 @@ sub match_field {
             $expanded_fields->{$field_pattern} = $fields->{$field_pattern};
         }
         else {
-            my @field_names = grep(/$field_pattern/, $cgi->param());
+            my @field_names = grep(/$field_pattern/, keys %$data);
+
             foreach my $field_name (@field_names) {
                 $expanded_fields->{$field_name} = 
                   { type => $fields->{$field_pattern}->{'type'} };
@@ -1218,7 +1177,7 @@ sub match_field {
                         # No need to look for a valid requestee if the flag(type)
                         # has been deleted (may occur in race conditions).
                         delete $expanded_fields->{$field_name};
-                        $cgi->delete($field_name);
+                        delete $data->{$field_name};
                     }
                 }
             }
@@ -1227,35 +1186,19 @@ sub match_field {
     $fields = $expanded_fields;
 
     for my $field (keys %{$fields}) {
+        #Concatenate login names, so that we have a common way to handle them.
+        my $raw_field;
+        if (ref $data->{$field}) {
+            $raw_field = join(" ", @{$data->{$field}});
+        }
+        else {
+            $raw_field = $data->{$field};
+        }
+        $raw_field = clean_text($raw_field || '');
 
-        # Tolerate fields that do not exist.
-        #
-        # This is so that fields like qa_contact can be specified in the code
-        # and it won't break if the CGI object does not know about them.
-        #
-        # It has the side-effect that if a bad field name is passed it will be
-        # quietly ignored rather than raising a code error.
-
-        next if !defined $cgi->param($field);
-
-        # We need to move the query to $raw_field, where it will be split up,
-        # modified by the search, and put back into the CGI environment
-        # incrementally.
-
-        my $raw_field = join(" ", $cgi->param($field));
-
-        # When we add back in values later, it matters that we delete
-        # the field here, and not set it to '', so that we will add
-        # things to an empty list, and not to a list containing one
-        # empty string.
-        # If the field accepts only one match (type eq "single") and
-        # no match or more than one match is found for this field,
-        # we will set it back to '' so that the field remains defined
-        # outside this function (it was if we came here; else we would
-        # have returned earlier above).
-        # If the field accepts several matches (type eq "multi") and no match
-        # is found, we leave this field undefined (= empty array).
-        $cgi->delete($field);
+        # Tolerate fields that do not exist (in case you specify
+        # e.g. the QA contact, and it's currently not in use).
+        next unless ($raw_field && $raw_field ne '');
 
         my @queries = ();
 
@@ -1263,11 +1206,9 @@ sub match_field {
         # into @queries, or in the case of fields which only accept single
         # entries, we simply use the verbatim text.
 
-        $raw_field =~ s/^\s+|\s+$//sg;  # trim leading/trailing space
-
         # single field
         if ($fields->{$field}->{'type'} eq 'single') {
-            @queries = ($raw_field) unless $raw_field =~ /^\s*$/;
+            @queries = ($raw_field);
 
         # multi-field
         }
@@ -1288,35 +1229,22 @@ sub match_field {
             $limit = $params->{'maxusermatches'} + 1;
         }
 
+        my @logins;
         for my $query (@queries) {
-
             my $users = match(
                 $query,   # match string
                 $limit,   # match limit
                 1         # exclude_disabled
             );
 
-            # skip confirmation for exact matches
-            if ((scalar(@{$users}) == 1)
-                && (lc(@{$users}[0]->login) eq lc($query)))
-
-            {
-                $cgi->append(-name=>$field,
-                             -values=>[@{$users}[0]->login]);
-
-                next;
-            }
-
-            $matches->{$field}->{$query}->{'users'}  = $users;
-            $matches->{$field}->{$query}->{'status'} = 'success';
-
             # here is where it checks for multiple matches
-
             if (scalar(@{$users}) == 1) { # exactly one match
+                push(@logins, @{$users}[0]->login);
 
-                $cgi->append(-name=>$field,
-                             -values=>[@{$users}[0]->login]);
+                # skip confirmation for exact matches
+                next if (lc(@{$users}[0]->login) eq lc($query));
 
+                $matches->{$field}->{$query}->{'status'} = 'success';
                 $need_confirm = 1 if $params->{'confirmuniqueusermatch'};
 
             }
@@ -1324,6 +1252,7 @@ sub match_field {
                     && ($params->{'maxusermatches'} != 1)) {
                 $need_confirm = 1;
                 $match_multiple = 1;
+                push(@non_conclusive_fields, $field);
 
                 if (($params->{'maxusermatches'})
                    && (scalar(@{$users}) > $params->{'maxusermatches'}))
@@ -1331,23 +1260,31 @@ sub match_field {
                     $matches->{$field}->{$query}->{'status'} = 'trunc';
                     pop @{$users};  # take the last one out
                 }
+                else {
+                    $matches->{$field}->{$query}->{'status'} = 'success';
+                }
 
             }
             else {
                 # everything else fails
                 $matchsuccess = 0; # fail
+                push(@non_conclusive_fields, $field);
                 $matches->{$field}->{$query}->{'status'} = 'fail';
                 $need_confirm = 1;  # confirmation screen shows failures
             }
+
+            $matches->{$field}->{$query}->{'users'}  = $users;
         }
-        # Above, we deleted the field before adding matches. If no match
-        # or more than one match has been found for a field expecting only
-        # one match (type eq "single"), we set it back to '' so
-        # that the caller of this function can still check whether this
+
+        # If no match or more than one match has been found for a field
+        # expecting only one match (type eq "single"), we set it back to ''
+        # so that the caller of this function can still check whether this
         # field was defined or not (and it was if we came here).
-        if (!defined $cgi->param($field)
-            && $fields->{$field}->{'type'} eq 'single') {
-            $cgi->param($field, '');
+        if ($fields->{$field}->{'type'} eq 'single') {
+            $data->{$field} = $logins[0] || '';
+        }
+        else {
+            $data->{$field} = \@logins;
         }
     }
 
@@ -1363,22 +1300,24 @@ sub match_field {
     }
 
     # Skip confirmation if we were told to, or if we don't need to confirm.
-    return $retval if ($behavior == MATCH_SKIP_CONFIRM || !$need_confirm);
+    if ($behavior == MATCH_SKIP_CONFIRM || !$need_confirm) {
+        return wantarray ? ($retval, \@non_conclusive_fields) : $retval;
+    }
 
     my $template = Bugzilla->template;
+    my $cgi = Bugzilla->cgi;
     my $vars = {};
 
-    $vars->{'script'}        = Bugzilla->cgi->url(-relative => 1); # for self-referencing URLs
+    $vars->{'script'}        = $cgi->url(-relative => 1); # for self-referencing URLs
     $vars->{'fields'}        = $fields; # fields being matched
     $vars->{'matches'}       = $matches; # matches that were made
     $vars->{'matchsuccess'}  = $matchsuccess; # continue or fail
     $vars->{'matchmultiple'} = $match_multiple;
 
-    print Bugzilla->cgi->header();
+    print $cgi->header();
 
     $template->process("global/confirm-user-match.html.tmpl", $vars)
       || ThrowTemplateError($template->error());
-
     exit;
 
 }
@@ -2296,6 +2235,49 @@ Untaints C<$passwd1> if successful.
 If a second password is passed in, this function also verifies that
 the two passwords match.
 
+=item C<match_field($data, $fields, $behavior)>
+
+=over
+
+=item B<Description>
+
+Wrapper for the C<match()> function.
+
+=item B<Params>
+
+=over
+
+=item C<$fields> - A hashref with field names as keys and a hash as values.
+Each hash is of the form { 'type' => 'single|multi' }, which specifies
+whether the field can take a single login name only or several.
+
+=item C<$data> (optional) - A hashref with field names as keys and field values
+as values. If undefined, C<Bugzilla-E<gt>input_params> is used.
+
+=item C<$behavior> (optional) - If set to C<MATCH_SKIP_CONFIRM>, no confirmation
+screen is displayed. In that case, the fields which don't match a unique user
+are left undefined. If not set, a confirmation screen is displayed if at
+least one field doesn't match any login name or match more than one.
+
+=back
+
+=item B<Returns>
+
+If the third parameter is set to C<MATCH_SKIP_CONFIRM>, the function returns
+either C<USER_MATCH_SUCCESS> if all fields can be set unambiguously,
+C<USER_MATCH_FAILED> if at least one field doesn't match any user account,
+or C<USER_MATCH_MULTIPLE> if some fields match more than one user account.
+
+If the third parameter is not set, then if all fields could be set
+unambiguously, nothing is returned, else a confirmation page is displayed.
+
+=item B<Note>
+
+This function must be called early in a script, before anything external
+is done with the data.
+
+=back
+
 =back
 
 =head1 SEE ALSO
index c550f0d8c358d70f1254f7c497d420908facbbd0..70afd9c9ff240497477995337fabdaa79bb49bcc 100755 (executable)
@@ -118,7 +118,7 @@ if ($action eq 'add') {
 if ($action eq 'new') {
     check_token_data($token, 'add_component');
     # Do the user matching
-    Bugzilla::User::match_field ($cgi, {
+    Bugzilla::User::match_field ({
         'initialowner'     => { 'type' => 'single' },
         'initialqacontact' => { 'type' => 'single' },
         'initialcc'        => { 'type' => 'multi'  },
@@ -219,7 +219,7 @@ if ($action eq 'edit') {
 if ($action eq 'update') {
     check_token_data($token, 'edit_component');
     # Do the user matching
-    Bugzilla::User::match_field ($cgi, {
+    Bugzilla::User::match_field ({
         'initialowner'     => { 'type' => 'single' },
         'initialqacontact' => { 'type' => 'single' },
         'initialcc'        => { 'type' => 'multi'  },
index 671774ef730dd342f7b6700ca2986e09deda52e7..c59cf8be5e3f72dcffcbc2151782ed22a1849016 100755 (executable)
@@ -193,7 +193,7 @@ if ($cgi->param('update')) {
                 }
             }
             if (scalar %{$arglist}) {
-                &Bugzilla::User::match_field($cgi, $arglist);
+                Bugzilla::User::match_field($arglist);
             }
 
             for my $sid (@scheduleids) {
index 044c1304090f777e96410955efa2b00223bee10f..c7b11d8a720a6db855382919cda5906d086ccbb1 100755 (executable)
@@ -152,6 +152,17 @@ sub post_bug {
         $fields->{$field} = undef if !exists $fields->{$field};
     }
 
+    my ($retval, $non_conclusive_fields) =
+      Bugzilla::User::match_field({
+        'assigned_to'   => { 'type' => 'single' },
+        'qa_contact'    => { 'type' => 'single' },
+        'cc'            => { 'type' => 'multi'  }
+      }, $fields, MATCH_SKIP_CONFIRM);
+
+    if ($retval != USER_MATCH_SUCCESS) {
+        ThrowUserError('user_match_too_many', {fields => $non_conclusive_fields});
+    }
+
     my $bug = Bugzilla::Bug->create($fields);
     debug_print("Created bug " . $bug->id);
     return ($bug, $bug->comments->[0]);
index 749acb492eead778f1125707d9a595c24f3a30a4..e8f9acc017361b01e336afac6dc605feb0fa0fb3 100755 (executable)
@@ -85,7 +85,7 @@ if ($token) {
 }    
 
 # do a match on the fields if applicable
-Bugzilla::User::match_field ($cgi, {
+Bugzilla::User::match_field ({
     'cc'            => { 'type' => 'multi'  },
     'assigned_to'   => { 'type' => 'single' },
     'qa_contact'    => { 'type' => 'single' },
index 21cf94fc79a680beb18cef9668e622936051b261..6c9baa3d3d53d57f72203af7a150b42d640f6ee6 100755 (executable)
@@ -142,7 +142,7 @@ if (defined $cgi->param('dontchange')) {
 }
 
 # do a match on the fields if applicable
-Bugzilla::User::match_field($cgi, {
+Bugzilla::User::match_field({
     'qa_contact'                => { 'type' => 'single' },
     'newcc'                     => { 'type' => 'multi'  },
     'masscc'                    => { 'type' => 'multi'  },
index 594ed183366684a9b4142607a56b73953ca8baa8..b54477cb19097189a96192778e77a74f38bba182 100755 (executable)
@@ -63,7 +63,7 @@ unless (defined $cgi->param('requestee')
     $fields->{'requestee'}->{'type'} = 'single';
 }
 
-Bugzilla::User::match_field($cgi, $fields);
+Bugzilla::User::match_field($fields);
 
 if ($action eq 'queue') {
     queue();
index 80172bb03ae62c95d0a75e352d40465dc298cccb..467d4a174c6f1daa6e342a75816cbbca89e9802f 100644 (file)
     <tt>[% name FILTER html %]</tt> does not exist or you are not allowed 
     to see that user.
 
+  [% ELSIF error == "user_match_too_many" %]
+    [% title = "No Conclusive Match" %]
+    [% terms.Bugzilla %] cannot make a conclusive match for one or more
+    of the names and/or email addresses you entered for
+    the [% fields.join(', ') FILTER html %] field(s).
+
   [% ELSIF error == "user_not_insider" %]
     [% title = "User Not In Insidergroup" %]
     Sorry, but you are not allowed to (un)mark comments or attachments
index cffae38ccbdfdd62dbf810da5b45cee121d41c61..e6ee8fb8a0012f22959b1f33a8027c8ad7497e8c 100755 (executable)
@@ -247,7 +247,7 @@ sub SaveEmail {
     my $cgi = Bugzilla->cgi;
     my $user = Bugzilla->user;
 
-    Bugzilla::User::match_field($cgi, { 'new_watchedusers' => {'type' => 'multi'} });
+    Bugzilla::User::match_field({ 'new_watchedusers' => {'type' => 'multi'} });
 
     ###########################################################################
     # Role-based preferences