blob: a3386b41b9f78b8846b13df9f883369a331a6f89 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2018 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"""A servlet for project owners to edit/delete a template"""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import collections
12import logging
13import time
14
15import ezt
16
17from framework import authdata
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020018from framework import flaskservlet
Copybara854996b2021-09-07 19:36:02 +000019from framework import framework_bizobj
20from framework import framework_helpers
21from framework import framework_views
22from framework import servlet
23from framework import urls
24from framework import permissions
25from tracker import field_helpers
26from tracker import template_helpers
27from tracker import tracker_bizobj
28from tracker import tracker_helpers
29from tracker import tracker_views
30from proto import tracker_pb2
31from services import user_svc
32
33
34class TemplateDetail(servlet.Servlet):
35 """Servlet allowing project owners to edit/delete an issue template"""
36
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020037 _MAIN_TAB_MODE = flaskservlet.FlaskServlet.MAIN_TAB_PROCESS
Copybara854996b2021-09-07 19:36:02 +000038 _PAGE_TEMPLATE = 'tracker/template-detail-page.ezt'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020039 _PROCESS_SUBTAB = flaskservlet.FlaskServlet.PROCESS_TAB_TEMPLATES
Copybara854996b2021-09-07 19:36:02 +000040
41 def AssertBasePermission(self, mr):
42 """Check whether the user has any permission to visit this page.
43
44 Args:
45 mr: commonly used info parsed from the request.
46 """
47 super(TemplateDetail, self).AssertBasePermission(mr)
48 template = self.services.template.GetTemplateByName(mr.cnxn,
49 mr.template_name, mr.project_id)
50
51 if template:
52 allow_view = permissions.CanViewTemplate(
53 mr.auth.effective_ids, mr.perms, mr.project, template)
54 if not allow_view:
55 raise permissions.PermissionException(
56 'User is not allowed to view this issue template')
57 else:
58 self.abort(404, 'issue template not found %s' % mr.template_name)
59
60 def GatherPageData(self, mr):
61 """Build up a dictionary of data values to use when rendering the page.
62
63 Args:
64 mr: commonly used info parsed from the request.
65
66 Returns:
67 Dict of values used by EZT for rendering the page.
68 """
69
70 config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
71 template = self.services.template.GetTemplateByName(mr.cnxn,
72 mr.template_name, mr.project_id)
73 template_view = tracker_views.IssueTemplateView(
74 mr, template, self.services.user, config)
75 with mr.profiler.Phase('making user views'):
76 users_involved = tracker_bizobj.UsersInvolvedInTemplate(template)
77 users_by_id = framework_views.MakeAllUserViews(
78 mr.cnxn, self.services.user, users_involved)
79 framework_views.RevealAllEmailsToMembers(
80 mr.cnxn, self.services, mr.auth, users_by_id, mr.project)
81 field_name_set = {fd.field_name.lower() for fd in config.field_defs
82 if fd.field_type is tracker_pb2.FieldTypes.ENUM_TYPE and
83 not fd.is_deleted}
84 non_masked_labels = tracker_bizobj.NonMaskedLabels(
85 template.labels, field_name_set)
86
87 field_views = tracker_views.MakeAllFieldValueViews(
88 config, template.labels, [], template.field_values, users_by_id,
89 phases=template.phases)
90 uneditable_fields = ezt.boolean(False)
91 for fv in field_views:
92 if permissions.CanEditValueForFieldDef(
93 mr.auth.effective_ids, mr.perms, mr.project, fv.field_def.field_def):
94 fv.is_editable = ezt.boolean(True)
95 else:
96 fv.is_editable = ezt.boolean(False)
97 uneditable_fields = ezt.boolean(True)
98
99 (prechecked_approvals, required_approval_ids,
100 initial_phases) = template_helpers.GatherApprovalsPageData(
101 template.approval_values, template.phases, config)
102
103 allow_edit = permissions.CanEditTemplate(
104 mr.auth.effective_ids, mr.perms, mr.project, template)
105
106 return {
107 'admin_tab_mode':
108 self._PROCESS_SUBTAB,
109 'allow_edit':
110 ezt.boolean(allow_edit),
111 'uneditable_fields':
112 uneditable_fields,
113 'new_template_form':
114 ezt.boolean(False),
115 'initial_members_only':
116 template_view.members_only,
117 'template_name':
118 template_view.name,
119 'initial_summary':
120 template_view.summary,
121 'initial_must_edit_summary':
122 template_view.summary_must_be_edited,
123 'initial_content':
124 template_view.content,
125 'initial_status':
126 template_view.status,
127 'initial_owner':
128 template_view.ownername,
129 'initial_owner_defaults_to_member':
130 template_view.owner_defaults_to_member,
131 'initial_components':
132 template_view.components,
133 'initial_component_required':
134 template_view.component_required,
135 'fields':
136 [
137 view for view in field_views
138 if view.field_def.type_name is not 'APPROVAL_TYPE'
139 ],
140 'initial_add_approvals':
141 ezt.boolean(prechecked_approvals),
142 'initial_phases':
143 initial_phases,
144 'approvals':
145 [
146 view for view in field_views
147 if view.field_def.type_name is 'APPROVAL_TYPE'
148 ],
149 'prechecked_approvals':
150 prechecked_approvals,
151 'required_approval_ids':
152 required_approval_ids,
153 'labels':
154 non_masked_labels,
155 'initial_admins':
156 template_view.admin_names,
157 }
158
159 def ProcessFormData(self, mr, post_data):
160 """Validate and store the contents of the issues tracker admin page.
161
162 Args:
163 mr: commonly used info parsed from the request.
164 post_data: HTML form data from the request.
165
166 Returns:
167 String URL to redirect the user to, or None if response was already sent.
168 """
169
170 config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
171 parsed = template_helpers.ParseTemplateRequest(post_data, config)
172 field_helpers.ShiftEnumFieldsIntoLabels(
173 parsed.labels, [], parsed.field_val_strs, [], config)
174 template = self.services.template.GetTemplateByName(mr.cnxn,
175 parsed.name, mr.project_id)
176 allow_edit = permissions.CanEditTemplate(
177 mr.auth.effective_ids, mr.perms, mr.project, template)
178 if not allow_edit:
179 raise permissions.PermissionException(
180 'User is not allowed edit this issue template.')
181
182 if 'deletetemplate' in post_data:
183 self.services.template.DeleteIssueTemplateDef(
184 mr.cnxn, mr.project_id, template.template_id)
185 return framework_helpers.FormatAbsoluteURL(
186 mr, urls.ADMIN_TEMPLATES, deleted=1, ts=int(time.time()))
187
188 (admin_ids, owner_id, component_ids,
189 field_values, phases,
190 approvals) = template_helpers.GetTemplateInfoFromParsed(
191 mr, self.services, parsed, config)
192
193 labels = [label for label in parsed.labels if label]
194 field_helpers.AssertCustomFieldsEditPerms(
195 mr, config, field_values, [], [], labels, [])
196 field_helpers.ApplyRestrictedDefaultValues(
197 mr, config, field_values, labels, template.field_values,
198 template.labels)
199
200 if mr.errors.AnyErrors():
201 field_views = tracker_views.MakeAllFieldValueViews(
202 config, [], [], field_values, {})
203
204 prechecked_approvals = template_helpers.GetCheckedApprovalsFromParsed(
205 parsed.approvals_to_phase_idx)
206
207 self.PleaseCorrect(
208 mr,
209 initial_members_only=ezt.boolean(parsed.members_only),
210 template_name=parsed.name,
211 initial_summary=parsed.summary,
212 initial_must_edit_summary=ezt.boolean(parsed.summary_must_be_edited),
213 initial_content=parsed.content,
214 initial_status=parsed.status,
215 initial_owner=parsed.owner_str,
216 initial_owner_defaults_to_member=ezt.boolean(
217 parsed.owner_defaults_to_member),
218 initial_components=', '.join(parsed.component_paths),
219 initial_component_required=ezt.boolean(parsed.component_required),
220 initial_admins=parsed.admin_str,
221 labels=parsed.labels,
222 fields=[view for view in field_views
223 if view.field_def.type_name is not 'APPROVAL_TYPE'],
224 initial_add_approvals=ezt.boolean(parsed.add_approvals),
225 initial_phases=[tracker_pb2.Phase(name=name) for name in
226 parsed.phase_names],
227 approvals=[view for view in field_views
228 if view.field_def.type_name is 'APPROVAL_TYPE'],
229 prechecked_approvals=prechecked_approvals,
230 required_approval_ids=parsed.required_approval_ids
231 )
232 return
233
234 self.services.template.UpdateIssueTemplateDef(
235 mr.cnxn, mr.project_id, template.template_id, name=parsed.name,
236 content=parsed.content, summary=parsed.summary,
237 summary_must_be_edited=parsed.summary_must_be_edited,
238 status=parsed.status, members_only=parsed.members_only,
239 owner_defaults_to_member=parsed.owner_defaults_to_member,
240 component_required=parsed.component_required, owner_id=owner_id,
241 labels=labels, component_ids=component_ids, admin_ids=admin_ids,
242 field_values=field_values, phases=phases, approval_values=approvals)
243
244 return framework_helpers.FormatAbsoluteURL(
245 mr, urls.TEMPLATE_DETAIL, template=template.name,
246 saved=1, ts=int(time.time()))
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200247
248 # def GetTemplateDetail(self, **kwargs):
249 # return self.handler(**kwargs)
250
251 # def PostTemplateDetail(self, **kwargs):
252 # return self.handler(**kwargs)