blob: d82409882b5d72aac5aa5e4c863863cf545d5a42 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Classes that implement the advanced search feature page.
7
8The advanced search page simply displays an HTML page with a form.
9The form handler converts the widget-based query into a googley query
10string and redirects the user to the issue list servlet.
11"""
12from __future__ import print_function
13from __future__ import division
14from __future__ import absolute_import
15
16import logging
17import re
18
19from features import savedqueries_helpers
20from framework import framework_helpers
21from framework import permissions
22from framework import servlet
23from framework import urls
24
25# Patterns for search values that can be words, labels,
26# component paths, or email addresses.
27VALUE_RE = re.compile(r'[-a-zA-Z0-9._>@]+')
28
29
30class IssueAdvancedSearch(servlet.Servlet):
31 """IssueAdvancedSearch shows a form to enter an advanced search."""
32
33 _PAGE_TEMPLATE = 'tracker/issue-advsearch-page.ezt'
34 _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_ISSUES
35
36 # This form *only* redirects to a GET request, and permissions are checked
37 # in that handler.
38 CHECK_SECURITY_TOKEN = False
39
40 def GatherPageData(self, mr):
41 """Build up a dictionary of data values to use when rendering the page.
42
43 Args:
44 mr: commonly used info parsed from the request.
45
46 Returns:
47 Dict of values used by EZT for rendering the page.
48 """
49 # TODO(jrobbins): Allow deep-linking into this page.
50 canned_query_views = []
51 if mr.project_id:
52 with mr.profiler.Phase('getting canned queries'):
53 canned_queries = self.services.features.GetCannedQueriesByProjectID(
54 mr.cnxn, mr.project_id)
55 canned_query_views = [
56 savedqueries_helpers.SavedQueryView(sq, idx + 1, None, None)
57 for idx, sq in enumerate(canned_queries)]
58
59 saved_query_views = []
60 if mr.auth.user_id and self.services.features:
61 with mr.profiler.Phase('getting saved queries'):
62 saved_queries = self.services.features.GetSavedQueriesByUserID(
63 mr.cnxn, mr.me_user_id)
64 saved_query_views = [
65 savedqueries_helpers.SavedQueryView(sq, idx + 1, None, None)
66 for idx, sq in enumerate(saved_queries)
67 if (mr.project_id in sq.executes_in_project_ids or
68 not mr.project_id)]
69
70 return {
71 'issue_tab_mode': 'issueAdvSearch',
72 'page_perms': self.MakePagePerms(mr, None, permissions.CREATE_ISSUE),
73 'canned_queries': canned_query_views,
74 'saved_queries': saved_query_views,
75 }
76
77 def ProcessFormData(self, mr, post_data):
78 """Process a posted advanced query form.
79
80 Args:
81 mr: commonly used info parsed from the request.
82 post_data: HTML form data from the request.
83
84 Returns:
85 String URL to redirect the user to after processing.
86 """
87 # Default to searching open issues in this project.
88 can = post_data.get('can', 2)
89
90 terms = []
91 self._AccumulateANDTerm('', 'words', post_data, terms)
92 self._AccumulateANDTerm('-', 'without', post_data, terms)
93 self._AccumulateANDTerm('label:', 'labels', post_data, terms)
94 self._AccumulateORTerm('component:', 'components', post_data, terms)
95 self._AccumulateORTerm('status:', 'statuses', post_data, terms)
96 self._AccumulateORTerm('reporter:', 'reporters', post_data, terms)
97 self._AccumulateORTerm('owner:', 'owners', post_data, terms)
98 self._AccumulateORTerm('cc:', 'cc', post_data, terms)
99 self._AccumulateORTerm('commentby:', 'commentby', post_data, terms)
100
101 if 'starcount' in post_data:
102 starcount = int(post_data['starcount'])
103 if starcount >= 0:
104 terms.append('starcount:%s' % starcount)
105
106 return framework_helpers.FormatAbsoluteURL(
107 mr, urls.ISSUE_LIST, q=' '.join(terms), can=can)
108
109 def _AccumulateANDTerm(self, operator, form_field, post_data, search_query):
110 """Build a query that matches issues with ALL of the given field values."""
111 user_input = post_data.get(form_field)
112 if user_input:
113 values = VALUE_RE.findall(user_input)
114 search_terms = ['%s%s' % (operator, v) for v in values]
115 search_query.extend(search_terms)
116
117 def _AccumulateORTerm(self, operator, form_field, post_data, search_query):
118 """Build a query that matches issues with ANY of the given field values."""
119 user_input = post_data.get(form_field)
120 if user_input:
121 values = VALUE_RE.findall(user_input)
122 search_term = '%s%s' % (operator, ','.join(values))
123 search_query.append(search_term)