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)
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))
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))
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)
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
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
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)
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)
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)
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
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
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)
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
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
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
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)
def has_permission(self, request, view): return acl.check_unlisted_addons_reviewer(request)
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))
def wrapper(request, *args, **kw): if acl.check_unlisted_addons_reviewer(request): return f(request, *args, **kw) raise PermissionDenied
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)
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 is_reviewer(channel): return (acl.check_addons_reviewer(request) if channel == amo.RELEASE_CHANNEL_LISTED else acl.check_unlisted_addons_reviewer(request))
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)
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): 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)