Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/tracker/templatedetail.py b/tracker/templatedetail.py
new file mode 100644
index 0000000..cd48a80
--- /dev/null
+++ b/tracker/templatedetail.py
@@ -0,0 +1,245 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""A servlet for project owners to edit/delete a template"""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import collections
+import logging
+import time
+
+import ezt
+
+from framework import authdata
+from framework import framework_bizobj
+from framework import framework_helpers
+from framework import framework_views
+from framework import servlet
+from framework import urls
+from framework import permissions
+from tracker import field_helpers
+from tracker import template_helpers
+from tracker import tracker_bizobj
+from tracker import tracker_helpers
+from tracker import tracker_views
+from proto import tracker_pb2
+from services import user_svc
+
+
+class TemplateDetail(servlet.Servlet):
+ """Servlet allowing project owners to edit/delete an issue template"""
+
+ _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_PROCESS
+ _PAGE_TEMPLATE = 'tracker/template-detail-page.ezt'
+ _PROCESS_SUBTAB = servlet.Servlet.PROCESS_TAB_TEMPLATES
+
+ def AssertBasePermission(self, mr):
+ """Check whether the user has any permission to visit this page.
+
+ Args:
+ mr: commonly used info parsed from the request.
+ """
+ super(TemplateDetail, self).AssertBasePermission(mr)
+ template = self.services.template.GetTemplateByName(mr.cnxn,
+ mr.template_name, mr.project_id)
+
+ if template:
+ allow_view = permissions.CanViewTemplate(
+ mr.auth.effective_ids, mr.perms, mr.project, template)
+ if not allow_view:
+ raise permissions.PermissionException(
+ 'User is not allowed to view this issue template')
+ else:
+ self.abort(404, 'issue template not found %s' % mr.template_name)
+
+ def GatherPageData(self, mr):
+ """Build up a dictionary of data values to use when rendering the page.
+
+ Args:
+ mr: commonly used info parsed from the request.
+
+ Returns:
+ Dict of values used by EZT for rendering the page.
+ """
+
+ config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
+ template = self.services.template.GetTemplateByName(mr.cnxn,
+ mr.template_name, mr.project_id)
+ template_view = tracker_views.IssueTemplateView(
+ mr, template, self.services.user, config)
+ with mr.profiler.Phase('making user views'):
+ users_involved = tracker_bizobj.UsersInvolvedInTemplate(template)
+ users_by_id = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user, users_involved)
+ framework_views.RevealAllEmailsToMembers(
+ mr.cnxn, self.services, mr.auth, users_by_id, mr.project)
+ field_name_set = {fd.field_name.lower() for fd in config.field_defs
+ if fd.field_type is tracker_pb2.FieldTypes.ENUM_TYPE and
+ not fd.is_deleted}
+ non_masked_labels = tracker_bizobj.NonMaskedLabels(
+ template.labels, field_name_set)
+
+ field_views = tracker_views.MakeAllFieldValueViews(
+ config, template.labels, [], template.field_values, users_by_id,
+ phases=template.phases)
+ uneditable_fields = ezt.boolean(False)
+ for fv in field_views:
+ if permissions.CanEditValueForFieldDef(
+ mr.auth.effective_ids, mr.perms, mr.project, fv.field_def.field_def):
+ fv.is_editable = ezt.boolean(True)
+ else:
+ fv.is_editable = ezt.boolean(False)
+ uneditable_fields = ezt.boolean(True)
+
+ (prechecked_approvals, required_approval_ids,
+ initial_phases) = template_helpers.GatherApprovalsPageData(
+ template.approval_values, template.phases, config)
+
+ allow_edit = permissions.CanEditTemplate(
+ mr.auth.effective_ids, mr.perms, mr.project, template)
+
+ return {
+ 'admin_tab_mode':
+ self._PROCESS_SUBTAB,
+ 'allow_edit':
+ ezt.boolean(allow_edit),
+ 'uneditable_fields':
+ uneditable_fields,
+ 'new_template_form':
+ ezt.boolean(False),
+ 'initial_members_only':
+ template_view.members_only,
+ 'template_name':
+ template_view.name,
+ 'initial_summary':
+ template_view.summary,
+ 'initial_must_edit_summary':
+ template_view.summary_must_be_edited,
+ 'initial_content':
+ template_view.content,
+ 'initial_status':
+ template_view.status,
+ 'initial_owner':
+ template_view.ownername,
+ 'initial_owner_defaults_to_member':
+ template_view.owner_defaults_to_member,
+ 'initial_components':
+ template_view.components,
+ 'initial_component_required':
+ template_view.component_required,
+ 'fields':
+ [
+ view for view in field_views
+ if view.field_def.type_name is not 'APPROVAL_TYPE'
+ ],
+ 'initial_add_approvals':
+ ezt.boolean(prechecked_approvals),
+ 'initial_phases':
+ initial_phases,
+ 'approvals':
+ [
+ view for view in field_views
+ if view.field_def.type_name is 'APPROVAL_TYPE'
+ ],
+ 'prechecked_approvals':
+ prechecked_approvals,
+ 'required_approval_ids':
+ required_approval_ids,
+ 'labels':
+ non_masked_labels,
+ 'initial_admins':
+ template_view.admin_names,
+ }
+
+ def ProcessFormData(self, mr, post_data):
+ """Validate and store the contents of the issues tracker admin page.
+
+ Args:
+ mr: commonly used info parsed from the request.
+ post_data: HTML form data from the request.
+
+ Returns:
+ String URL to redirect the user to, or None if response was already sent.
+ """
+
+ config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
+ parsed = template_helpers.ParseTemplateRequest(post_data, config)
+ field_helpers.ShiftEnumFieldsIntoLabels(
+ parsed.labels, [], parsed.field_val_strs, [], config)
+ template = self.services.template.GetTemplateByName(mr.cnxn,
+ parsed.name, mr.project_id)
+ allow_edit = permissions.CanEditTemplate(
+ mr.auth.effective_ids, mr.perms, mr.project, template)
+ if not allow_edit:
+ raise permissions.PermissionException(
+ 'User is not allowed edit this issue template.')
+
+ if 'deletetemplate' in post_data:
+ self.services.template.DeleteIssueTemplateDef(
+ mr.cnxn, mr.project_id, template.template_id)
+ return framework_helpers.FormatAbsoluteURL(
+ mr, urls.ADMIN_TEMPLATES, deleted=1, ts=int(time.time()))
+
+ (admin_ids, owner_id, component_ids,
+ field_values, phases,
+ approvals) = template_helpers.GetTemplateInfoFromParsed(
+ mr, self.services, parsed, config)
+
+ labels = [label for label in parsed.labels if label]
+ field_helpers.AssertCustomFieldsEditPerms(
+ mr, config, field_values, [], [], labels, [])
+ field_helpers.ApplyRestrictedDefaultValues(
+ mr, config, field_values, labels, template.field_values,
+ template.labels)
+
+ if mr.errors.AnyErrors():
+ field_views = tracker_views.MakeAllFieldValueViews(
+ config, [], [], field_values, {})
+
+ prechecked_approvals = template_helpers.GetCheckedApprovalsFromParsed(
+ parsed.approvals_to_phase_idx)
+
+ self.PleaseCorrect(
+ mr,
+ initial_members_only=ezt.boolean(parsed.members_only),
+ template_name=parsed.name,
+ initial_summary=parsed.summary,
+ initial_must_edit_summary=ezt.boolean(parsed.summary_must_be_edited),
+ initial_content=parsed.content,
+ initial_status=parsed.status,
+ initial_owner=parsed.owner_str,
+ initial_owner_defaults_to_member=ezt.boolean(
+ parsed.owner_defaults_to_member),
+ initial_components=', '.join(parsed.component_paths),
+ initial_component_required=ezt.boolean(parsed.component_required),
+ initial_admins=parsed.admin_str,
+ labels=parsed.labels,
+ fields=[view for view in field_views
+ if view.field_def.type_name is not 'APPROVAL_TYPE'],
+ initial_add_approvals=ezt.boolean(parsed.add_approvals),
+ initial_phases=[tracker_pb2.Phase(name=name) for name in
+ parsed.phase_names],
+ approvals=[view for view in field_views
+ if view.field_def.type_name is 'APPROVAL_TYPE'],
+ prechecked_approvals=prechecked_approvals,
+ required_approval_ids=parsed.required_approval_ids
+ )
+ return
+
+ self.services.template.UpdateIssueTemplateDef(
+ mr.cnxn, mr.project_id, template.template_id, name=parsed.name,
+ content=parsed.content, summary=parsed.summary,
+ summary_must_be_edited=parsed.summary_must_be_edited,
+ status=parsed.status, members_only=parsed.members_only,
+ owner_defaults_to_member=parsed.owner_defaults_to_member,
+ component_required=parsed.component_required, owner_id=owner_id,
+ labels=labels, component_ids=component_ids, admin_ids=admin_ids,
+ field_values=field_values, phases=phases, approval_values=approvals)
+
+ return framework_helpers.FormatAbsoluteURL(
+ mr, urls.TEMPLATE_DETAIL, template=template.name,
+ saved=1, ts=int(time.time()))