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)
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)
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)
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)
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)
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()
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]
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]
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)
def get_review_form(data, request=None, addon=None, version=None): helper = ReviewHelper(request=request, addon=addon, version=version) return ReviewForm(data, helper=helper)
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)
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)
def get_form(self, data=None): return ReviewForm(data=data, helper=ReviewHelper(request=self.request, addon=self.addon, version=self.version))