def shutdown(self):
        # We have to put the TestingDone field back before we shut down
        # in order to get the instance back to its original state.
        main_fieldset = get_review_request_fieldset('main')
        if not get_review_request_field('testing_done'):
            main_fieldset.add_field(TestingDoneField)

        info_fieldset = get_review_request_fieldset('info')
        if not get_review_request_field('branch'):
            info_fieldset.add_field(BranchField)
        if not get_review_request_field('depends_on'):
            info_fieldset.add_field(DependsOnField)
        if not get_review_request_field('blocks'):
            info_fieldset.add_field(BlocksField)

        super(MozReviewExtension, self).shutdown()
Esempio n. 2
0
    def reopen(self, user=None):
        """Reopens the review request for review."""
        from reviewboard.reviews.models.review_request_draft import \
            ReviewRequestDraft

        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        if self.status != self.PENDING_REVIEW:
            changedesc = ChangeDescription()
            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status,
                                             self.PENDING_REVIEW)

            if self.status == self.DISCARDED:
                # A draft is needed if reopening a discarded review request.
                self.public = False
                changedesc.save()
                draft = ReviewRequestDraft.create(self)
                draft.changedesc = changedesc
                draft.save()
            else:
                changedesc.public = True
                changedesc.save()
                self.changedescs.add(changedesc)

            self.status = self.PENDING_REVIEW
            self.save(update_counts=True)

        review_request_reopened.send(sender=self.__class__, user=user,
                                     review_request=self)
Esempio n. 3
0
 def shutdown(self):
     # We have to put the TestingDone field back before we shut down
     # in order to get the instance back to its original state.
     fieldset = get_review_request_fieldset('main')
     field = get_review_request_field('testing_done')
     if not field:
       fieldset.add_field(TestingDoneField)
Esempio n. 4
0
    def reopen(self, user=None):
        """Reopens the review request for review."""
        from reviewboard.reviews.models.review_request_draft import \
            ReviewRequestDraft

        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        if self.status != self.PENDING_REVIEW:
            changedesc = ChangeDescription()
            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status,
                                             self.PENDING_REVIEW)

            if self.status == self.DISCARDED:
                # A draft is needed if reopening a discarded review request.
                self.public = False
                changedesc.save()
                draft = ReviewRequestDraft.create(self)
                draft.changedesc = changedesc
                draft.save()
            else:
                changedesc.public = True
                changedesc.save()
                self.changedescs.add(changedesc)

            self.status = self.PENDING_REVIEW
            self.save(update_counts=True)

        review_request_reopened.send(sender=self.__class__, user=user,
                                     review_request=self)
Esempio n. 5
0
    def shutdown(self):
        # We have to put the TestingDone field back before we shut down
        # in order to get the instance back to its original state.
        main_fieldset = get_review_request_fieldset('main')
        testing_done_field = get_review_request_field('testing_done')
        if not testing_done_field:
            main_fieldset.add_field(TestingDoneField)

        super(MozReviewExtension, self).shutdown()
Esempio n. 6
0
    def close(self, type, user=None, description=None, rich_text=False):
        """Closes the review request.

        The type must be one of SUBMITTED or DISCARDED.
        """
        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        if type not in [self.SUBMITTED, self.DISCARDED]:
            raise AttributeError("%s is not a valid close type" % type)

        draft = get_object_or_none(self.draft)

        if self.status != type:
            if (draft is not None and
                not self.public and type == self.DISCARDED):
                # Copy over the draft information if this is a private discard.
                draft.copy_fields_to_request(self)

            # TODO: Use the user's default for rich_text.
            changedesc = ChangeDescription(public=True,
                                           text=description or "",
                                           rich_text=rich_text or False)

            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status, type)
            changedesc.save()

            self.changedescs.add(changedesc)

            if type == self.SUBMITTED:
                if not self.public:
                    raise PublishError("The draft must be public first.")
            else:
                self.commit_id = None

            self.status = type
            self.save(update_counts=True)

            review_request_closed.send(sender=self.__class__, user=user,
                                       review_request=self,
                                       type=type)
        else:
            # Update submission description.
            changedesc = self.changedescs.filter(public=True).latest()
            changedesc.timestamp = timezone.now()
            changedesc.text = description or ""
            changedesc.rich_text = rich_text
            changedesc.save()

            # Needed to renew last-update.
            self.save()

        # Delete the associated draft review request.
        if draft is not None:
            draft.delete()
