def approve_files(files_with_review_type):
    """Approve the files (and sign them).

    A file will be fully approved if:
    - it's waiting for a full review
    - it's preliminary reviewed, and its addon is waiting for a full review
    A file will be prelim approved if:
    - it's waiting for a prelim review
    """
    for file_, review_type in files_with_review_type:
        version = file_.version
        addon = version.addon
        helper = ReviewHelper(request=None, addon=addon, version=file_.version)
        # Provide the file to review/sign to the helper.
        helper.set_data({'addon_files': [file_], 'comments': u'bulk approval'})
        if review_type == 'full':
            # Already fully reviewed, or waiting for a full review.
            helper.handler.process_public()
            log.info(u'File %s (addon %s) fully reviewed', file_.pk, addon.pk)
        elif review_type == 'prelim':
            # Already prelim reviewed, or waiting for a prelim review.
            helper.handler.process_preliminary()
            log.info(u'File %s (addon %s) prelim reviewed', file_.pk, addon.pk)
        else:
            log.info(
                u'File %s (addon %s) not reviewed: '
                u'addon status: %s, file status: %s', file_.pk, addon.pk,
                addon.status, file_.status)
示例#2
0
def approve_files(files_with_review_type):
    """Approve the files (and sign them).

    A file will be fully approved if:
    - it's waiting for a full review
    - it's preliminary reviewed, and its addon is waiting for a full review
    A file will be prelim approved if:
    - it's waiting for a prelim review
    """
    for file_, review_type in files_with_review_type:
        version = file_.version
        addon = version.addon
        helper = ReviewHelper(request=None, addon=addon,
                              version=file_.version)
        # Provide the file to review/sign to the helper.
        helper.set_data({'addon_files': [file_],
                         'comments': u'bulk approval'})
        if review_type == 'full':
            # Already fully reviewed, or waiting for a full review.
            helper.handler.process_public()
            log.info(u'File %s (addon %s) fully reviewed', file_.pk, addon.pk)
        elif review_type == 'prelim':
            # Already prelim reviewed, or waiting for a prelim review.
            helper.handler.process_preliminary()
            log.info(u'File %s (addon %s) prelim reviewed', file_.pk, addon.pk)
        else:
            log.info(u'File %s (addon %s) not reviewed: '
                     u'addon status: %s, file status: %s',
                     file_.pk, addon.pk, addon.status, file_.status)
示例#3
0
 def set_status(self, status):
     self.addon.update(status=status)
     form = ReviewForm({'addon_files': [self.file.pk]},
                       helper=ReviewHelper(request=self.request,
                                           addon=self.addon,
                                           version=self.version))
     return form.helper.get_actions(self.request, self.addon)
def approve_files(files_with_review_type):
    """Approve the files waiting for review (and sign them)."""
    for file_, review_type in files_with_review_type:
        version = file_.version
        addon = version.addon
        helper = ReviewHelper(request=None, addon=addon, version=file_.version)
        # Provide the file to review/sign to the helper.
        helper.set_data({'addon_files': [file_], 'comments': u'bulk approval'})
        if review_type == 'full':
            # Already approved, or waiting for a full review.
            helper.handler.process_public()
            log.info(u'File %s (addon %s) approved', file_.pk, addon.pk)
        else:
            log.info(
                u'File %s (addon %s) not approved: '
                u'addon status: %s, file status: %s', file_.pk, addon.pk,
                addon.status, file_.status)
示例#5
0
def approve_files(files_with_review_type):
    """Approve the files waiting for review (and sign them)."""
    for file_, review_type in files_with_review_type:
        version = file_.version
        addon = version.addon
        helper = ReviewHelper(request=None, addon=addon,
                              version=file_.version)
        # Provide the file to review/sign to the helper.
        helper.set_data({'addon_files': [file_],
                         'comments': u'bulk approval'})
        if review_type == 'full':
            # Already approved, or waiting for a full review.
            helper.handler.process_public()
            log.info(u'File %s (addon %s) approved', file_.pk, addon.pk)
        else:
            log.info(u'File %s (addon %s) not approved: '
                     u'addon status: %s, file status: %s',
                     file_.pk, addon.pk, addon.status, file_.status)
示例#6
0
 def set_statuses(self, addon_status, file_status):
     self.file.update(status=file_status)
     self.addon.update(status=addon_status)
     # Need to clear self.version.all_files cache since we updated the file.
     del self.version.all_files
     form = ReviewForm(
         {'addon_files': [self.file.pk]},
         helper=ReviewHelper(request=self.request, addon=self.addon,
                             version=self.version))
     return form.helper.get_actions(self.request, self.addon)
示例#7
0
 def approve(self, version):
     """Do the approval itself, caling ReviewHelper to change the status,
     sign the files, send the e-mail, etc."""
     # Note: this should automatically use the TASK_USER_ID user.
     helper = ReviewHelper(addon=version.addon, version=version)
     helper.handler.data = {
         # The comment is not translated on purpose, to behave like regular
         # human approval does.
         'comments':
         u'This version has been approved for the public.'
         u'\r\n\r\nThank you!'
     }
     helper.handler.process_public()
示例#8
0
 def test_no_app(self):
     form = ReviewForm({'addon_files': [self.file.pk]},
                       helper=ReviewHelper(request=self.request,
                                           addon=self.addon,
                                           version=self.version))
     choices = form.fields['canned_response'].choices[1][1]
     # choices is grouped by the sort_group, where choices[0] is the
     # default "Choose a response..." option.
     # Within that, it's paired by [group, [[response, name],...]].
     # So above, choices[1][1] gets the first real group's list of
     # responses.
     eq_(len(choices), 1)
     assert self.cr_addon.response in choices[0]
     assert self.cr_app.response not in choices[0]
示例#9
0
 def test_no_app(self):
     self.set_statuses(addon_status=amo.STATUS_NOMINATED,
                       file_status=amo.STATUS_UNREVIEWED)
     form = ReviewForm({'addon_files': [self.file.pk]},
                       helper=ReviewHelper(request=self.request,
                                           addon=self.addon,
                                           version=self.version))
     choices = form.fields['canned_response'].choices[1][1]
     # choices is grouped by the sort_group, where choices[0] is the
     # default "Choose a response..." option.
     # Within that, it's paired by [group, [[response, name],...]].
     # So above, choices[1][1] gets the first real group's list of
     # responses.
     assert len(choices) == 1
     assert self.cr_addon.response in choices[0]
示例#10
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)
示例#11
0
def get_review_form(data, request=None, addon=None, version=None):
    helper = ReviewHelper(request=request, addon=addon, version=version)
    return ReviewForm(data, helper=helper)
示例#12
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)
示例#13
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, 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 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_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).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,
                  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)
示例#14
0
 def get_form(self, data=None):
     return ReviewForm(data=data,
                       helper=ReviewHelper(request=self.request,
                                           addon=self.addon,
                                           version=self.version))