Ejemplo n.º 1
0
def reviewlog(request):
    data = request.GET.copy()

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = ReviewLogForm(data)

    approvals = ActivityLog.objects.review_log()
    if not acl.check_unlisted_addons_reviewer(request):
        # Only display logs related to unlisted versions to users with the
        # right permission.
        list_channel = amo.RELEASE_CHANNEL_LISTED
        approvals = approvals.filter(versionlog__version__channel=list_channel)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term) |
                Q(addonlog__addon__name__localized_string__icontains=term) |
                Q(user__display_name__icontains=term) |
                Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    data = context(request, form=form, pager=pager)
    return render(request, 'reviewers/reviewlog.html', data)
Ejemplo n.º 2
0
def reviewlog(request):
    data = request.GET.copy()

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = ReviewLogForm(data)

    approvals = ActivityLog.objects.review_log()
    if not acl.check_unlisted_addons_reviewer(request):
        # Only display logs related to unlisted versions to users with the
        # right permission.
        list_channel = amo.RELEASE_CHANNEL_LISTED
        approvals = approvals.filter(versionlog__version__channel=list_channel)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term)
                | Q(addonlog__addon__name__localized_string__icontains=term)
                | Q(user__display_name__icontains=term)
                | Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    data = context(request, form=form, pager=pager)
    return render(request, 'reviewers/reviewlog.html', data)
Ejemplo n.º 3
0
def owner_or_unlisted_reviewer(request, addon):
    return (acl.check_unlisted_addons_reviewer(request) or
            # We don't want "admins" here, because it includes anyone with the
            # "Addons:Edit" perm, we only want those with
            # "Addons:ReviewUnlisted" perm (which is checked above).
            acl.check_addon_ownership(request, addon, admin=False, dev=True,
                                      viewer=True, support=True))
Ejemplo n.º 4
0
def owner_or_unlisted_reviewer(request, addon):
    return (
        acl.check_unlisted_addons_reviewer(request) or
        # We don't want "admins" here, because it includes anyone with the
        # "Addons:Edit" perm, we only want those with
        # "Addons:ReviewUnlisted" perm (which is checked above).
        acl.check_addon_ownership(request, addon, admin=False, dev=True))
Ejemplo n.º 5
0
def reviewlog(request):
    data = request.GET.copy()

    motd_editable = acl.action_allowed(
        request, amo.permissions.ADDON_REVIEWER_MOTD_EDIT)

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = forms.ReviewLogForm(data)

    approvals = ActivityLog.objects.review_log()
    if not acl.check_unlisted_addons_reviewer(request):
        # Display logs related to unlisted versions only to senior reviewers.
        list_channel = amo.RELEASE_CHANNEL_LISTED
        approvals = approvals.filter(versionlog__version__channel=list_channel)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term)
                | Q(addonlog__addon__name__localized_string__icontains=term)
                | Q(user__display_name__icontains=term)
                | Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    action_dict = {
        amo.LOG.APPROVE_VERSION.id:
        ugettext('was approved'),
        # The log will still show preliminary, even after the migration.
        amo.LOG.PRELIMINARY_VERSION.id:
        ugettext('given preliminary review'),
        amo.LOG.REJECT_VERSION.id:
        ugettext('rejected'),
        amo.LOG.ESCALATE_VERSION.id:
        pgettext('editors_review_history_nominated_adminreview', 'escalated'),
        amo.LOG.REQUEST_INFORMATION.id:
        ugettext('needs more information'),
        amo.LOG.REQUEST_SUPER_REVIEW.id:
        ugettext('needs super review'),
        amo.LOG.COMMENT_VERSION.id:
        ugettext('commented'),
        amo.LOG.CONFIRM_AUTO_APPROVED.id:
        ugettext('confirmed as approved'),
    }

    data = context(request,
                   form=form,
                   pager=pager,
                   ACTION_DICT=action_dict,
                   motd_editable=motd_editable)
    return render(request, 'editors/reviewlog.html', data)
Ejemplo n.º 6
0
 def get_serializer_class(self):
     # Override serializer to use serializer_class_with_unlisted_data if
     # we are allowed to access unlisted data.
     obj = getattr(self, 'instance')
     request = self.request
     if (acl.check_unlisted_addons_reviewer(request) or
             (obj and request.user.is_authenticated and
              obj.authors.filter(pk=request.user.pk).exists())):
         return self.serializer_class_with_unlisted_data
     return self.serializer_class
Ejemplo n.º 7
0
 def get_serializer_class(self):
     # Override serializer to use serializer_class_with_unlisted_data if
     # we are allowed to access unlisted data.
     obj = getattr(self, 'instance')
     request = self.request
     if acl.check_unlisted_addons_reviewer(request) or (
             obj and request.user.is_authenticated
             and obj.authors.filter(pk=request.user.pk).exists()):
         return self.serializer_class_with_unlisted_data
     return self.serializer_class