Esempio n. 7
0
    def close(self, type, user=None, description=None, rich_text=False):
        """Closes the review request.

        The type must be one of SUBMITTED or DISCARDED.
        """
        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        if type not in [self.SUBMITTED, self.DISCARDED]:
            raise AttributeError("%s is not a valid close type" % type)

        draft = get_object_or_none(self.draft)

        if self.status != type:
            if (draft is not None and
                not self.public and type == self.DISCARDED):
                # Copy over the draft information if this is a private discard.
                draft.copy_fields_to_request(self)

            # TODO: Use the user's default for rich_text.
            changedesc = ChangeDescription(public=True,
                                           text=description or "",
                                           rich_text=rich_text or False)

            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status, type)
            changedesc.save()

            self.changedescs.add(changedesc)

            if type == self.SUBMITTED:
                if not self.public:
                    raise PublishError("The draft must be public first.")
            else:
                self.commit_id = None

            self.status = type
            self.save(update_counts=True)

            review_request_closed.send(sender=self.__class__, user=user,
                                       review_request=self,
                                       type=type)
        else:
            # Update submission description.
            changedesc = self.changedescs.filter(public=True).latest()
            changedesc.timestamp = timezone.now()
            changedesc.text = description or ""
            changedesc.rich_text = rich_text
            changedesc.save()

            # Needed to renew last-update.
            self.save()

        # Delete the associated draft review request.
        if draft is not None:
            draft.delete()
    def shutdown(self):
        # Restore the built-in opcode generator.
        set_diff_opcode_generator_class(self.original_opcode_generator)

        # We have to put the TestingDone field back before we shut down
        # in order to get the instance back to its original state.
        main_fieldset = get_review_request_fieldset('main')
        if not get_review_request_field('testing_done'):
            main_fieldset.add_field(TestingDoneField)

        info_fieldset = get_review_request_fieldset('info')
        if not get_review_request_field('branch'):
            info_fieldset.add_field(BranchField)
        if not get_review_request_field('depends_on'):
            info_fieldset.add_field(DependsOnField)
        if not get_review_request_field('blocks'):
            info_fieldset.add_field(BlocksField)

        super(MozReviewExtension, self).shutdown()
Esempio n. 9
0
    def serialize_fields_changed_field(self, obj, **kwargs):
        review_request = obj.review_request.get()
        fields_changed = {}

        for field_name, data in six.iteritems(obj.fields_changed):
            field_cls = get_review_request_field(field_name)
            field = field_cls(review_request)

            fields_changed[field.field_id] = field.serialize_change_entry(obj)

        return fields_changed
Esempio n. 10
0
    def serialize_fields_changed_field(self, obj, **kwargs):
        review_request = obj.review_request.get()
        fields_changed = {}

        for field_name, data in six.iteritems(obj.fields_changed):
            field_cls = get_review_request_field(field_name)
            field = field_cls(review_request)

            fields_changed[field.field_id] = field.serialize_change_entry(obj)

        return fields_changed
Esempio n. 11
0
 def initialize(self):
     # Start by hiding the Testing Done field in all review requests, since
     # Mozilla developers will not be using it.
     fieldset = get_review_request_fieldset('main')
     field = get_review_request_field('testing_done')
     if (field):
       fieldset.remove_field(field)
     # All of our review request styling is injected via review-stylings-css,
     # which in turn loads the review.css static bundle.
     TemplateHook(self, 'base-css', 'rbmozui/review-stylings-css.html',
                  apply_to=review_request_url_names)
     TemplateHook(self, 'base-css', 'rbmozui/viewdiff-stylings-css.html',
                  apply_to=diffviewer_url_names)
     TemplateHook(self, 'base-before-content', 'rbmozui/review-header.html',
                  apply_to=review_request_url_names)
     TemplateHook(self, 'base-scripts-post', 'rbmozui/review-header-workaround.html',
                  apply_to=review_request_url_names)
