Example #1
0
def _ParseChoicesIntoWellKnownLabels(
    choices_text, field_name, config, field_type_str):
  """Parse a field's possible choices and integrate them into the config.

  Args:
    choices_text: string with one label and optional docstring per line.
    field_name: string name of the field definition being edited.
    config: ProjectIssueConfig PB of the current project.
    field_type_str: string name of the new field's type. None if an existing
      field is being updated

  Returns:
    A revised list of labels that can be used to update the config.
  """
  fd = tracker_bizobj.FindFieldDef(field_name, config)
  matches = framework_constants.IDENTIFIER_DOCSTRING_RE.findall(choices_text)
  maskingFieldNames = []
  # wkls should only be masked by the field if it is an enum_type.
  if (field_type_str == 'enum_type') or (
      fd and fd.field_type is tracker_pb2.FieldTypes.ENUM_TYPE):
    maskingFieldNames.append(field_name.lower())

  new_labels = [
      ('%s-%s' % (field_name, label), choice_docstring.strip(), False)
      for label, choice_docstring in matches]
  kept_labels = [
      (wkl.label, wkl.label_docstring, wkl.deprecated)
      for wkl in config.well_known_labels
      if not tracker_bizobj.LabelIsMaskedByField(
          wkl.label, maskingFieldNames)]
  revised_labels = kept_labels + new_labels
  return revised_labels
 def testLabelIsMaskedByField(self):
   self.assertIsNone(tracker_bizobj.LabelIsMaskedByField('UI', []))
   self.assertIsNone(tracker_bizobj.LabelIsMaskedByField('P-1', []))
   field_names = ['priority', 'size']
   self.assertIsNone(tracker_bizobj.LabelIsMaskedByField(
       'UI', field_names))
   self.assertIsNone(tracker_bizobj.LabelIsMaskedByField(
       'OS-All', field_names))
   self.assertEqual(
       'size', tracker_bizobj.LabelIsMaskedByField('size-xl', field_names))
   self.assertEqual(
       'size', tracker_bizobj.LabelIsMaskedByField('Size-XL', field_names))
Example #3
0
def _ParseChoicesIntoWellKnownLabels(choices_text, field_name, config):
    """Parse a field's possible choices and integrate them into the config.

  Args:
    choices_text: string with one label and optional docstring per line.
    field_name: string name of the field definition being edited.
    config: ProjectIssueConfig PB of the current project.

  Returns:
    A revised list of labels that can be used to update the config.
  """
    matches = framework_constants.IDENTIFIER_DOCSTRING_RE.findall(choices_text)
    new_labels = [('%s-%s' % (field_name, label), choice_docstring.strip(),
                   False) for label, choice_docstring in matches]
    kept_labels = [(wkl.label, wkl.label_docstring, False)
                   for wkl in config.well_known_labels
                   if not tracker_bizobj.LabelIsMaskedByField(
                       wkl.label, [field_name.lower()])]
    revised_labels = kept_labels + new_labels
    return revised_labels
Example #4
0
def _LabelsMaskedOrNot(config, field_names, invert=False, trim_prefix=False):
    """Return EZTItems for labels that'd be masked. Or not, when invert=True."""
    field_names = [fn.lower() for fn in field_names]
    result = []
    for wkl in config.well_known_labels:
        masked_by = tracker_bizobj.LabelIsMaskedByField(wkl.label, field_names)
        if (masked_by and not invert) or (not masked_by and invert):
            display_name = wkl.label
            if trim_prefix:
                display_name = display_name[len(masked_by) + 1:]
            result.append(
                template_helpers.EZTItem(
                    name=display_name,
                    name_padded=display_name.ljust(20),
                    commented='#' if wkl.deprecated else '',
                    docstring=wkl.label_docstring,
                    docstring_short=template_helpers.FitUnsafeText(
                        wkl.label_docstring, 40),
                    idx=len(result)))

    return result