Ejemplo n.º 8
0
def reviewlog(request):
    data = request.GET.copy()

    motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit')

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = forms.ReviewLogForm(data)

    approvals = ActivityLog.objects.review_queue()
    if not acl.check_unlisted_addons_reviewer(request):
        # Display logs related to unlisted add-ons only to senior reviewers.
        approvals = approvals.filter(addonlog__addon__is_listed=True)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term)
                | Q(addonlog__addon__name__localized_string__icontains=term)
                | Q(user__display_name__icontains=term)
                | Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    ad = {
        amo.LOG.APPROVE_VERSION.id:
        _('was approved'),
        amo.LOG.PRELIMINARY_VERSION.id:
        _('given preliminary review'),
        amo.LOG.REJECT_VERSION.id:
        _('rejected'),
        amo.LOG.ESCALATE_VERSION.id:
        pgettext('editors_review_history_nominated_adminreview', 'escalated'),
        amo.LOG.REQUEST_INFORMATION.id:
        _('needs more information'),
        amo.LOG.REQUEST_SUPER_REVIEW.id:
        _('needs super review'),
        amo.LOG.COMMENT_VERSION.id:
        _('commented'),
    }

    data = context(request,
                   form=form,
                   pager=pager,
                   ACTION_DICT=ad,
                   motd_editable=motd_editable)
    return render(request, 'editors/reviewlog.html', data)
Ejemplo n.º 9
0
    def has_object_permission(self, request, view, obj):
        can_access_because_unlisted_viewer = (
            request.method in SAFE_METHODS and acl.action_allowed(
                request, permissions.REVIEWER_TOOLS_UNLISTED_VIEW))
        can_access_because_unlisted_reviewer = acl.check_unlisted_addons_reviewer(
            request)
        has_unlisted_or_no_listed = obj.has_unlisted_versions(
            include_deleted=True) or not obj.has_listed_versions(
                include_deleted=True)

        return has_unlisted_or_no_listed and (
            can_access_because_unlisted_viewer
            or can_access_because_unlisted_reviewer)
Ejemplo n.º 10
0
def reviewlog(request):
    data = request.GET.copy()

    motd_editable = acl.action_allowed(
        request, amo.permissions.ADDON_REVIEWER_MOTD_EDIT)

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = forms.ReviewLogForm(data)

    approvals = ActivityLog.objects.review_log()
    if not acl.check_unlisted_addons_reviewer(request):
        # Display logs related to unlisted versions only to senior reviewers.
        list_channel = amo.RELEASE_CHANNEL_LISTED
        approvals = approvals.filter(versionlog__version__channel=list_channel)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term) |
                Q(addonlog__addon__name__localized_string__icontains=term) |
                Q(user__display_name__icontains=term) |
                Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    action_dict = {
        amo.LOG.APPROVE_VERSION.id: ugettext('was approved'),
        # The log will still show preliminary, even after the migration.
        amo.LOG.PRELIMINARY_VERSION.id: ugettext('given preliminary review'),
        amo.LOG.REJECT_VERSION.id: ugettext('rejected'),
        amo.LOG.ESCALATE_VERSION.id: pgettext(
            'editors_review_history_nominated_adminreview', 'escalated'),
        amo.LOG.REQUEST_INFORMATION.id: ugettext('needs more information'),
        amo.LOG.REQUEST_SUPER_REVIEW.id: ugettext('needs super review'),
        amo.LOG.COMMENT_VERSION.id: ugettext('commented'),
        amo.LOG.CONFIRM_AUTO_APPROVED.id: ugettext('confirmed as approved'),
    }

    data = context(request, form=form, pager=pager, ACTION_DICT=action_dict,
                   motd_editable=motd_editable)
    return render(request, 'editors/reviewlog.html', data)