Esempio n. 12
0
    def reopen(self, user=None):
        """Reopens the review request for review."""
        from reviewboard.reviews.models.review_request_draft import \
            ReviewRequestDraft

        if (user and not self.is_mutable_by(user) and not user.has_perm(
                "reviews.can_change_status", self.local_site)):
            raise PermissionError

        old_status = self.status
        old_public = self.public

        if old_status != self.PENDING_REVIEW:
            # The reopening signal is only fired when actually making a status
            # change since the main consumers (extensions) probably only care
            # about changes.
            review_request_reopening.send(sender=self.__class__,
                                          user=user,
                                          review_request=self)

            changedesc = ChangeDescription(user=user or self.submitter)
            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, old_status,
                                             self.PENDING_REVIEW)

            if old_status == self.DISCARDED:
                # A draft is needed if reopening a discarded review request.
                self.public = False
                changedesc.save()
                draft = ReviewRequestDraft.create(self)
                draft.changedesc = changedesc
                draft.save()
            else:
                changedesc.public = True
                changedesc.save()
                self.changedescs.add(changedesc)

            self.status = self.PENDING_REVIEW
            self.save(update_counts=True)

        review_request_reopened.send(sender=self.__class__,
                                     user=user,
                                     review_request=self,
                                     old_status=old_status,
                                     old_public=old_public)
Esempio n. 13
0
    def reopen(self, user=None):
        """Reopens the review request for review."""
        from reviewboard.reviews.models.review_request_draft import \
            ReviewRequestDraft

        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        old_status = self.status
        old_public = self.public

        if old_status != self.PENDING_REVIEW:
            # The reopening signal is only fired when actually making a status
            # change since the main consumers (extensions) probably only care
            # about changes.
            review_request_reopening.send(sender=self.__class__,
                                          user=user,
                                          review_request=self)

            changedesc = ChangeDescription(user=user or self.submitter)
            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, old_status,
                                             self.PENDING_REVIEW)

            if old_status == self.DISCARDED:
                # A draft is needed if reopening a discarded review request.
                self.public = False
                changedesc.save()
                draft = ReviewRequestDraft.create(self)
                draft.changedesc = changedesc
                draft.save()
            else:
                changedesc.public = True
                changedesc.save()
                self.changedescs.add(changedesc)

            self.status = self.PENDING_REVIEW
            self.save(update_counts=True)

        review_request_reopened.send(sender=self.__class__, user=user,
                                     review_request=self,
                                     old_status=old_status,
                                     old_public=old_public)
Esempio n. 14
0
def review_request_field(context, nodelist, review_request_details, field_id):
    """Render a block with a specific review request field.

    Args:
        context (dict):
            The render context.

        nodelist (django.template.NodeList):
            The contents of the template inside the blocktag.

        review_request_details (reviewboard.reviews.models.
                                base_review_request_details.
                                BaseReviewRequestDetails):
            The review request or draft being rendered.

        field_id (unicode):
            The ID of the field to add to the render context.

    Returns:
        unicode:
        The rendered block.
    """
    request = context.get('request')

    try:
        field_cls = get_review_request_field(field_id)
        field = field_cls(review_request_details, request=request)
    except Exception as e:
        logging.exception('Error instantiating field %r: %s',
                          field_id, e)
        return ''

    context.push()

    try:
        context['field'] = field
        return nodelist.render(context)
    finally:
        context.pop()
Esempio n. 15
0
def review_request_field(context, nodelist, review_request_details, field_id):
    """Render a block with a specific review request field.

    Args:
        context (dict):
            The render context.

        nodelist (django.template.NodeList):
            The contents of the template inside the blocktag.

        review_request_details (reviewboard.reviews.models.
                                base_review_request_details.
                                BaseReviewRequestDetails):
            The review request or draft being rendered.

        field_id (unicode):
            The ID of the field to add to the render context.

    Returns:
        unicode:
        The rendered block.
    """
    request = context.get('request')

    try:
        field_cls = get_review_request_field(field_id)
        field = field_cls(review_request_details, request=request)
    except Exception as e:
        logging.exception('Error instantiating field %r: %s',
                          field_id, e)
        return ''

    context.push()

    try:
        context['field'] = field
        return nodelist.render(context)
    finally:
        context.pop()
