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()))