Ejemplo n.º 11
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED
                else acl.check_unlisted_addons_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.objects,
                                  pk=file_.version.addon_id)
    channel = file_.version.channel

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if (is_appropriate_reviewer(addon, channel) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.guarded_file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: addon/file disabled and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    if channel == amo.RELEASE_CHANNEL_UNLISTED:
        if (acl.check_unlisted_addons_reviewer(request) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: version is unlisted and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    attachment = (type == 'attachment' or not request.APP.browser)

    loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                    filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response['X-Target-Digest'] = file_.hash
    return response
Ejemplo n.º 12
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED
                else acl.check_unlisted_addons_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.objects,
                                  pk=file_.version.addon_id)
    channel = file_.version.channel

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if (is_appropriate_reviewer(addon, channel) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.guarded_file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: addon/file disabled and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    if channel == amo.RELEASE_CHANNEL_UNLISTED:
        if (acl.check_unlisted_addons_reviewer(request) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: version is unlisted and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    attachment = bool(type == 'attachment')

    loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                    filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response['X-Target-Digest'] = file_.hash
    return response
Ejemplo n.º 13
0
def reviewlog(request):
    data = request.GET.copy()

    motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit')

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = forms.ReviewLogForm(data)

    approvals = ActivityLog.objects.review_queue()
    if not acl.check_unlisted_addons_reviewer(request):
        # Display logs related to unlisted add-ons only to senior reviewers.
        approvals = approvals.filter(addonlog__addon__is_listed=True)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term) |
                Q(addonlog__addon__name__localized_string__icontains=term) |
                Q(user__display_name__icontains=term) |
                Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    ad = {
        amo.LOG.APPROVE_VERSION.id: _('was approved'),
        amo.LOG.PRELIMINARY_VERSION.id: _('given preliminary review'),
        amo.LOG.REJECT_VERSION.id: _('rejected'),
        amo.LOG.ESCALATE_VERSION.id: pgettext(
            'editors_review_history_nominated_adminreview', 'escalated'),
        amo.LOG.REQUEST_INFORMATION.id: _('needs more information'),
        amo.LOG.REQUEST_SUPER_REVIEW.id: _('needs super review'),
        amo.LOG.COMMENT_VERSION.id: _('commented'),
    }

    data = context(request, form=form, pager=pager, ACTION_DICT=ad,
                   motd_editable=motd_editable)
    return render(request, 'editors/reviewlog.html', data)
Ejemplo n.º 14
0
def perform_review_permission_checks(request,
                                     addon,
                                     channel,
                                     content_review_only=False):
    """Perform the permission checks needed by the review() view or anything
    that follows the same behavior, such as the whiteboard() view.

    Raises PermissionDenied when the current user on the request does not have
    the right permissions for the context defined by addon, channel and
    content_review_only boolean.
    """
    unlisted_only = (channel == amo.RELEASE_CHANNEL_UNLISTED
                     or not addon.has_listed_versions())
    was_auto_approved = (channel == amo.RELEASE_CHANNEL_LISTED
                         and addon.current_version
                         and addon.current_version.was_auto_approved)
    static_theme = addon.type == amo.ADDON_STATICTHEME

    # Are we looking at an unlisted review page, or (weirdly) the listed
    # review page of an unlisted-only add-on?
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    # If we're only doing a content review, we just need to check for the
    # content review permission, otherwise it's the "main" review page.
    if content_review_only:
        if not acl.action_allowed(request,
                                  amo.permissions.ADDONS_CONTENT_REVIEW):
            raise PermissionDenied
    elif static_theme:
        if not acl.action_allowed(request,
                                  amo.permissions.STATIC_THEMES_REVIEW):
            raise PermissionDenied
    else:
        # Was the add-on auto-approved?
        if was_auto_approved and not acl.action_allowed(
                request, amo.permissions.ADDONS_POST_REVIEW):
            raise PermissionDenied

        # Finally, if it wasn't auto-approved, check for legacy reviewer
        # permission.
        if not was_auto_approved and not acl.action_allowed(
                request, amo.permissions.ADDONS_REVIEW):
            raise PermissionDenied
Ejemplo n.º 15
0
def perform_review_permission_checks(
        request, addon, channel, content_review_only=False):
    """Perform the permission checks needed by the review() view or anything
    that follows the same behavior, such as the whiteboard() view.

    Raises PermissionDenied when the current user on the request does not have
    the right permissions for the context defined by addon, channel and
    content_review_only boolean.
    """
    unlisted_only = (
        channel == amo.RELEASE_CHANNEL_UNLISTED or
        not addon.has_listed_versions())
    was_auto_approved = (
        channel == amo.RELEASE_CHANNEL_LISTED and
        addon.current_version and addon.current_version.was_auto_approved)
    static_theme = addon.type == amo.ADDON_STATICTHEME

    # Are we looking at an unlisted review page, or (weirdly) the listed
    # review page of an unlisted-only add-on?
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    # If we're only doing a content review, we just need to check for the
    # content review permission, otherwise it's the "main" review page.
    if content_review_only:
        if not acl.action_allowed(
                request, amo.permissions.ADDONS_CONTENT_REVIEW):
            raise PermissionDenied
    elif static_theme:
        if not acl.action_allowed(
                request, amo.permissions.STATIC_THEMES_REVIEW):
            raise PermissionDenied
    else:
        # Was the add-on auto-approved?
        if was_auto_approved and not acl.action_allowed(
                request, amo.permissions.ADDONS_POST_REVIEW):
            raise PermissionDenied

        # Finally, if it wasn't auto-approved, check for legacy reviewer
        # permission.
        if not was_auto_approved and not acl.action_allowed(
                request, amo.permissions.ADDONS_REVIEW):
            raise PermissionDenied
Ejemplo n.º 16
0
    def __init__(self, *args, **kw):
        self.addon = kw.pop('addon')
        self.request = kw.pop('request')
        super(FileCompareForm, self).__init__(*args, **kw)

        queryset = File.objects.filter(version__addon=self.addon)
        if acl.check_unlisted_addons_reviewer(self.request):
            should_show_channel = (
                queryset.filter(
                    version__channel=amo.RELEASE_CHANNEL_LISTED).exists() and
                queryset.filter(
                    version__channel=amo.RELEASE_CHANNEL_UNLISTED).exists())
        else:
            should_show_channel = False
            queryset = queryset.filter(
                version__channel=amo.RELEASE_CHANNEL_LISTED)

        self.fields['left'].queryset = queryset
        self.fields['right'].queryset = queryset
        self.fields['left'].widget.should_show_channel = should_show_channel
        self.fields['right'].widget.should_show_channel = should_show_channel
Ejemplo n.º 17
0
    def __init__(self, *args, **kw):
        self.addon = kw.pop('addon')
        self.request = kw.pop('request')
        super(FileCompareForm, self).__init__(*args, **kw)

        queryset = File.objects.filter(version__addon=self.addon)
        if acl.check_unlisted_addons_reviewer(self.request):
            should_show_channel = (
                queryset.filter(
                    version__channel=amo.RELEASE_CHANNEL_LISTED).exists()
                and queryset.filter(
                    version__channel=amo.RELEASE_CHANNEL_UNLISTED).exists())
        else:
            should_show_channel = False
            queryset = queryset.filter(
                version__channel=amo.RELEASE_CHANNEL_LISTED)

        self.fields['left'].queryset = queryset
        self.fields['right'].queryset = queryset
        self.fields['left'].widget.should_show_channel = should_show_channel
        self.fields['right'].widget.should_show_channel = should_show_channel
Ejemplo n.º 18
0
def reviewlog(request):
    data = request.GET.copy()

    motd_editable = acl.action_allowed(
        request, amo.permissions.ADDON_REVIEWER_MOTD_EDIT)

    if not data.get('start') and not data.get('end'):
        today = date.today()
        data['start'] = date(today.year, today.month, 1)

    form = forms.ReviewLogForm(data)

    approvals = ActivityLog.objects.review_log()
    if not acl.check_unlisted_addons_reviewer(request):
        # Display logs related to unlisted versions only to senior reviewers.
        list_channel = amo.RELEASE_CHANNEL_LISTED
        approvals = approvals.filter(versionlog__version__channel=list_channel)

    if form.is_valid():
        data = form.cleaned_data
        if data['start']:
            approvals = approvals.filter(created__gte=data['start'])
        if data['end']:
            approvals = approvals.filter(created__lt=data['end'])
        if data['search']:
            term = data['search']
            approvals = approvals.filter(
                Q(commentlog__comments__icontains=term)
                | Q(addonlog__addon__name__localized_string__icontains=term)
                | Q(user__display_name__icontains=term)
                | Q(user__username__icontains=term)).distinct()

    pager = amo.utils.paginate(request, approvals, 50)
    data = context(request,
                   form=form,
                   pager=pager,
                   motd_editable=motd_editable)
    return render(request, 'editors/reviewlog.html', data)
Ejemplo n.º 19
0
 def has_permission(self, request, view):
     return acl.check_unlisted_addons_reviewer(request)
Ejemplo n.º 20
0
 def has_permission(self, request, view):
     return acl.check_unlisted_addons_reviewer(request)
Ejemplo n.º 21
0
 def is_appropriate_reviewer(addon, channel):
     return (acl.is_reviewer(request, addon)
             if channel == amo.RELEASE_CHANNEL_LISTED
             else acl.check_unlisted_addons_reviewer(request))
Ejemplo n.º 22
0
 def is_appropriate_reviewer(addon, channel):
     return (acl.is_reviewer(request, addon)
             if channel == amo.RELEASE_CHANNEL_LISTED else
             acl.check_unlisted_addons_reviewer(request))
Ejemplo n.º 23
0
 def wrapper(request, *args, **kw):
     if acl.check_unlisted_addons_reviewer(request):
         return f(request, *args, **kw)
     raise PermissionDenied
Ejemplo n.º 24
0
def review(request, addon, channel=None):
    # channel is passed in as text, but we want the constant.
    channel = amo.CHANNEL_CHOICES_LOOKUP.get(
        channel, amo.RELEASE_CHANNEL_LISTED)
    unlisted_only = (channel == amo.RELEASE_CHANNEL_UNLISTED or
                     not addon.has_listed_versions())
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    version = addon.find_latest_version(
        channel=channel, exclude=(amo.STATUS_BETA,))

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(
            request, ugettext('Self-reviews are not allowed.'))
        return redirect(reverse('editors.queue'))

    # Get the current info request state to set as the default.
    form_initial = {'info_request': version and version.has_info_request}

    form_helper = ReviewHelper(request=request, addon=addon, version=version)
    form = forms.ReviewForm(request.POST if request.method == 'POST' else None,
                            helper=form_helper, initial=form_initial)
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
    is_post_reviewer = acl.action_allowed(request,
                                          amo.permissions.ADDONS_POST_REVIEW)

    approvals_info = None
    reports = None
    user_reviews = None
    was_auto_approved = False
    if channel == amo.RELEASE_CHANNEL_LISTED:
        if addon.current_version:
            was_auto_approved = addon.current_version.was_auto_approved
        if is_post_reviewer and version and version.is_webextension:
            try:
                approvals_info = addon.addonapprovalscounter
            except AddonApprovalsCounter.DoesNotExist:
                pass

        developers = addon.listed_authors
        reports = Paginator(
            (AbuseReport.objects
                        .filter(Q(addon=addon) | Q(user__in=developers))
                        .order_by('-created')), 5).page(1)
        user_reviews = Paginator(
            (Review.without_replies
                   .filter(addon=addon, rating__lte=3, body__isnull=False)
                   .order_by('-created')), 5).page(1)

        if was_auto_approved and is_post_reviewer:
            queue_type = 'auto_approved'
        else:
            queue_type = form.helper.handler.review_type
        redirect_url = reverse('editors.queue_%s' % queue_type)
    else:
        redirect_url = reverse('editors.unlisted_queue_all')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.user,
                                                     addon=addon)
        if form.cleaned_data.get('adminflag') and is_admin:
            addon.update(admin_review=False)
        amo.messages.success(
            request, ugettext('Review successfully processed.'))
        clear_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since editors will almost certainly need to access
    # them. But only if we're not running in eager mode, since that could mean
    # blocking page load for several minutes.
    if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
        for file_ in version.all_files:
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        # Find the previously approved version to compare to.
        show_diff = version and (
            addon.versions.exclude(id=version.id).filter(
                # We're looking for a version that was either manually approved
                # or auto-approved but then confirmed.
                Q(autoapprovalsummary__isnull=True) |
                Q(autoapprovalsummary__verdict=amo.AUTO_APPROVED,
                  autoapprovalsummary__confirmed=True)
            ).filter(
                channel=channel,
                files__isnull=False,
                created__lt=version.created,
                files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form for.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # The actions we should show the comments form for (contrary to minimal
    # form above, it defaults to True, because most actions do need to have
    # the comments form).
    actions_comments = [k for (k, a) in actions if a.get('comments', True)]

    # The actions we should show the 'info request' checkbox for.
    actions_info_request = [k for (k, a) in actions
                            if a.get('info_request', False)]

    versions = (Version.unfiltered.filter(addon=addon, channel=channel)
                                  .select_related('autoapprovalsummary')
                                  .exclude(files__status=amo.STATUS_BETA)
                                  .order_by('-created')
                                  .transform(Version.transformer_activity)
                                  .transform(Version.transformer))

    # We assume comments on old deleted versions are for listed versions.
    # See _get_comments_for_hard_deleted_versions above for more detail.
    all_versions = (_get_comments_for_hard_deleted_versions(addon)
                    if channel == amo.RELEASE_CHANNEL_LISTED else [])
    all_versions.extend(versions)
    all_versions.sort(key=lambda v: v.created,
                      reverse=True)

    pager = amo.utils.paginate(request, all_versions, 10)
    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    max_average_daily_users = int(
        get_config('AUTO_APPROVAL_MAX_AVERAGE_DAILY_USERS') or 0)
    min_approved_updates = int(
        get_config('AUTO_APPROVAL_MIN_APPROVED_UPDATES') or 0)
    auto_approval_info = {}
    # Now that we've paginated the versions queryset, iterate on them to
    # generate auto approvals info. Note that the variable should not clash
    # the already existing 'version'.
    for a_version in pager.object_list:
        if not is_post_reviewer or not a_version.is_ready_for_auto_approval:
            continue
        try:
            summary = a_version.autoapprovalsummary
        except AutoApprovalSummary.DoesNotExist:
            auto_approval_info[a_version.pk] = None
            continue
        # Call calculate_verdict() again, it will use the data already stored.
        # Need to pass max_average_daily_users and min_approved_updates current
        # values.
        verdict_info = summary.calculate_verdict(
            max_average_daily_users=max_average_daily_users,
            min_approved_updates=min_approved_updates,
            pretty=True)
        auto_approval_info[a_version.pk] = verdict_info

    if version:
        flags = get_flags(version)
    else:
        flags = []

    user_changes_actions = [
        amo.LOG.ADD_USER_WITH_ROLE.id,
        amo.LOG.CHANGE_USER_WITH_ROLE.id,
        amo.LOG.REMOVE_USER_WITH_ROLE.id]
    user_changes_log = AddonLog.objects.filter(
        activity_log__action__in=user_changes_actions,
        addon=addon).order_by('id')
    ctx = context(request, version=version, addon=addon,
                  pager=pager, num_pages=num_pages, count=count, flags=flags,
                  form=form, canned=canned, is_admin=is_admin,
                  show_diff=show_diff,
                  actions=actions, actions_minimal=actions_minimal,
                  actions_comments=actions_comments,
                  actions_info_request=actions_info_request,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=(channel == amo.RELEASE_CHANNEL_UNLISTED),
                  approvals_info=approvals_info,
                  is_post_reviewer=is_post_reviewer,
                  auto_approval_info=auto_approval_info,
                  reports=reports, user_reviews=user_reviews,
                  was_auto_approved=was_auto_approved)

    return render(request, 'editors/review.html', ctx)
Ejemplo n.º 25
0
def review(request, addon):
    if not addon.is_listed and not acl.check_unlisted_addons_reviewer(request):
        raise http.Http404

    version = addon.find_latest_version_including_rejected()

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('editors.queue'))

    form_helper = ReviewHelper(request=request, addon=addon, version=version)
    form = forms.ReviewForm(request.POST or None, helper=form_helper)
    if addon.is_listed:
        queue_type = form.helper.review_type
        redirect_url = reverse('editors.queue_%s' % queue_type)
    else:
        redirect_url = reverse('editors.unlisted_queue_all')

    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.user,
                                                     addon=addon)
        if form.cleaned_data.get('adminflag') and is_admin:
            addon.update(admin_review=False)
        amo.messages.success(request, _('Review successfully processed.'))
        clear_review_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since editors will almost certainly need to access
    # them. But only if we're not running in eager mode, since that could mean
    # blocking page load for several minutes.
    if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
        for file_ in version.files.all():
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        show_diff = version and (addon.versions.exclude(id=version.id).filter(
            files__isnull=False,
            created__lt=version.created,
            files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    versions = (Version.unfiltered.filter(addon=addon).exclude(
        files__status=amo.STATUS_BETA).order_by('-created').transform(
            Version.transformer_activity).transform(Version.transformer))

    class PseudoVersion(object):
        def __init__(self):
            self.all_activity = []

        all_files = ()
        approvalnotes = None
        compatible_apps_ordered = ()
        releasenotes = None
        status = 'Deleted'
        deleted = True
        channel = amo.RELEASE_CHANNEL_LISTED

        @property
        def created(self):
            return self.all_activity[0].created

        @property
        def version(self):
            return (self.all_activity[0].activity_log.details.get(
                'version', '[deleted]'))

    # Grab review history for deleted versions of this add-on
    # Version are soft-deleted now but we need this for older deletions.
    comments = (CommentLog.objects.filter(
        activity_log__action__in=amo.LOG_REVIEW_QUEUE,
        activity_log__versionlog=None,
        activity_log__addonlog__addon=addon).order_by(
            'created').select_related('activity_log'))

    comment_versions = defaultdict(PseudoVersion)
    for c in comments:
        c.version = c.activity_log.details.get('version', c.created)
        comment_versions[c.version].all_activity.append(c)

    all_versions = comment_versions.values()
    all_versions.extend(versions)
    all_versions.sort(key=lambda v: v.created, reverse=True)

    pager = amo.utils.paginate(request, all_versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    try:
        flags = ViewQueue.objects.get(id=addon.id).flags
    except ViewQueue.DoesNotExist:
        flags = []

    user_changes_actions = [
        amo.LOG.ADD_USER_WITH_ROLE.id, amo.LOG.CHANGE_USER_WITH_ROLE.id,
        amo.LOG.REMOVE_USER_WITH_ROLE.id
    ]
    user_changes_log = AddonLog.objects.filter(
        activity_log__action__in=user_changes_actions,
        addon=addon).order_by('id')
    ctx = context(request,
                  version=version,
                  addon=addon,
                  pager=pager,
                  num_pages=num_pages,
                  count=count,
                  flags=flags,
                  form=form,
                  canned=canned,
                  is_admin=is_admin,
                  show_diff=show_diff,
                  actions=actions,
                  actions_minimal=actions_minimal,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=not addon.is_listed)

    return render(request, 'editors/review.html', ctx)
Ejemplo n.º 26
0
 def is_reviewer(channel):
     return (acl.check_addons_reviewer(request)
             if channel == amo.RELEASE_CHANNEL_LISTED
             else acl.check_unlisted_addons_reviewer(request))
Ejemplo n.º 27
0
def review(request, addon):
    if not addon.is_listed and not acl.check_unlisted_addons_reviewer(request):
        raise http.Http404

    version = addon.latest_version

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('editors.queue'))

    form_helper = ReviewHelper(request=request, addon=addon, version=version)
    form = forms.ReviewForm(request.POST or None, helper=form_helper)
    queue_type = ('prelim' if form.helper.review_type == 'preliminary'
                  else form.helper.review_type)
    if addon.is_listed:
        redirect_url = reverse('editors.queue_%s' % queue_type)
    else:
        redirect_url = reverse('editors.unlisted_queue_%s' % queue_type)

    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.user,
                                                     addon=addon)
        if form.cleaned_data.get('adminflag') and is_admin:
            addon.update(admin_review=False)
        amo.messages.success(request, _('Review successfully processed.'))
        clear_review_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since editors will almost certainly need to access
    # them. But only if we're not running in eager mode, since that could mean
    # blocking page load for several minutes.
    if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
        for file_ in version.files.all():
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    statuses = [amo.STATUS_PUBLIC, amo.STATUS_LITE,
                amo.STATUS_LITE_AND_NOMINATED]

    try:
        show_diff = version and (
            addon.versions.exclude(id=version.id).filter(
                files__isnull=False, created__lt=version.created,
                files__status__in=statuses).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    versions = (Version.unfiltered.filter(addon=addon)
                                  .exclude(files__status=amo.STATUS_BETA)
                                  .order_by('-created')
                                  .transform(Version.transformer_activity)
                                  .transform(Version.transformer))

    class PseudoVersion(object):
        def __init__(self):
            self.all_activity = []

        all_files = ()
        approvalnotes = None
        compatible_apps_ordered = ()
        releasenotes = None
        status = 'Deleted'
        deleted = True

        @property
        def created(self):
            return self.all_activity[0].created

        @property
        def version(self):
            return (self.all_activity[0].activity_log
                        .details.get('version', '[deleted]'))

    # Grab review history for deleted versions of this add-on
    # Version are soft-deleted now but we need this for older deletions.
    comments = (CommentLog.objects
                .filter(activity_log__action__in=amo.LOG_REVIEW_QUEUE,
                        activity_log__versionlog=None,
                        activity_log__addonlog__addon=addon)
                .order_by('created')
                .select_related('activity_log'))

    comment_versions = defaultdict(PseudoVersion)
    for c in comments:
        c.version = c.activity_log.details.get('version', c.created)
        comment_versions[c.version].all_activity.append(c)

    all_versions = comment_versions.values()
    all_versions.extend(versions)
    all_versions.sort(key=lambda v: v.created,
                      reverse=True)

    pager = amo.utils.paginate(request, all_versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    try:
        flags = ViewQueue.objects.get(id=addon.id).flags
    except ViewQueue.DoesNotExist:
        flags = []

    user_changes_actions = [
        amo.LOG.ADD_USER_WITH_ROLE.id,
        amo.LOG.CHANGE_USER_WITH_ROLE.id,
        amo.LOG.REMOVE_USER_WITH_ROLE.id]
    user_changes_log = AddonLog.objects.filter(
        activity_log__action__in=user_changes_actions,
        addon=addon).order_by('id')
    ctx = context(request, version=version, addon=addon,
                  pager=pager, num_pages=num_pages, count=count, flags=flags,
                  form=form, canned=canned, is_admin=is_admin,
                  show_diff=show_diff,
                  actions=actions, actions_minimal=actions_minimal,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=not addon.is_listed)

    return render(request, 'editors/review.html', ctx)
Ejemplo n.º 28
0
def review(request, addon, channel=None):
    # channel is passed in as text, but we want the constant.
    channel = amo.CHANNEL_CHOICES_LOOKUP.get(
        channel, amo.RELEASE_CHANNEL_LISTED)
    unlisted_only = (channel == amo.RELEASE_CHANNEL_UNLISTED or
                     not addon.has_listed_versions())
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    version = addon.find_latest_version(
        channel=channel, exclude=(amo.STATUS_BETA,))

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('editors.queue'))

    form_helper = ReviewHelper(request=request, addon=addon, version=version)
    form = forms.ReviewForm(request.POST or None, helper=form_helper)
    if channel == amo.RELEASE_CHANNEL_LISTED:
        queue_type = form.helper.handler.review_type
        redirect_url = reverse('editors.queue_%s' % queue_type)
    else:
        redirect_url = reverse('editors.unlisted_queue_all')

    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.user,
                                                     addon=addon)
        if form.cleaned_data.get('adminflag') and is_admin:
            addon.update(admin_review=False)
        amo.messages.success(request, _('Review successfully processed.'))
        clear_review_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since editors will almost certainly need to access
    # them. But only if we're not running in eager mode, since that could mean
    # blocking page load for several minutes.
    if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
        for file_ in version.all_files:
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        show_diff = version and (
            addon.versions.exclude(id=version.id).filter(
                channel=channel,
                files__isnull=False,
                created__lt=version.created,
                files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    versions = (Version.unfiltered.filter(addon=addon, channel=channel)
                                  .exclude(files__status=amo.STATUS_BETA)
                                  .order_by('-created')
                                  .transform(Version.transformer_activity)
                                  .transform(Version.transformer))

    # We assume comments on old deleted versions are for listed versions.
    # See _get_comments_for_hard_deleted_versions above for more detail.
    all_versions = (_get_comments_for_hard_deleted_versions(addon)
                    if channel == amo.RELEASE_CHANNEL_LISTED else [])
    all_versions.extend(versions)
    all_versions.sort(key=lambda v: v.created,
                      reverse=True)

    pager = amo.utils.paginate(request, all_versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    if version:
        version.admin_review = addon.admin_review
        version.sources_provided = bool(version.source)
        version.is_webextension = any(
            file_.is_webextension for file_ in version.all_files)
        flags = get_flags(version)
    else:
        flags = []

    user_changes_actions = [
        amo.LOG.ADD_USER_WITH_ROLE.id,
        amo.LOG.CHANGE_USER_WITH_ROLE.id,
        amo.LOG.REMOVE_USER_WITH_ROLE.id]
    user_changes_log = AddonLog.objects.filter(
        activity_log__action__in=user_changes_actions,
        addon=addon).order_by('id')
    ctx = context(request, version=version, addon=addon,
                  pager=pager, num_pages=num_pages, count=count, flags=flags,
                  form=form, canned=canned, is_admin=is_admin,
                  show_diff=show_diff,
                  actions=actions, actions_minimal=actions_minimal,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=(channel == amo.RELEASE_CHANNEL_UNLISTED))

    return render(request, 'editors/review.html', ctx)
Ejemplo n.º 29
0
 def is_reviewer(channel):
     return (acl.check_addons_reviewer(request)
             if channel == amo.RELEASE_CHANNEL_LISTED else
             acl.check_unlisted_addons_reviewer(request))
Ejemplo n.º 30
0
def review(request, addon, channel=None):
    if channel == 'content':
        # 'content' is not a real channel, just a different review mode for
        # listed add-ons.
        content_review_only = True
        channel = 'listed'
    else:
        content_review_only = False
    # channel is passed in as text, but we want the constant.
    channel = amo.CHANNEL_CHOICES_LOOKUP.get(channel,
                                             amo.RELEASE_CHANNEL_LISTED)

    if content_review_only and not acl.action_allowed(
            request, amo.permissions.ADDONS_CONTENT_REVIEW):
        raise PermissionDenied

    unlisted_only = (channel == amo.RELEASE_CHANNEL_UNLISTED
                     or not addon.has_listed_versions())
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    version = addon.find_latest_version(channel=channel,
                                        exclude=(amo.STATUS_BETA, ))

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(request,
                             ugettext('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.queue'))

    # Get the current info request state to set as the default.
    form_initial = {'info_request': version and version.has_info_request}

    form_helper = ReviewHelper(request=request,
                               addon=addon,
                               version=version,
                               content_review_only=content_review_only)
    form = forms.ReviewForm(request.POST if request.method == 'POST' else None,
                            helper=form_helper,
                            initial=form_initial)
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
    is_post_reviewer = acl.action_allowed(request,
                                          amo.permissions.ADDONS_POST_REVIEW)

    approvals_info = None
    reports = None
    user_reviews = None
    was_auto_approved = False
    if channel == amo.RELEASE_CHANNEL_LISTED:
        if addon.current_version:
            was_auto_approved = addon.current_version.was_auto_approved
        if is_post_reviewer and version and version.is_webextension:
            try:
                approvals_info = addon.addonapprovalscounter
            except AddonApprovalsCounter.DoesNotExist:
                pass

        developers = addon.listed_authors
        reports = Paginator((AbuseReport.objects.filter(
            Q(addon=addon) | Q(user__in=developers)).order_by('-created')),
                            5).page(1)
        user_reviews = Paginator((Review.without_replies.filter(
            addon=addon, rating__lte=3,
            body__isnull=False).order_by('-created')), 5).page(1)

        if content_review_only:
            queue_type = 'content_review'
        elif was_auto_approved and is_post_reviewer:
            queue_type = 'auto_approved'
        else:
            queue_type = form.helper.handler.review_type
        redirect_url = reverse('reviewers.queue_%s' % queue_type)
    else:
        redirect_url = reverse('reviewers.unlisted_queue_all')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        if form.cleaned_data.get('notify'):
            ReviewerSubscription.objects.get_or_create(user=request.user,
                                                       addon=addon)
        if form.cleaned_data.get('adminflag') and is_admin:
            addon.update(admin_review=False)
        amo.messages.success(request,
                             ugettext('Review successfully processed.'))
        clear_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since reviewers will almost certainly need to access
    # them. But only if we're not running in eager mode, since that could mean
    # blocking page load for several minutes.
    if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
        for file_ in version.all_files:
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        # Find the previously approved version to compare to.
        show_diff = version and (addon.versions.exclude(id=version.id).filter(
            # We're looking for a version that was either manually approved
            # or auto-approved but then confirmed.
            Q(autoapprovalsummary__isnull=True)
            | Q(autoapprovalsummary__verdict=amo.AUTO_APPROVED,
                autoapprovalsummary__confirmed=True)).filter(
                    channel=channel,
                    files__isnull=False,
                    created__lt=version.created,
                    files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form for.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # The actions we should show the comments form for (contrary to minimal
    # form above, it defaults to True, because most actions do need to have
    # the comments form).
    actions_comments = [k for (k, a) in actions if a.get('comments', True)]

    # The actions we should show the 'info request' checkbox for.
    actions_info_request = [
        k for (k, a) in actions if a.get('info_request', False)
    ]

    versions = (Version.unfiltered.filter(
        addon=addon,
        channel=channel).select_related('autoapprovalsummary').exclude(
            files__status=amo.STATUS_BETA).order_by('-created').transform(
                Version.transformer_activity).transform(Version.transformer))

    # We assume comments on old deleted versions are for listed versions.
    # See _get_comments_for_hard_deleted_versions above for more detail.
    all_versions = (_get_comments_for_hard_deleted_versions(addon)
                    if channel == amo.RELEASE_CHANNEL_LISTED else [])
    all_versions.extend(versions)
    all_versions.sort(key=lambda v: v.created, reverse=True)

    pager = amo.utils.paginate(request, all_versions, 10)
    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    auto_approval_info = {}
    # Now that we've paginated the versions queryset, iterate on them to
    # generate auto approvals info. Note that the variable should not clash
    # the already existing 'version'.
    for a_version in pager.object_list:
        if not is_post_reviewer or not a_version.is_ready_for_auto_approval:
            continue
        try:
            summary = a_version.autoapprovalsummary
        except AutoApprovalSummary.DoesNotExist:
            auto_approval_info[a_version.pk] = None
            continue
        # Call calculate_verdict() again, it will use the data already stored.
        verdict_info = summary.calculate_verdict(pretty=True)
        auto_approval_info[a_version.pk] = verdict_info

    if version:
        flags = get_flags(version)
    else:
        flags = []

    user_changes_actions = [
        amo.LOG.ADD_USER_WITH_ROLE.id, amo.LOG.CHANGE_USER_WITH_ROLE.id,
        amo.LOG.REMOVE_USER_WITH_ROLE.id
    ]
    user_changes_log = AddonLog.objects.filter(
        activity_log__action__in=user_changes_actions,
        addon=addon).order_by('id')
    ctx = context(request,
                  version=version,
                  addon=addon,
                  pager=pager,
                  num_pages=num_pages,
                  count=count,
                  flags=flags,
                  form=form,
                  canned=canned,
                  is_admin=is_admin,
                  show_diff=show_diff,
                  actions=actions,
                  actions_minimal=actions_minimal,
                  actions_comments=actions_comments,
                  actions_info_request=actions_info_request,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=(channel == amo.RELEASE_CHANNEL_UNLISTED),
                  approvals_info=approvals_info,
                  is_post_reviewer=is_post_reviewer,
                  auto_approval_info=auto_approval_info,
                  reports=reports,
                  user_reviews=user_reviews,
                  was_auto_approved=was_auto_approved,
                  content_review_only=content_review_only)

    return render(request, 'reviewers/review.html', ctx)