Esempio n. 16
0
    def close(self,
              close_type=None,
              user=None,
              description=None,
              rich_text=False,
              **kwargs):
        """Closes the review request.

        Args:
            close_type (unicode):
                How the close occurs. This should be one of
                :py:attr:`SUBMITTED` or :py:attr:`DISCARDED`.

            user (django.contrib.auth.models.User):
                The user who is closing the review request.

            description (unicode):
                An optional description that indicates why the review request
                was closed.

            rich_text (bool):
                Indicates whether or not that the description is rich text.

        Raises:
            ValueError:
                The provided close type is not a valid value.

            PermissionError:
                The user does not have permission to close the review request.

            TypeError:
                Keyword arguments were supplied to the function.

        .. versionchanged:: 3.0
           The ``type`` argument is deprecated: ``close_type`` should be used
           instead.

           This method raises :py:exc:`ValueError` instead of
           :py:exc:`AttributeError` when the ``close_type`` has an incorrect
           value.
        """
        if close_type is None:
            try:
                close_type = kwargs.pop('type')
            except KeyError:
                raise AttributeError('close_type must be provided')

            warnings.warn(
                'The "type" argument was deprecated in Review Board 3.0 and '
                'will be removed in a future version. Use "close_type" '
                'instead.')

        if kwargs:
            raise TypeError('close() does not accept keyword arguments.')

        if (user and not self.is_mutable_by(user) and not user.has_perm(
                "reviews.can_change_status", self.local_site)):
            raise PermissionError

        if close_type not in [self.SUBMITTED, self.DISCARDED]:
            raise ValueError("%s is not a valid close type" % type)

        review_request_closing.send(sender=type(self),
                                    user=user,
                                    review_request=self,
                                    close_type=close_type,
                                    type=deprecated_signal_argument(
                                        signal_name='review_request_closing',
                                        old_name='type',
                                        new_name='close_type',
                                        value=close_type),
                                    description=description,
                                    rich_text=rich_text)

        draft = get_object_or_none(self.draft)

        if self.status != close_type:
            if (draft is not None and not self.public
                    and close_type == self.DISCARDED):
                # Copy over the draft information if this is a private discard.
                draft.copy_fields_to_request(self)

            # TODO: Use the user's default for rich_text.
            changedesc = ChangeDescription(public=True,
                                           text=description or "",
                                           rich_text=rich_text or False,
                                           user=user or self.submitter)

            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status,
                                             close_type)
            changedesc.save()

            self.changedescs.add(changedesc)

            if close_type == self.SUBMITTED:
                if not self.public:
                    raise PublishError("The draft must be public first.")
            else:
                self.commit_id = None

            self.status = close_type
            self.save(update_counts=True)

            review_request_closed.send(sender=type(self),
                                       user=user,
                                       review_request=self,
                                       close_type=close_type,
                                       type=deprecated_signal_argument(
                                           signal_name='review_request_closed',
                                           old_name='type',
                                           new_name='close_type',
                                           value=close_type),
                                       description=description,
                                       rich_text=rich_text)
        else:
            # Update submission description.
            changedesc = self.changedescs.filter(public=True).latest()
            changedesc.timestamp = timezone.now()
            changedesc.text = description or ""
            changedesc.rich_text = rich_text
            changedesc.save()

            # Needed to renew last-update.
            self.save()

        # Delete the associated draft review request.
        if draft is not None:
            draft.delete()