Example #5
0
    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.
    """
        with mr.profiler.Phase('getting config'):
            config = self.services.config.GetProjectConfig(
                mr.cnxn, mr.project_id)

        # In addition to checking perms, we adjust some default field values for
        # project members.
        is_member = framework_bizobj.UserIsInProject(mr.project,
                                                     mr.auth.effective_ids)
        page_perms = self.MakePagePerms(
            mr, None, permissions.CREATE_ISSUE, permissions.SET_STAR,
            permissions.EDIT_ISSUE, permissions.EDIT_ISSUE_SUMMARY,
            permissions.EDIT_ISSUE_STATUS, permissions.EDIT_ISSUE_OWNER,
            permissions.EDIT_ISSUE_CC)

        with work_env.WorkEnv(mr, self.services) as we:
            userprefs = we.GetUserPrefs(mr.auth.user_id)
            code_font = any(
                pref for pref in userprefs.prefs
                if pref.name == 'code_font' and pref.value == 'true')

        template = self._GetTemplate(mr.cnxn, config, mr.template_name,
                                     is_member)

        if template.summary:
            initial_summary = template.summary
            initial_summary_must_be_edited = template.summary_must_be_edited
        else:
            initial_summary = PLACEHOLDER_SUMMARY
            initial_summary_must_be_edited = True

        if template.status:
            initial_status = template.status
        elif is_member:
            initial_status = 'Accepted'
        else:
            initial_status = 'New'  # not offering meta, only used in hidden field.

        component_paths = []
        for component_id in template.component_ids:
            component_paths.append(
                tracker_bizobj.FindComponentDefByID(component_id, config).path)
        initial_components = ', '.join(component_paths)

        if template.owner_id:
            initial_owner = framework_views.MakeUserView(
                mr.cnxn, self.services.user, template.owner_id)
        elif template.owner_defaults_to_member and page_perms.EditIssue:
            initial_owner = mr.auth.user_view
        else:
            initial_owner = None

        if initial_owner:
            initial_owner_name = initial_owner.email
            owner_avail_state = initial_owner.avail_state
            owner_avail_message_short = initial_owner.avail_message_short
        else:
            initial_owner_name = ''
            owner_avail_state = None
            owner_avail_message_short = None

        # Check whether to allow attachments from the entry page
        allow_attachments = tracker_helpers.IsUnderSoftAttachmentQuota(
            mr.project)

        config_view = tracker_views.ConfigView(mr, self.services, config,
                                               template)
        # If the user followed a link that specified the template name, make sure
        # that it is also in the menu as the current choice.
        # TODO(jeffcarp): Unit test this.
        config_view.template_view.can_view = ezt.boolean(True)

        # TODO(jeffcarp): Unit test this.
        offer_templates = len(config_view.template_names) > 1
        restrict_to_known = config.restrict_to_known
        enum_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
        }  # TODO(jrobbins): restrictions
        link_or_template_labels = mr.GetListParam('labels', template.labels)
        labels = [
            lab for lab in link_or_template_labels if
            not tracker_bizobj.LabelIsMaskedByField(lab, enum_field_name_set)
        ]

        # Corp-mode users automatically add R-V-G.
        with work_env.WorkEnv(mr, self.services) as we:
            userprefs = we.GetUserPrefs(mr.auth.user_id)
            corp_mode = any(
                up.name == 'restrict_new_issues' and up.value == 'true'
                for up in userprefs.prefs)
            if corp_mode:
                if not any(lab.lower().startswith('restrict-view-')
                           for lab in labels):
                    labels.append(CORP_RESTRICTION_LABEL)

        field_user_views = tracker_views.MakeFieldUserViews(
            mr.cnxn, template, self.services.user)
        approval_ids = [av.approval_id for av in template.approval_values]
        field_views = tracker_views.MakeAllFieldValueViews(
            config,
            link_or_template_labels, [],
            template.field_values,
            field_user_views,
            parent_approval_ids=approval_ids,
            phases=template.phases)
        # TODO(jojwang): monorail:6305, remove this hack when Edit perms for field
        # values are implemented.
        field_views = [
            view for view in field_views
            if view.field_name.lower() not in RESTRICTED_FLT_FIELDS
        ]

        # TODO(jrobbins): remove "or []" after next release.
        (prechecked_approvals, required_approval_ids,
         phases) = issue_tmpl_helpers.GatherApprovalsPageData(
             template.approval_values or [], template.phases, config)
        approvals = [
            view for view in field_views if view.field_id in approval_ids
        ]

        page_data = {
            'issue_tab_mode':
            'issueEntry',
            'initial_summary':
            initial_summary,
            'template_summary':
            initial_summary,
            'clear_summary_on_click':
            ezt.boolean(initial_summary_must_be_edited
                        and 'initial_summary' not in mr.form_overrides),
            'must_edit_summary':
            ezt.boolean(initial_summary_must_be_edited),
            'initial_description':
            template.content,
            'template_name':
            template.name,
            'component_required':
            ezt.boolean(template.component_required),
            'initial_status':
            initial_status,
            'initial_owner':
            initial_owner_name,
            'owner_avail_state':
            owner_avail_state,
            'owner_avail_message_short':
            owner_avail_message_short,
            'initial_components':
            initial_components,
            'initial_cc':
            '',
            'initial_blocked_on':
            '',
            'initial_blocking':
            '',
            'initial_hotlists':
            '',
            'labels':
            labels,
            'fields':
            field_views,
            'any_errors':
            ezt.boolean(mr.errors.AnyErrors()),
            'page_perms':
            page_perms,
            'allow_attachments':
            ezt.boolean(allow_attachments),
            'max_attach_size':
            template_helpers.BytesKbOrMb(
                framework_constants.MAX_POST_BODY_SIZE),
            'offer_templates':
            ezt.boolean(offer_templates),
            'config':
            config_view,
            'restrict_to_known':
            ezt.boolean(restrict_to_known),
            'is_member':
            ezt.boolean(is_member),
            'code_font':
            ezt.boolean(code_font),
            # The following are necessary for displaying phases that come with
            # this template. These are read-only.
            'allow_edit':
            ezt.boolean(False),
            'initial_phases':
            phases,
            'approvals':
            approvals,
            'prechecked_approvals':
            prechecked_approvals,
            'required_approval_ids':
            required_approval_ids,
            # See monorail:4692 and the use of PHASES_WITH_MILESTONES
            # in elements/flt/mr-launch-overview/mr-phase.js
            'issue_phase_names':
            list({
                phase.name.lower()
                for phase in phases if phase.name in PHASES_WITH_MILESTONES
            }),
        }

        return page_data
Example #6
0
    def __init__(self, mr, template, user_service, config):
        super(IssueTemplateView, self).__init__(template)

        self.ownername = ''
        try:
            self.owner_view = framework_views.MakeUserView(
                mr.cnxn, user_service, template.owner_id)
        except exceptions.NoSuchUserException:
            self.owner_view = None
        if self.owner_view:
            self.ownername = self.owner_view.email

        self.admin_views = list(
            framework_views.MakeAllUserViews(mr.cnxn, user_service,
                                             template.admin_ids).values())
        self.admin_names = ', '.join(
            sorted([admin_view.email for admin_view in self.admin_views]))

        self.summary_must_be_edited = ezt.boolean(
            template.summary_must_be_edited)
        self.members_only = ezt.boolean(template.members_only)
        self.owner_defaults_to_member = ezt.boolean(
            template.owner_defaults_to_member)
        self.component_required = ezt.boolean(template.component_required)

        component_paths = []
        for component_id in template.component_ids:
            component_paths.append(
                tracker_bizobj.FindComponentDefByID(component_id, config).path)
        self.components = ', '.join(component_paths)

        self.can_view = ezt.boolean(
            permissions.CanViewTemplate(mr.auth.effective_ids, mr.perms,
                                        mr.project, template))
        self.can_edit = ezt.boolean(
            permissions.CanEditTemplate(mr.auth.effective_ids, mr.perms,
                                        mr.project, template))

        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
        }  # TODO(jrobbins): restrictions
        non_masked_labels = [
            lab for lab in template.labels
            if not tracker_bizobj.LabelIsMaskedByField(lab, field_name_set)
        ]

        for i, label in enumerate(non_masked_labels):
            setattr(self, 'label%d' % i, label)
        for i in range(len(non_masked_labels), framework_constants.MAX_LABELS):
            setattr(self, 'label%d' % i, '')

        field_user_views = MakeFieldUserViews(mr.cnxn, template, user_service)

        self.field_values = []
        for fv in template.field_values:
            self.field_values.append(
                template_helpers.EZTItem(field_id=fv.field_id,
                                         val=tracker_bizobj.GetFieldValue(
                                             fv, field_user_views),
                                         idx=len(self.field_values)))

        self.complete_field_values = MakeAllFieldValueViews(
            config, template.labels, [], template.field_values,
            field_user_views)

        # Templates only display and edit the first value of multi-valued fields, so
        # expose a single value, if any.
        # TODO(jrobbins): Fully support multi-valued fields in templates.
        for idx, field_value_view in enumerate(self.complete_field_values):
            field_value_view.idx = idx
            if field_value_view.values:
                field_value_view.val = field_value_view.values[0].val
            else:
                field_value_view.val = None
Example #7
0
    def ProcessSubtabForm(self, post_data, mr):
        """Process changes to labels and custom field definitions.

    Args:
      post_data: HTML form data for the HTTP request being processed.
      mr: commonly used info parsed from the request.

    Returns:
      The URL of the page to show after processing.
    """
        if not self.CheckPerm(mr, permissions.EDIT_PROJECT):
            raise permissions.PermissionException(
                'Only project owners may edit the label definitions')

        wkl_text = post_data.get('predefinedlabels', '')
        wkl_matches = framework_constants.IDENTIFIER_DOCSTRING_RE.findall(
            wkl_text)
        wkl_tuples = [(label.lstrip('#'), docstring.strip(),
                       label.startswith('#'))
                      for label, docstring in wkl_matches]
        if not wkl_tuples:
            mr.errors.label_defs = 'A project cannot have zero labels'
        label_counter = collections.Counter(wkl[0].lower()
                                            for wkl in wkl_tuples)
        for lab, count in label_counter.items():
            if count > 1:
                mr.errors.label_defs = 'Duplicate label: %s' % lab

        config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
        field_names = [
            fd.field_name for fd in config.field_defs
            if fd.field_type is tracker_pb2.FieldTypes.ENUM_TYPE
            and not fd.is_deleted
        ]
        masked_labels = tracker_helpers.LabelsMaskedByFields(
            config, field_names)
        field_names_lower = [field_name.lower() for field_name in field_names]
        for wkl in wkl_tuples:
            conflict = tracker_bizobj.LabelIsMaskedByField(
                wkl[0], field_names_lower)
            if conflict:
                mr.errors.label_defs = (
                    'Label "%s" should be defined in enum "%s"' %
                    (wkl[0], conflict))
        wkl_tuples.extend([(masked.name, masked.docstring, False)
                           for masked in masked_labels])

        excl_prefix_text = post_data.get('excl_prefixes', '')
        excl_prefixes = framework_constants.IDENTIFIER_RE.findall(
            excl_prefix_text)

        if mr.errors.AnyErrors():
            self.PleaseCorrect(mr, labels_text=wkl_text)
            return

        self.services.config.UpdateConfig(mr.cnxn,
                                          mr.project,
                                          well_known_labels=wkl_tuples,
                                          excl_label_prefixes=excl_prefixes)

        # TODO(jrobbins): define a "strict" mode that affects only labels.

        return urls.ADMIN_LABELS
Example #8
0
  def __init__(
      self, issue, users_by_id, config, open_related=None,
      closed_related=None, all_related=None):
    """Store relevant values for later display by EZT.

    Args:
      issue: An Issue protocol buffer.
      users_by_id: dict {user_id: UserViews} for all users mentioned in issue.
      config: ProjectIssueConfig for this issue.
      open_related: dict of visible open issues that are related to this issue.
      closed_related: dict {issue_id: issue} of visible closed issues that
          are related to this issue.
      all_related: dict {issue_id: issue} of all blocked-on, blocking,
          or merged-into issues referenced from this issue, regardless of
          perms.
    """
    super(IssueView, self).__init__(issue)

    # The users involved in this issue must be present in users_by_id if
    # this IssueView is to be used on the issue detail or peek pages. But,
    # they can be absent from users_by_id if the IssueView is used as a
    # tile in the grid view.
    self.owner = users_by_id.get(issue.owner_id)
    self.derived_owner = users_by_id.get(issue.derived_owner_id)
    self.cc = [users_by_id.get(cc_id) for cc_id in issue.cc_ids
               if cc_id]
    self.derived_cc = [users_by_id.get(cc_id)
                       for cc_id in issue.derived_cc_ids
                       if cc_id]
    self.status = framework_views.StatusView(issue.status, config)
    self.derived_status = framework_views.StatusView(
        issue.derived_status, config)
    # If we don't have a config available, we don't need to access is_open, so
    # let it be True.
    self.is_open = ezt.boolean(
        not config or
        tracker_helpers.MeansOpenInProject(
            tracker_bizobj.GetStatus(issue), config))

    self.components = sorted(
        [ComponentValueView(component_id, config, False)
         for component_id in issue.component_ids
         if tracker_bizobj.FindComponentDefByID(component_id, config)] +
        [ComponentValueView(component_id, config, True)
         for component_id in issue.derived_component_ids
         if tracker_bizobj.FindComponentDefByID(component_id, config)],
        key=lambda cvv: cvv.path)

    self.fields = [
        MakeFieldValueView(
            fd, config, issue.labels, issue.derived_labels, issue.field_values,
            users_by_id)
        # TODO(jrobbins): field-level view restrictions, display options
        for fd in config.field_defs
        if not fd.is_deleted]
    self.fields = sorted(
        self.fields, key=lambda f: (f.applicable_type, f.field_name))

    field_names = [fd.field_name.lower() for fd in config.field_defs
                   if not fd.is_deleted]  # TODO(jrobbins): restricts
    self.labels = [
        framework_views.LabelView(label, config)
        for label in tracker_bizobj.NonMaskedLabels(issue.labels, field_names)]
    self.derived_labels = [
        framework_views.LabelView(label, config)
        for label in issue.derived_labels
        if not tracker_bizobj.LabelIsMaskedByField(label, field_names)]
    self.restrictions = _RestrictionsView(issue)

    # TODO(jrobbins): sort by order of labels in project config

    self.short_summary = issue.summary[:tracker_constants.SHORT_SUMMARY_LENGTH]

    if issue.closed_timestamp:
      self.closed = timestr.FormatAbsoluteDate(issue.closed_timestamp)
    else:
      self.closed = ''

    blocked_on_iids = issue.blocked_on_iids
    blocking_iids = issue.blocking_iids

    # Note that merged_into_str and blocked_on_str includes all issue
    # references, even those referring to issues that the user can't view,
    # so open_related and closed_related cannot be used.
    if all_related is not None:
      all_blocked_on_refs = [
          (all_related[ref_iid].project_name, all_related[ref_iid].local_id)
          for ref_iid in issue.blocked_on_iids]
      all_blocked_on_refs.extend([
          (r.project, r.issue_id) for r in issue.dangling_blocked_on_refs])
      self.blocked_on_str = ', '.join(
          tracker_bizobj.FormatIssueRef(
              ref, default_project_name=issue.project_name)
          for ref in all_blocked_on_refs)
      all_blocking_refs = [
          (all_related[ref_iid].project_name, all_related[ref_iid].local_id)
          for ref_iid in issue.blocking_iids]
      all_blocking_refs.extend([
          (r.project, r.issue_id) for r in issue.dangling_blocking_refs])
      self.blocking_str = ', '.join(
          tracker_bizobj.FormatIssueRef(
              ref, default_project_name=issue.project_name)
          for ref in all_blocking_refs)
      if issue.merged_into:
        merged_issue = all_related[issue.merged_into]
        merged_into_ref = merged_issue.project_name, merged_issue.local_id
      else:
        merged_into_ref = None
      self.merged_into_str = tracker_bizobj.FormatIssueRef(
          merged_into_ref, default_project_name=issue.project_name)

    self.blocked_on = []
    self.has_dangling = ezt.boolean(self.dangling_blocked_on_refs)
    self.blocking = []
    current_project_name = issue.project_name

    if open_related is not None and closed_related is not None:
      self.merged_into = IssueRefView(
          current_project_name, issue.merged_into,
          open_related, closed_related)

      self.blocked_on = [
          IssueRefView(current_project_name, iid, open_related, closed_related)
          for iid in blocked_on_iids]
      self.blocked_on.extend(
          [DanglingIssueRefView(ref.project, ref.issue_id)
           for ref in issue.dangling_blocked_on_refs])
      self.blocked_on = [irv for irv in self.blocked_on if irv.visible]
      # TODO(jrobbins): sort by irv project_name and local_id

      self.blocking = [
          IssueRefView(current_project_name, iid, open_related, closed_related)
          for iid in blocking_iids]
      self.blocking.extend(
          [DanglingIssueRefView(ref.project, ref.issue_id)
           for ref in issue.dangling_blocking_refs])
      self.blocking = [irv for irv in self.blocking if irv.visible]
      # TODO(jrobbins): sort by irv project_name and local_id

    self.multiple_blocked_on = ezt.boolean(len(self.blocked_on) >= 2)
    self.detail_relative_url = tracker_helpers.FormatRelativeIssueURL(
        issue.project_name, urls.ISSUE_DETAIL, id=issue.local_id)
Example #9
0
  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.
    """
    with self.profiler.Phase('getting config'):
      config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)

    # In addition to checking perms, we adjust some default field values for
    # project members.
    is_member = framework_bizobj.UserIsInProject(
        mr.project, mr.auth.effective_ids)
    page_perms = self.MakePagePerms(
        mr, None,
        permissions.CREATE_ISSUE,
        permissions.SET_STAR,
        permissions.EDIT_ISSUE,
        permissions.EDIT_ISSUE_SUMMARY,
        permissions.EDIT_ISSUE_STATUS,
        permissions.EDIT_ISSUE_OWNER,
        permissions.EDIT_ISSUE_CC)

    wkp = _SelectTemplate(mr.template_name, config, is_member)

    if wkp.summary:
      initial_summary = wkp.summary
      initial_summary_must_be_edited = wkp.summary_must_be_edited
    else:
      initial_summary = PLACEHOLDER_SUMMARY
      initial_summary_must_be_edited = True

    if wkp.status:
      initial_status = wkp.status
    elif is_member:
      initial_status = 'Accepted'
    else:
      initial_status = 'New'  # not offering meta, only used in hidden field.

    component_paths = []
    for component_id in wkp.component_ids:
      component_paths.append(
          tracker_bizobj.FindComponentDefByID(component_id, config).path)
    initial_components = ', '.join(component_paths)

    if wkp.owner_id:
      initial_owner = framework_views.MakeUserView(
          mr.cnxn, self.services.user, wkp.owner_id)
    elif wkp.owner_defaults_to_member and page_perms.EditIssue:
      initial_owner = mr.auth.user_view
    else:
      initial_owner = None

    if initial_owner:
      initial_owner_name = initial_owner.email
      owner_avail_state = initial_owner.avail_state
      owner_avail_message_short = initial_owner.avail_message_short
    else:
      initial_owner_name = ''
      owner_avail_state = None
      owner_avail_message_short = None

    # Check whether to allow attachments from the entry page
    allow_attachments = tracker_helpers.IsUnderSoftAttachmentQuota(mr.project)

    config_view = tracker_views.ConfigView(mr, self.services, config)
    # If the user followed a link that specified the template name, make sure
    # that it is also in the menu as the current choice.
    for template_view in config_view.templates:
      if template_view.name == mr.template_name:
        template_view.can_view = ezt.boolean(True)

    offer_templates = len(list(
        tmpl for tmpl in config_view.templates if tmpl.can_view)) > 1
    restrict_to_known = config.restrict_to_known
    field_name_set = {fd.field_name.lower() for fd in config.field_defs
                      if not fd.is_deleted}  # TODO(jrobbins): restrictions
    link_or_template_labels = mr.GetListParam('labels', wkp.labels)
    labels = [lab for lab in link_or_template_labels
              if not tracker_bizobj.LabelIsMaskedByField(lab, field_name_set)]

    field_user_views = tracker_views.MakeFieldUserViews(
        mr.cnxn, wkp, self.services.user)
    field_views = [
        tracker_views.MakeFieldValueView(
            fd, config, link_or_template_labels, [], wkp.field_values,
            field_user_views)
        # TODO(jrobbins): field-level view restrictions, display options
        for fd in config.field_defs
        if not fd.is_deleted]

    page_data = {
        'issue_tab_mode': 'issueEntry',
        'initial_summary': initial_summary,
        'template_summary': initial_summary,
        'clear_summary_on_click': ezt.boolean(
            initial_summary_must_be_edited and
            'initial_summary' not in mr.form_overrides),
        'must_edit_summary': ezt.boolean(initial_summary_must_be_edited),

        'initial_description': wkp.content,
        'template_name': wkp.name,
        'component_required': ezt.boolean(wkp.component_required),
        'initial_status': initial_status,
        'initial_owner': initial_owner_name,
        'owner_avail_state': owner_avail_state,
        'owner_avail_message_short': owner_avail_message_short,
        'initial_components': initial_components,
        'initial_cc': '',
        'initial_blocked_on': '',
        'initial_blocking': '',
        'labels': labels,
        'fields': field_views,

        'any_errors': ezt.boolean(mr.errors.AnyErrors()),
        'page_perms': page_perms,
        'allow_attachments': ezt.boolean(allow_attachments),
        'max_attach_size': template_helpers.BytesKbOrMb(
            framework_constants.MAX_POST_BODY_SIZE),

        'offer_templates': ezt.boolean(offer_templates),
        'config': config_view,

        'restrict_to_known': ezt.boolean(restrict_to_known),
        }

    return page_data