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