Esempio n. 17
0
    def close(self, close_type=None, user=None, description=None,
              rich_text=False, **kwargs):
        """Closes the review request.

        Args:
            close_type (unicode):
                How the close occurs. This should be one of
                :py:attr:`SUBMITTED` or :py:attr:`DISCARDED`.

            user (django.contrib.auth.models.User):
                The user who is closing the review request.

            description (unicode):
                An optional description that indicates why the review request
                was closed.

            rich_text (bool):
                Indicates whether or not that the description is rich text.

        Raises:
            ValueError:
                The provided close type is not a valid value.

            PermissionError:
                The user does not have permission to close the review request.

            TypeError:
                Keyword arguments were supplied to the function.

        .. versionchanged:: 3.0
           The ``type`` argument is deprecated: ``close_type`` should be used
           instead.

           This method raises :py:exc:`ValueError` instead of
           :py:exc:`AttributeError` when the ``close_type`` has an incorrect
           value.
        """
        if close_type is None:
            try:
                close_type = kwargs.pop('type')
            except KeyError:
                raise AttributeError('close_type must be provided')

            warnings.warn(
                'The "type" argument was deprecated in Review Board 3.0 and '
                'will be removed in a future version. Use "close_type" '
                'instead.'
            )

        if kwargs:
            raise TypeError('close() does not accept keyword arguments.')

        if (user and not self.is_mutable_by(user) and
            not user.has_perm("reviews.can_change_status", self.local_site)):
            raise PermissionError

        if close_type not in [self.SUBMITTED, self.DISCARDED]:
            raise ValueError("%s is not a valid close type" % type)

        review_request_closing.send(
            sender=type(self),
            user=user,
            review_request=self,
            close_type=close_type,
            type=deprecated_signal_argument(
                signal_name='review_request_closing',
                old_name='type',
                new_name='close_type',
                value=close_type),
            description=description,
            rich_text=rich_text)

        draft = get_object_or_none(self.draft)

        if self.status != close_type:
            if (draft is not None and
                not self.public and close_type == self.DISCARDED):
                # Copy over the draft information if this is a private discard.
                draft.copy_fields_to_request(self)

            # TODO: Use the user's default for rich_text.
            changedesc = ChangeDescription(public=True,
                                           text=description or "",
                                           rich_text=rich_text or False,
                                           user=user or self.submitter)

            status_field = get_review_request_field('status')(self)
            status_field.record_change_entry(changedesc, self.status,
                                             close_type)
            changedesc.save()

            self.changedescs.add(changedesc)

            if close_type == self.SUBMITTED:
                if not self.public:
                    raise PublishError("The draft must be public first.")
            else:
                self.commit_id = None

            self.status = close_type
            self.save(update_counts=True)

            review_request_closed.send(
                sender=type(self),
                user=user,
                review_request=self,
                close_type=close_type,
                type=deprecated_signal_argument(
                    signal_name='review_request_closed',
                    old_name='type',
                    new_name='close_type',
                    value=close_type),
                description=description,
                rich_text=rich_text)
        else:
            # Update submission description.
            changedesc = self.changedescs.filter(public=True).latest()
            changedesc.timestamp = timezone.now()
            changedesc.text = description or ""
            changedesc.rich_text = rich_text
            changedesc.save()

            # Needed to renew last-update.
            self.save()

        # Delete the associated draft review request.
        if draft is not None:
            draft.delete()
    def initialize(self):
        initialize_pulse_handlers(self)

        URLHook(self,
                patterns('', url(r'^mozreview/', include('mozreview.urls'))))

        HeaderDropdownActionHook(self, actions=[{
            'label': 'MozReview',
            'items': [
                {
                    'label': 'User Guide',
                    'url': 'https://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview-user.html',
                },
                {
                    'label': 'Mercurial for Mozillians',
                    'url': 'https://mozilla-version-control-tools.readthedocs.org/en/latest/hgmozilla/index.html',
                },
                {
                    'label': 'Hacking MozReview',
                    'url': 'https://mozilla-version-control-tools.readthedocs.org/en/latest/hacking-mozreview.html',
                },
                {
                    'label': 'File a Bug',
                    'url': 'https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=MozReview',
                },
            ],
        }])

        ReviewRequestDropdownActionHook(self, actions=[
        {
            'label': 'Automation',
            'id': 'automation-menu',
            'items': [
                {
                    'id': 'autoland-try-trigger',
                    'label': 'Trigger a Try Build',
                    'url': '#',
                },
                {
                    'id': 'autoland-trigger',
                    'label': 'Land Commits',
                    'url': '#',
                },
            ],
        },
        ])

        # Hide fields from all review requests that are not used by Mozilla
        # developers.
        main_fieldset = get_review_request_fieldset('main')
        testing_done_field = get_review_request_field('testing_done')
        if testing_done_field:
            main_fieldset.remove_field(testing_done_field)

        info_fieldset = get_review_request_fieldset('info')
        for field_name in ('branch', 'depends_on', 'blocks'):
            field = get_review_request_field(field_name)
            if field:
                info_fieldset.remove_field(field)

        # We "monkey patch" (yes, I feel dirty) the should_render method on
        # the description field so that it is not rendered for parent review
        # requests.
        description_field = get_review_request_field('description')
        if description_field:
            description_field.should_render = (lambda self, value:
                not is_parent(self.review_request_details))

        # All of our review request styling is injected via
        # review-stylings-css, which in turn loads the review.css static
        # bundle.
        TemplateHook(self, 'base-css', 'mozreview/review-stylings-css.html',
                     apply_to=review_request_url_names)
        TemplateHook(self, 'base-css', 'mozreview/viewdiff-stylings-css.html',
                     apply_to=diffviewer_url_names)
        TemplateHook(self, 'base-scripts-post',
                     'mozreview/review-scripts-js.html',
                     apply_to=review_request_url_names)
        TemplateHook(self, 'base-extrahead',
                     'mozreview/base-extrahead-login-form.html',
                     apply_to=['login'])
        TemplateHook(self, 'before-login-form',
                     'mozreview/before-login-form.html', apply_to=['login'])
        TemplateHook(self, 'after-login-form',
                     'mozreview/after-login-form.html', apply_to=['login'])
        TemplateHook(self, 'base-after-content',
                     'mozreview/scm_level.html')
        TemplateHook(self, 'base-after-content',
                     'mozreview/repository.html')

        ReviewRequestFieldsHook(self, 'main', [CommitsListField])
        # This forces the Commits field to be the top item.
        main_fieldset.field_classes.insert(0,
                                           main_fieldset.field_classes.pop())

        # The above hack forced Commits at the top, but the rest of these
        # fields are fine below the Description.
        ReviewRequestFieldsHook(self, 'main', [CombinedReviewersField])
        ReviewRequestFieldsHook(self, 'main', [TryField])
        ReviewRequestFieldsHook(self, 'main', [BaseCommitField])
        ReviewRequestFieldsHook(self, 'main', [FileDiffReviewerField])

        # We want pull to appear first as it is the more robust way of
        # retrieving changesets.
        ReviewRequestFieldsHook(self, 'info', [PullCommitField])
        ReviewRequestFieldsHook(self, 'info', [ImportCommitField])

        # Use a custom method to calculate a review approval state.
        MozReviewApprovalHook(self)

        SignalHook(self, post_save, self.on_draft_changed,
                   sender=ReviewRequestDraft)

        HostingServiceHook(self, HMORepository)

        URLHook(self, patterns('',
            url(r'^import-pullrequest/(?P<user>.+)/(?P<repo>.+)/(?P<pullrequest>\d+)/$',
            import_pullrequest, name='import_pullrequest')))
    def get_extra_data_field_supports_markdown(self, review_request, key):
        field_cls = get_review_request_field(key)

        return field_cls and getattr(field_cls, 'enable_markdown', False)
Esempio n. 20
0
    def initialize(self):
        AuthBackendHook(self, BugzillaBackend)

        self.original_opcode_generator = get_diff_opcode_generator_class()
        set_diff_opcode_generator_class(NoFilterDiffOpcodeGenerator)

        initialize_pulse_handlers(self)

        URLHook(self,
                patterns('', url(r'^mozreview/', include('mozreview.urls'))))

        HeaderDropdownActionHook(
            self,
            actions=[{
                'id':
                'nav-mozreview-menu',
                'label':
                'MozReview',
                'items': [
                    {
                        'label':
                        'User Guide',
                        'url':
                        'https://mozilla-version-control-tools.readthedocs.io/en/latest/mozreview-user.html',
                    },
                    {
                        'label':
                        'Mercurial for Mozillians',
                        'url':
                        'https://mozilla-version-control-tools.readthedocs.io/en/latest/hgmozilla/index.html',
                    },
                    {
                        'label':
                        'Hacking MozReview',
                        'url':
                        'https://mozilla-version-control-tools.readthedocs.io/en/latest/hacking-mozreview.html',
                    },
                    {
                        'label':
                        'File a Bug',
                        'url':
                        'https://bugzilla.mozilla.org/enter_bug.cgi?product=MozReview&component=General',
                    },
                ],
            }])

        review_request_dropdown_actions = [
            {
                'label':
                'Automation',
                'id':
                'automation-menu',
                'items': [
                    {
                        'id': 'autoland-try-trigger',
                        'label': 'Trigger a Try Build',
                        'url': '#',
                    },
                    {
                        'id': 'autoland-trigger',
                        'label': 'Land Commits',
                        'url': '#',
                    },
                ],
            },
        ]

        ReviewRequestDropdownActionHook(
            self, actions=review_request_dropdown_actions)
        DiffViewerDropdownActionHook(self,
                                     actions=review_request_dropdown_actions)

        # Hide fields from all review requests that are not used by Mozilla
        # developers.
        main_fieldset = get_review_request_fieldset('main')
        testing_done_field = get_review_request_field('testing_done')
        if testing_done_field:
            main_fieldset.remove_field(testing_done_field)

        info_fieldset = get_review_request_fieldset('info')
        for field_name in ('branch', 'depends_on', 'blocks'):
            field = get_review_request_field(field_name)
            if field:
                info_fieldset.remove_field(field)

        # We "monkey patch" (yes, I feel dirty) the should_render method on
        # the description field so that it is not rendered
        description_field = get_review_request_field('description')
        if description_field:
            self.old_desc_should_render = description_field.should_render
            description_field.should_render = lambda self, value: False

        # All of our review request styling is injected via
        # review-stylings-css, which in turn loads the review.css static
        # bundle.
        TemplateHook(self,
                     'base-css',
                     'mozreview/review-stylings-css.html',
                     apply_to=review_request_url_names)
        TemplateHook(self,
                     'base-css',
                     'mozreview/viewdiff-stylings-css.html',
                     apply_to=diffviewer_url_names)
        TemplateHook(
            self,
            'base-css',
            'mozreview/admin-stylings-css.html',
            apply_to=['reviewboard.extensions.views.configure_extension'])
        TemplateHook(self,
                     'base-scripts-post',
                     'mozreview/review-scripts-js.html',
                     apply_to=review_request_url_names)
        TemplateHook(self,
                     'base-extrahead',
                     'mozreview/base-extrahead-login-form.html',
                     apply_to=['login'])
        TemplateHook(self,
                     'before-login-form',
                     'mozreview/before-login-form.html',
                     apply_to=['login'])
        TemplateHook(self,
                     'after-login-form',
                     'mozreview/after-login-form.html',
                     apply_to=['login'])
        TemplateHook(self, 'base-after-content', 'mozreview/user-data.html')
        TemplateHook(self, 'base-after-content', 'mozreview/repository.html')
        TemplateHook(self,
                     'base-after-content',
                     'mozreview/user_review_flag.html',
                     apply_to=review_request_url_names)
        TemplateHook(self,
                     'base-after-content',
                     'mozreview/diff-data.html',
                     apply_to=diffviewer_url_names)
        TemplateHook(self, 'review-summary-body',
                     'mozreview/review_summary_flag_info.html')

        ReviewRequestFieldsHook(self, 'main', [CommitsListField])
        ReviewRequestFieldsHook(self, 'main', [CommitDetailField])
        CommitContextTemplateHook(self,
                                  'mozreview-pre-review-request-box',
                                  'mozreview/commits.html',
                                  apply_to=review_request_url_names)

        # The above hack forced Commits at the top, but the rest of these
        # fields are fine below the Description.
        ReviewRequestFieldsHook(self, 'main', [CombinedReviewersField])
        ReviewRequestFieldsHook(self, 'main', [TryField])
        ReviewRequestFieldsHook(self, 'main', [BaseCommitField])
        ReviewRequestFieldsHook(self, 'main', [FileDiffReviewerField])

        ReviewRequestFieldsHook(self, 'info', [CommitAuthorField])

        # Use a custom method to calculate a review approval state.
        MozReviewApprovalHook(self)

        # Instantiate the various signal handlers
        initialize_signal_handlers(self)

        HostingServiceHook(self, HMORepository)
        HostingServiceHook(self, BMOBugTracker)
    def get_extra_data_field_supports_markdown(self, review_request, key):
        field_cls = get_review_request_field(key)

        return field_cls and getattr(field_cls, 'enable_markdown', False)