add a queue for decision actions that are held (#22788)
* rename utils/CinderAction* to actions/ContentAction* * CinderDecision to ContentDecison * Add a basic admin to view ContentDecisions * Add basic queue to show decisions where action has been held
This commit is contained in:
Родитель
d174685158
Коммит
6628df7d22
|
@ -28,7 +28,7 @@ POLICY_DOCUMENT_URL = (
|
|||
log = olympia.core.logger.getLogger('z.abuse')
|
||||
|
||||
|
||||
class CinderAction:
|
||||
class ContentAction:
|
||||
description = 'Action has been taken'
|
||||
valid_targets = ()
|
||||
# No reporter emails will be sent while the paths are set to None
|
||||
|
@ -83,26 +83,6 @@ class CinderAction:
|
|||
"""No owner emails will be sent. Override to send owner emails"""
|
||||
return ()
|
||||
|
||||
def get_target_name(self):
|
||||
return str(
|
||||
_('"{}" for {}').format(self.target, self.target.addon.name)
|
||||
if isinstance(self.target, Rating)
|
||||
else getattr(self.target, 'name', self.target)
|
||||
)
|
||||
|
||||
def get_target_type(self):
|
||||
match self.target:
|
||||
case target if isinstance(target, Addon):
|
||||
return target.get_type_display()
|
||||
case target if isinstance(target, UserProfile):
|
||||
return _('User profile')
|
||||
case target if isinstance(target, Collection):
|
||||
return _('Collection')
|
||||
case target if isinstance(target, Rating):
|
||||
return _('Rating')
|
||||
case target:
|
||||
return target.__class__.__name__
|
||||
|
||||
@property
|
||||
def owner_template_path(self):
|
||||
return f'abuse/emails/{self.__class__.__name__}.txt'
|
||||
|
@ -114,7 +94,7 @@ class CinderAction:
|
|||
if not owners:
|
||||
return
|
||||
template = loader.get_template(self.owner_template_path)
|
||||
target_name = self.get_target_name()
|
||||
target_name = self.decision.get_target_name()
|
||||
reference_id = f'ref:{self.decision.get_reference_id()}'
|
||||
# override target_url to devhub if there is no public listing
|
||||
target_url = (
|
||||
|
@ -134,7 +114,7 @@ class CinderAction:
|
|||
'reference_id': reference_id,
|
||||
'target': self.target,
|
||||
'target_url': target_url,
|
||||
'type': self.get_target_type(),
|
||||
'type': self.decision.get_target_type(),
|
||||
'SITE_URL': settings.SITE_URL,
|
||||
**(extra_context or {}),
|
||||
}
|
||||
|
@ -194,7 +174,7 @@ class CinderAction:
|
|||
with translation.override(
|
||||
abuse_report.application_locale or settings.LANGUAGE_CODE
|
||||
):
|
||||
target_name = self.get_target_name()
|
||||
target_name = self.decision.get_target_name()
|
||||
reference_id = (
|
||||
f'ref:{self.decision.get_reference_id()}/{abuse_report.id}'
|
||||
)
|
||||
|
@ -209,7 +189,7 @@ class CinderAction:
|
|||
'policy_document_url': POLICY_DOCUMENT_URL,
|
||||
'reference_id': reference_id,
|
||||
'target_url': absolutify(self.target.get_url_path()),
|
||||
'type': self.get_target_type(),
|
||||
'type': self.decision.get_target_type(),
|
||||
'SITE_URL': settings.SITE_URL,
|
||||
}
|
||||
if is_appeal:
|
||||
|
@ -256,7 +236,7 @@ class AnyOwnerEmailMixin:
|
|||
return [target.user]
|
||||
|
||||
|
||||
class CinderActionBanUser(CinderAction):
|
||||
class ContentActionBanUser(ContentAction):
|
||||
description = 'Account has been banned'
|
||||
valid_targets = (UserProfile,)
|
||||
reporter_template_path = 'abuse/emails/reporter_takedown_user.txt'
|
||||
|
@ -291,7 +271,7 @@ class CinderActionBanUser(CinderAction):
|
|||
return [self.target]
|
||||
|
||||
|
||||
class CinderActionDisableAddon(CinderAction):
|
||||
class ContentActionDisableAddon(ContentAction):
|
||||
description = 'Add-on has been disabled'
|
||||
valid_targets = (Addon,)
|
||||
reporter_template_path = 'abuse/emails/reporter_takedown_addon.txt'
|
||||
|
@ -319,7 +299,7 @@ class CinderActionDisableAddon(CinderAction):
|
|||
return self.target.authors.all()
|
||||
|
||||
|
||||
class CinderActionRejectVersion(CinderActionDisableAddon):
|
||||
class ContentActionRejectVersion(ContentActionDisableAddon):
|
||||
description = 'Add-on version(s) have been rejected'
|
||||
|
||||
def should_hold_action(self):
|
||||
|
@ -336,13 +316,13 @@ class CinderActionRejectVersion(CinderActionDisableAddon):
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
class CinderActionRejectVersionDelayed(CinderActionRejectVersion):
|
||||
class ContentActionRejectVersionDelayed(ContentActionRejectVersion):
|
||||
description = 'Add-on version(s) will be rejected'
|
||||
reporter_template_path = 'abuse/emails/reporter_takedown_addon_delayed.txt'
|
||||
reporter_appeal_template_path = 'abuse/emails/reporter_appeal_takedown_delayed.txt'
|
||||
|
||||
|
||||
class CinderActionEscalateAddon(CinderAction):
|
||||
class ContentActionEscalateAddon(ContentAction):
|
||||
valid_targets = (Addon,)
|
||||
|
||||
def process_action(self):
|
||||
|
@ -351,7 +331,7 @@ class CinderActionEscalateAddon(CinderAction):
|
|||
handle_escalate_action.delay(job_pk=self.decision.cinder_job.pk)
|
||||
|
||||
|
||||
class CinderActionDeleteCollection(CinderAction):
|
||||
class ContentActionDeleteCollection(ContentAction):
|
||||
valid_targets = (Collection,)
|
||||
description = 'Collection has been deleted'
|
||||
reporter_template_path = 'abuse/emails/reporter_takedown_collection.txt'
|
||||
|
@ -378,7 +358,7 @@ class CinderActionDeleteCollection(CinderAction):
|
|||
return [self.target.author]
|
||||
|
||||
|
||||
class CinderActionDeleteRating(CinderAction):
|
||||
class ContentActionDeleteRating(ContentAction):
|
||||
valid_targets = (Rating,)
|
||||
description = 'Rating has been deleted'
|
||||
reporter_template_path = 'abuse/emails/reporter_takedown_rating.txt'
|
||||
|
@ -416,7 +396,9 @@ class CinderActionDeleteRating(CinderAction):
|
|||
return [self.target.user]
|
||||
|
||||
|
||||
class CinderActionTargetAppealApprove(AnyTargetMixin, AnyOwnerEmailMixin, CinderAction):
|
||||
class ContentActionTargetAppealApprove(
|
||||
AnyTargetMixin, AnyOwnerEmailMixin, ContentAction
|
||||
):
|
||||
description = 'Reported content is within policy, after appeal'
|
||||
|
||||
def process_action(self):
|
||||
|
@ -438,18 +420,18 @@ class CinderActionTargetAppealApprove(AnyTargetMixin, AnyOwnerEmailMixin, Cinder
|
|||
return None
|
||||
|
||||
|
||||
class CinderActionOverrideApprove(CinderActionTargetAppealApprove):
|
||||
class ContentActionOverrideApprove(ContentActionTargetAppealApprove):
|
||||
description = 'Reported content is within policy, after override'
|
||||
|
||||
|
||||
class CinderActionApproveNoAction(AnyTargetMixin, NoActionMixin, CinderAction):
|
||||
class ContentActionApproveNoAction(AnyTargetMixin, NoActionMixin, ContentAction):
|
||||
description = 'Reported content is within policy, initial decision, so no action'
|
||||
reporter_template_path = 'abuse/emails/reporter_content_approve.txt'
|
||||
reporter_appeal_template_path = 'abuse/emails/reporter_appeal_approve.txt'
|
||||
|
||||
|
||||
class CinderActionApproveInitialDecision(
|
||||
AnyTargetMixin, NoActionMixin, AnyOwnerEmailMixin, CinderAction
|
||||
class ContentActionApproveInitialDecision(
|
||||
AnyTargetMixin, NoActionMixin, AnyOwnerEmailMixin, ContentAction
|
||||
):
|
||||
description = (
|
||||
'Reported content is within policy, initial decision, approving versions'
|
||||
|
@ -458,23 +440,23 @@ class CinderActionApproveInitialDecision(
|
|||
reporter_appeal_template_path = 'abuse/emails/reporter_appeal_approve.txt'
|
||||
|
||||
|
||||
class CinderActionTargetAppealRemovalAffirmation(
|
||||
AnyTargetMixin, NoActionMixin, AnyOwnerEmailMixin, CinderAction
|
||||
class ContentActionTargetAppealRemovalAffirmation(
|
||||
AnyTargetMixin, NoActionMixin, AnyOwnerEmailMixin, ContentAction
|
||||
):
|
||||
description = 'Reported content is still offending, after appeal.'
|
||||
|
||||
|
||||
class CinderActionIgnore(AnyTargetMixin, NoActionMixin, CinderAction):
|
||||
class ContentActionIgnore(AnyTargetMixin, NoActionMixin, ContentAction):
|
||||
description = 'Report is invalid, so no action'
|
||||
reporter_template_path = 'abuse/emails/reporter_invalid_ignore.txt'
|
||||
# no appeal template because no appeals possible
|
||||
|
||||
|
||||
class CinderActionAlreadyRemoved(AnyTargetMixin, NoActionMixin, CinderAction):
|
||||
class ContentActionAlreadyRemoved(AnyTargetMixin, NoActionMixin, ContentAction):
|
||||
description = 'Content is already disabled or deleted, so no action'
|
||||
reporter_template_path = 'abuse/emails/reporter_disabled_ignore.txt'
|
||||
# no appeal template because no appeals possible
|
||||
|
||||
|
||||
class CinderActionNotImplemented(NoActionMixin, CinderAction):
|
||||
class ContentActionNotImplemented(NoActionMixin, ContentAction):
|
||||
pass
|
|
@ -19,7 +19,7 @@ from olympia.amo.admin import AMOModelAdmin, DateRangeFilter, FakeChoicesMixin
|
|||
from olympia.ratings.models import Rating
|
||||
from olympia.translations.utils import truncate_text
|
||||
|
||||
from .models import AbuseReport, CinderPolicy
|
||||
from .models import AbuseReport, CinderPolicy, ContentDecision
|
||||
from .tasks import sync_cinder_policies
|
||||
|
||||
|
||||
|
@ -448,5 +448,46 @@ class CinderPolicyAdmin(AMOModelAdmin):
|
|||
return HttpResponseRedirect(reverse('admin:abuse_cinderpolicy_changelist'))
|
||||
|
||||
|
||||
class ContentDecisionAdmin(AMOModelAdmin):
|
||||
fields = (
|
||||
'id',
|
||||
'created',
|
||||
'modified',
|
||||
'addon',
|
||||
'user',
|
||||
'rating',
|
||||
'collection',
|
||||
'action',
|
||||
'action_date',
|
||||
'cinder_id',
|
||||
'notes',
|
||||
'policies',
|
||||
'appeal_job',
|
||||
)
|
||||
list_display = (
|
||||
'created',
|
||||
'action',
|
||||
'addon',
|
||||
'user',
|
||||
'rating',
|
||||
'collection',
|
||||
)
|
||||
readonly_fields = fields
|
||||
view_on_site = False
|
||||
|
||||
def has_add_permission(self, request):
|
||||
# Adding new decisions through the admin is useless, so we prevent it.
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
# Decisions shouldn't be deleted - if they're wrong, they should be overridden.
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
# Decisions can't be changed - if they're wrong, they should be overridden.
|
||||
return False
|
||||
|
||||
|
||||
admin.site.register(AbuseReport, AbuseReportAdmin)
|
||||
admin.site.register(CinderPolicy, CinderPolicyAdmin)
|
||||
admin.site.register(ContentDecision, ContentDecisionAdmin)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
import olympia.core.logger
|
||||
from olympia.abuse.models import CinderDecision
|
||||
from olympia.abuse.models import ContentDecision
|
||||
from olympia.abuse.tasks import handle_escalate_action
|
||||
from olympia.constants.abuse import DECISION_ACTIONS
|
||||
|
||||
|
@ -10,7 +10,7 @@ class Command(BaseCommand):
|
|||
log = olympia.core.logger.getLogger('z.abuse')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
qs = CinderDecision.objects.filter(
|
||||
qs = ContentDecision.objects.filter(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON,
|
||||
cinder_job__forwarded_to_job__isnull=True,
|
||||
cinder_job__isnull=False,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-21 13:44
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('abuse', '0041_alter_decision_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelTable(
|
||||
name='cinderdecision',
|
||||
table='abuse_cinderdecision',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-21 13:48
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('addons', '0052_auto_20240927_1810'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('bandwagon', '0008_alter_collection_options_alter_collection_managers_and_more'),
|
||||
('ratings', '0011_remove_rating_one_review_per_user_and_more'),
|
||||
('abuse', '0042_alter_cinderdecision_table'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='CinderDecision',
|
||||
new_name='ContentDecision',
|
||||
),
|
||||
]
|
|
@ -5,7 +5,9 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.db import models
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.db.transaction import atomic
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from olympia import amo
|
||||
from olympia.addons.models import Addon
|
||||
|
@ -23,6 +25,23 @@ from olympia.ratings.models import Rating
|
|||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import Version, VersionReviewerFlags
|
||||
|
||||
from .actions import (
|
||||
ContentActionAlreadyRemoved,
|
||||
ContentActionApproveInitialDecision,
|
||||
ContentActionApproveNoAction,
|
||||
ContentActionBanUser,
|
||||
ContentActionDeleteCollection,
|
||||
ContentActionDeleteRating,
|
||||
ContentActionDisableAddon,
|
||||
ContentActionEscalateAddon,
|
||||
ContentActionIgnore,
|
||||
ContentActionNotImplemented,
|
||||
ContentActionOverrideApprove,
|
||||
ContentActionRejectVersion,
|
||||
ContentActionRejectVersionDelayed,
|
||||
ContentActionTargetAppealApprove,
|
||||
ContentActionTargetAppealRemovalAffirmation,
|
||||
)
|
||||
from .cinder import (
|
||||
CinderAddon,
|
||||
CinderAddonHandledByReviewers,
|
||||
|
@ -32,23 +51,6 @@ from .cinder import (
|
|||
CinderUnauthenticatedReporter,
|
||||
CinderUser,
|
||||
)
|
||||
from .utils import (
|
||||
CinderActionAlreadyRemoved,
|
||||
CinderActionApproveInitialDecision,
|
||||
CinderActionApproveNoAction,
|
||||
CinderActionBanUser,
|
||||
CinderActionDeleteCollection,
|
||||
CinderActionDeleteRating,
|
||||
CinderActionDisableAddon,
|
||||
CinderActionEscalateAddon,
|
||||
CinderActionIgnore,
|
||||
CinderActionNotImplemented,
|
||||
CinderActionOverrideApprove,
|
||||
CinderActionRejectVersion,
|
||||
CinderActionRejectVersionDelayed,
|
||||
CinderActionTargetAppealApprove,
|
||||
CinderActionTargetAppealRemovalAffirmation,
|
||||
)
|
||||
|
||||
|
||||
class CinderJobQuerySet(BaseQuerySet):
|
||||
|
@ -81,7 +83,7 @@ class CinderJob(ModelBase):
|
|||
to=Addon, blank=True, null=True, on_delete=models.deletion.SET_NULL
|
||||
)
|
||||
decision = models.OneToOneField(
|
||||
to='abuse.CinderDecision',
|
||||
to='abuse.ContentDecision',
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='cinder_job',
|
||||
|
@ -157,7 +159,7 @@ class CinderJob(ModelBase):
|
|||
|
||||
@classmethod
|
||||
def handle_already_removed(cls, abuse_report):
|
||||
decision = CinderDecision(
|
||||
decision = ContentDecision(
|
||||
addon=abuse_report.addon,
|
||||
rating=abuse_report.rating,
|
||||
collection=abuse_report.collection,
|
||||
|
@ -270,7 +272,7 @@ class CinderJob(ModelBase):
|
|||
new_job.target_addon.update_all_due_dates()
|
||||
# Update our fks to connected objects
|
||||
AbuseReport.objects.filter(cinder_job=self).update(cinder_job=new_job)
|
||||
CinderDecision.objects.filter(appeal_job=self).update(appeal_job=new_job)
|
||||
ContentDecision.objects.filter(appeal_job=self).update(appeal_job=new_job)
|
||||
self.update(forwarded_to_job=new_job)
|
||||
|
||||
def process_decision(
|
||||
|
@ -284,11 +286,11 @@ class CinderJob(ModelBase):
|
|||
"""This is called for cinder originated decisions.
|
||||
See resolve_job for reviewer tools originated decisions."""
|
||||
overridden_action = getattr(self.decision, 'action', None)
|
||||
# We need either an AbuseReport or CinderDecision for the target props
|
||||
# We need either an AbuseReport or ContentDecision for the target props
|
||||
abuse_report_or_decision = (
|
||||
self.appealed_decisions.first() or self.abusereport_set.first()
|
||||
)
|
||||
cinder_decision, _ = CinderDecision.objects.update_or_create(
|
||||
cinder_decision, _ = ContentDecision.objects.update_or_create(
|
||||
cinder_job=self,
|
||||
defaults={
|
||||
'addon': (
|
||||
|
@ -302,7 +304,7 @@ class CinderJob(ModelBase):
|
|||
'cinder_id': decision_cinder_id,
|
||||
'action': decision_action,
|
||||
'notes': decision_notes[
|
||||
: CinderDecision._meta.get_field('notes').max_length
|
||||
: ContentDecision._meta.get_field('notes').max_length
|
||||
],
|
||||
},
|
||||
)
|
||||
|
@ -327,7 +329,7 @@ class CinderJob(ModelBase):
|
|||
resolved_in_reviewer_tools=self.resolvable_in_reviewer_tools,
|
||||
)
|
||||
|
||||
cinder_decision = self.decision or CinderDecision(
|
||||
cinder_decision = self.decision or ContentDecision(
|
||||
addon=abuse_report_or_decision.addon,
|
||||
rating=abuse_report_or_decision.rating,
|
||||
collection=abuse_report_or_decision.collection,
|
||||
|
@ -908,7 +910,7 @@ class CinderPolicy(ModelBase):
|
|||
verbose_name_plural = 'Cinder Policies'
|
||||
|
||||
|
||||
class CinderDecision(ModelBase):
|
||||
class ContentDecision(ModelBase):
|
||||
action = models.PositiveSmallIntegerField(choices=DECISION_ACTIONS.choices)
|
||||
cinder_id = models.CharField(max_length=36, default=None, null=True, unique=True)
|
||||
action_date = models.DateTimeField(null=True, db_column='date')
|
||||
|
@ -928,6 +930,7 @@ class CinderDecision(ModelBase):
|
|||
collection = models.ForeignKey(Collection, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
class Meta:
|
||||
db_table = 'abuse_cinderdecision'
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
name='just_one_of_addon_user_rating_collection_must_be_set',
|
||||
|
@ -998,24 +1001,24 @@ class CinderDecision(ModelBase):
|
|||
@classmethod
|
||||
def get_action_helper_class(cls, decision_action):
|
||||
return {
|
||||
DECISION_ACTIONS.AMO_BAN_USER: CinderActionBanUser,
|
||||
DECISION_ACTIONS.AMO_DISABLE_ADDON: CinderActionDisableAddon,
|
||||
DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON: CinderActionRejectVersion,
|
||||
DECISION_ACTIONS.AMO_BAN_USER: ContentActionBanUser,
|
||||
DECISION_ACTIONS.AMO_DISABLE_ADDON: ContentActionDisableAddon,
|
||||
DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON: ContentActionRejectVersion,
|
||||
DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON: (
|
||||
CinderActionRejectVersionDelayed
|
||||
ContentActionRejectVersionDelayed
|
||||
),
|
||||
DECISION_ACTIONS.AMO_ESCALATE_ADDON: CinderActionEscalateAddon,
|
||||
DECISION_ACTIONS.AMO_DELETE_COLLECTION: CinderActionDeleteCollection,
|
||||
DECISION_ACTIONS.AMO_DELETE_RATING: CinderActionDeleteRating,
|
||||
DECISION_ACTIONS.AMO_APPROVE: CinderActionApproveNoAction,
|
||||
DECISION_ACTIONS.AMO_APPROVE_VERSION: CinderActionApproveInitialDecision,
|
||||
DECISION_ACTIONS.AMO_IGNORE: CinderActionIgnore,
|
||||
DECISION_ACTIONS.AMO_CLOSED_NO_ACTION: CinderActionAlreadyRemoved,
|
||||
}.get(decision_action, CinderActionNotImplemented)
|
||||
DECISION_ACTIONS.AMO_ESCALATE_ADDON: ContentActionEscalateAddon,
|
||||
DECISION_ACTIONS.AMO_DELETE_COLLECTION: ContentActionDeleteCollection,
|
||||
DECISION_ACTIONS.AMO_DELETE_RATING: ContentActionDeleteRating,
|
||||
DECISION_ACTIONS.AMO_APPROVE: ContentActionApproveNoAction,
|
||||
DECISION_ACTIONS.AMO_APPROVE_VERSION: ContentActionApproveInitialDecision,
|
||||
DECISION_ACTIONS.AMO_IGNORE: ContentActionIgnore,
|
||||
DECISION_ACTIONS.AMO_CLOSED_NO_ACTION: ContentActionAlreadyRemoved,
|
||||
}.get(decision_action, ContentActionNotImplemented)
|
||||
|
||||
def get_action_helper(self, *, overridden_action=None, appealed_action=None):
|
||||
# Base case when it's a new decision, that wasn't an appeal
|
||||
CinderActionClass = self.get_action_helper_class(self.action)
|
||||
ContentActionClass = self.get_action_helper_class(self.action)
|
||||
skip_reporter_notify = False
|
||||
|
||||
if appealed_action:
|
||||
|
@ -1023,22 +1026,22 @@ class CinderDecision(ModelBase):
|
|||
if appealed_action in DECISION_ACTIONS.REMOVING:
|
||||
if self.action in DECISION_ACTIONS.APPROVING:
|
||||
# i.e. we've reversed our target takedown
|
||||
CinderActionClass = CinderActionTargetAppealApprove
|
||||
ContentActionClass = ContentActionTargetAppealApprove
|
||||
elif self.action == appealed_action:
|
||||
# i.e. we've not reversed our target takedown
|
||||
CinderActionClass = CinderActionTargetAppealRemovalAffirmation
|
||||
# (a reporter appeal doesn't need any alternate CinderAction class)
|
||||
ContentActionClass = ContentActionTargetAppealRemovalAffirmation
|
||||
# (a reporter appeal doesn't need any alternate ContentAction class)
|
||||
|
||||
elif overridden_action in DECISION_ACTIONS.REMOVING:
|
||||
# override on a decision that was a takedown before, and wasn't an appeal
|
||||
if self.action in DECISION_ACTIONS.APPROVING:
|
||||
CinderActionClass = CinderActionOverrideApprove
|
||||
ContentActionClass = ContentActionOverrideApprove
|
||||
if self.action == overridden_action:
|
||||
# For an override that is still a takedown we can send the same emails
|
||||
# to the target; but we don't want to notify the reporter again.
|
||||
skip_reporter_notify = True
|
||||
|
||||
cinder_action = CinderActionClass(decision=self)
|
||||
cinder_action = ContentActionClass(decision=self)
|
||||
if skip_reporter_notify:
|
||||
cinder_action.reporter_template_path = None
|
||||
cinder_action.reporter_appeal_template_path = None
|
||||
|
@ -1106,7 +1109,7 @@ class CinderDecision(ModelBase):
|
|||
if is_reporter:
|
||||
if not abuse_report:
|
||||
raise ImproperlyConfigured(
|
||||
'CinderDecision.appeal() called with is_reporter=True without an '
|
||||
'ContentDecision.appeal() called with is_reporter=True without an '
|
||||
'abuse_report'
|
||||
)
|
||||
if not user:
|
||||
|
@ -1123,7 +1126,8 @@ class CinderDecision(ModelBase):
|
|||
# If we still don't have a user at this point there is nothing
|
||||
# we can do, something was wrong in the call chain.
|
||||
raise ImproperlyConfigured(
|
||||
'CinderDecision.appeal() called with is_reporter=False without user'
|
||||
'ContentDecision.appeal() called with is_reporter=False without '
|
||||
'user'
|
||||
)
|
||||
if user:
|
||||
appealer_entity = CinderUser(user)
|
||||
|
@ -1264,11 +1268,38 @@ class CinderDecision(ModelBase):
|
|||
else:
|
||||
action_helper.hold_action()
|
||||
|
||||
def get_target_review_url(self):
|
||||
return (
|
||||
reverse('reviewers.review', args=(self.target.id,))
|
||||
if isinstance(self.target, Addon)
|
||||
else ''
|
||||
)
|
||||
|
||||
def get_target_type(self):
|
||||
match self.target:
|
||||
case target if isinstance(target, Addon):
|
||||
return target.get_type_display()
|
||||
case target if isinstance(target, UserProfile):
|
||||
return _('User profile')
|
||||
case target if isinstance(target, Collection):
|
||||
return _('Collection')
|
||||
case target if isinstance(target, Rating):
|
||||
return _('Rating')
|
||||
case target:
|
||||
return target.__class__.__name__
|
||||
|
||||
def get_target_name(self):
|
||||
return str(
|
||||
_('"{}" for {}').format(self.target, self.target.addon.name)
|
||||
if isinstance(self.target, Rating)
|
||||
else getattr(self.target, 'name', self.target)
|
||||
)
|
||||
|
||||
|
||||
class CinderAppeal(ModelBase):
|
||||
text = models.TextField(blank=False, help_text='The content of the appeal.')
|
||||
decision = models.ForeignKey(
|
||||
to=CinderDecision, on_delete=models.CASCADE, related_name='appeals'
|
||||
to=ContentDecision, on_delete=models.CASCADE, related_name='appeals'
|
||||
)
|
||||
reporter_report = models.OneToOneField(
|
||||
to=AbuseReport, on_delete=models.CASCADE, null=True
|
||||
|
|
|
@ -19,9 +19,9 @@ from olympia.users.models import UserProfile
|
|||
from .models import (
|
||||
AbuseReport,
|
||||
AbuseReportManager,
|
||||
CinderDecision,
|
||||
CinderJob,
|
||||
CinderPolicy,
|
||||
ContentDecision,
|
||||
)
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ def appeal_to_cinder(
|
|||
*, decision_cinder_id, abuse_report_id, appeal_text, user_id, is_reporter
|
||||
):
|
||||
try:
|
||||
decision = CinderDecision.objects.get(cinder_id=decision_cinder_id)
|
||||
decision = ContentDecision.objects.get(cinder_id=decision_cinder_id)
|
||||
if abuse_report_id:
|
||||
abuse_report = AbuseReport.objects.get(id=abuse_report_id)
|
||||
else:
|
||||
|
@ -137,7 +137,7 @@ def notify_addon_decision_to_cinder(*, log_entry_id, addon_id=None):
|
|||
try:
|
||||
log_entry = ActivityLog.objects.get(id=log_entry_id)
|
||||
addon = Addon.unfiltered.get(id=addon_id)
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
decision.notify_reviewer_decision(
|
||||
log_entry=log_entry,
|
||||
entity_helper=CinderJob.get_entity_helper(
|
||||
|
@ -190,7 +190,7 @@ def sync_cinder_policies():
|
|||
data = response.json()
|
||||
sync_policies(data)
|
||||
CinderPolicy.objects.exclude(
|
||||
Q(cinderdecision__id__gte=0)
|
||||
Q(contentdecision__id__gte=0)
|
||||
| Q(reviewactionreason__id__gte=0)
|
||||
| Q(modified__gte=now)
|
||||
).delete()
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{% include "abuse/emails/CinderActionTargetAppealApprove.txt" with is_override=True %}
|
|
@ -0,0 +1 @@
|
|||
{% include "abuse/emails/ContentActionTargetAppealApprove.txt" with is_override=True %}
|
|
@ -11,7 +11,7 @@ import waffle
|
|||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo, core
|
||||
from olympia.abuse.models import AbuseReport, CinderDecision, CinderJob
|
||||
from olympia.abuse.models import AbuseReport, CinderJob, ContentDecision
|
||||
from olympia.activity.models import ActivityLog
|
||||
from olympia.addons.models import Addon, Preview
|
||||
from olympia.amo.tests import (
|
||||
|
@ -1225,7 +1225,7 @@ class TestCinderAddonHandledByReviewers(TestCinderAddon):
|
|||
addon.current_version.file.update(is_signed=True)
|
||||
job = CinderJob.objects.create(job_id='1234-xyz')
|
||||
job.appealed_decisions.add(
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
addon=addon,
|
||||
cinder_id='1234-decision',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON,
|
||||
|
@ -1246,7 +1246,7 @@ class TestCinderAddonHandledByReviewers(TestCinderAddon):
|
|||
job = CinderJob.objects.create(job_id='1234-xyz')
|
||||
CinderJob.objects.create(forwarded_to_job=job)
|
||||
job.appealed_decisions.add(
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
addon=addon,
|
||||
cinder_id='1234-decision',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON,
|
||||
|
|
|
@ -7,7 +7,7 @@ import responses
|
|||
from olympia.amo.tests import addon_factory
|
||||
from olympia.constants.abuse import DECISION_ACTIONS
|
||||
|
||||
from ..models import AbuseReport, CinderDecision, CinderJob
|
||||
from ..models import AbuseReport, CinderJob, ContentDecision
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -15,18 +15,18 @@ def test_backfill_cinder_escalations():
|
|||
addon = addon_factory()
|
||||
job_with_reports = CinderJob.objects.create(
|
||||
job_id='1',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon
|
||||
),
|
||||
)
|
||||
abuse = AbuseReport.objects.create(guid=addon.guid, cinder_job=job_with_reports)
|
||||
appeal_job = CinderJob.objects.create(
|
||||
job_id='2',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon
|
||||
),
|
||||
)
|
||||
appealled_decision = CinderDecision.objects.create(
|
||||
appealled_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon, appeal_job=appeal_job
|
||||
)
|
||||
|
||||
|
@ -34,24 +34,24 @@ def test_backfill_cinder_escalations():
|
|||
# decision that wasn't an escalation (or isn't any longer)
|
||||
CinderJob.objects.create(
|
||||
job_id='3',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
),
|
||||
)
|
||||
# decision without an associated cinder job (shouldn't occur, but its handled)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon
|
||||
)
|
||||
# decision that already has a forwarded job created, so we don't need to backfill
|
||||
CinderJob.objects.create(
|
||||
job_id='4',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon
|
||||
),
|
||||
forwarded_to_job=CinderJob.objects.create(job_id='5'),
|
||||
)
|
||||
assert CinderJob.objects.count() == 5
|
||||
assert CinderDecision.objects.count() == 6
|
||||
assert ContentDecision.objects.count() == 6
|
||||
responses.add(
|
||||
responses.POST,
|
||||
f'{settings.CINDER_SERVER_URL}create_report',
|
||||
|
@ -67,7 +67,7 @@ def test_backfill_cinder_escalations():
|
|||
|
||||
call_command('backfill_cinder_escalations')
|
||||
assert CinderJob.objects.count() == 7
|
||||
assert CinderDecision.objects.count() == 6
|
||||
assert ContentDecision.objects.count() == 6
|
||||
|
||||
new_job_with_reports = job_with_reports.reload().forwarded_to_job
|
||||
assert new_job_with_reports
|
||||
|
|
|
@ -9,6 +9,7 @@ from django.core import mail
|
|||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.utils import IntegrityError
|
||||
from django.urls import reverse
|
||||
|
||||
import pytest
|
||||
import responses
|
||||
|
@ -37,6 +38,22 @@ from olympia.ratings.models import Rating
|
|||
from olympia.reviewers.models import NeedsHumanReview
|
||||
from olympia.versions.models import Version, VersionReviewerFlags
|
||||
|
||||
from ..actions import (
|
||||
ContentActionAlreadyRemoved,
|
||||
ContentActionApproveInitialDecision,
|
||||
ContentActionApproveNoAction,
|
||||
ContentActionBanUser,
|
||||
ContentActionDeleteCollection,
|
||||
ContentActionDeleteRating,
|
||||
ContentActionDisableAddon,
|
||||
ContentActionEscalateAddon,
|
||||
ContentActionIgnore,
|
||||
ContentActionOverrideApprove,
|
||||
ContentActionRejectVersion,
|
||||
ContentActionRejectVersionDelayed,
|
||||
ContentActionTargetAppealApprove,
|
||||
ContentActionTargetAppealRemovalAffirmation,
|
||||
)
|
||||
from ..cinder import (
|
||||
CinderAddon,
|
||||
CinderAddonHandledByReviewers,
|
||||
|
@ -49,25 +66,9 @@ from ..models import (
|
|||
AbuseReport,
|
||||
AbuseReportManager,
|
||||
CinderAppeal,
|
||||
CinderDecision,
|
||||
CinderJob,
|
||||
CinderPolicy,
|
||||
)
|
||||
from ..utils import (
|
||||
CinderActionAlreadyRemoved,
|
||||
CinderActionApproveInitialDecision,
|
||||
CinderActionApproveNoAction,
|
||||
CinderActionBanUser,
|
||||
CinderActionDeleteCollection,
|
||||
CinderActionDeleteRating,
|
||||
CinderActionDisableAddon,
|
||||
CinderActionEscalateAddon,
|
||||
CinderActionIgnore,
|
||||
CinderActionOverrideApprove,
|
||||
CinderActionRejectVersion,
|
||||
CinderActionRejectVersionDelayed,
|
||||
CinderActionTargetAppealApprove,
|
||||
CinderActionTargetAppealRemovalAffirmation,
|
||||
ContentDecision,
|
||||
)
|
||||
|
||||
|
||||
|
@ -710,7 +711,7 @@ class TestCinderJobManager(TestCase):
|
|||
appeal_job = CinderJob.objects.create(job_id='appeal', target_addon=addon)
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
appeal_job=appeal_job,
|
||||
addon=addon,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -725,7 +726,7 @@ class TestCinderJobManager(TestCase):
|
|||
addon = addon_factory()
|
||||
CinderJob.objects.create(
|
||||
job_id='2',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon
|
||||
),
|
||||
)
|
||||
|
@ -741,7 +742,7 @@ class TestCinderJobManager(TestCase):
|
|||
)
|
||||
job = CinderJob.objects.create(
|
||||
job_id=2,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon_factory()
|
||||
),
|
||||
)
|
||||
|
@ -793,7 +794,7 @@ class TestCinderJob(TestCase):
|
|||
# case when there is already a decision
|
||||
cinder_job.update(
|
||||
target_addon=None,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
),
|
||||
)
|
||||
|
@ -979,7 +980,7 @@ class TestCinderJob(TestCase):
|
|||
assert len(mail.outbox) == 0
|
||||
addon = Addon.objects.get()
|
||||
CinderJob.objects.get().update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON, addon=addon
|
||||
)
|
||||
)
|
||||
|
@ -1029,7 +1030,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
def test_handle_job_recreated(self):
|
||||
addon = addon_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon, notes='blah'
|
||||
)
|
||||
job = CinderJob.objects.create(
|
||||
|
@ -1057,7 +1058,7 @@ class TestCinderJob(TestCase):
|
|||
job_id='9999', target_addon=addon, forwarded_to_job=exisiting_escalation_job
|
||||
)
|
||||
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon, notes='blah'
|
||||
)
|
||||
old_job = CinderJob.objects.create(
|
||||
|
@ -1100,7 +1101,7 @@ class TestCinderJob(TestCase):
|
|||
guid=addon.guid, cinder_job=exisiting_report_job
|
||||
)
|
||||
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon, notes='blah'
|
||||
)
|
||||
old_job = CinderJob.objects.create(
|
||||
|
@ -1138,7 +1139,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
def test_handle_job_recreated_appeal(self):
|
||||
addon = addon_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon, notes='blah'
|
||||
)
|
||||
appeal_job = CinderJob.objects.create(
|
||||
|
@ -1147,7 +1148,7 @@ class TestCinderJob(TestCase):
|
|||
original_job = CinderJob.objects.create(
|
||||
job_id='0000',
|
||||
target_addon=addon,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=addon,
|
||||
notes='its okay',
|
||||
|
@ -1178,9 +1179,9 @@ class TestCinderJob(TestCase):
|
|||
policy_b = CinderPolicy.objects.create(uuid='678-90', name='bbb', text='BBB')
|
||||
|
||||
with mock.patch.object(
|
||||
CinderActionBanUser, 'process_action'
|
||||
ContentActionBanUser, 'process_action'
|
||||
) as action_mock, mock.patch.object(
|
||||
CinderActionBanUser, 'notify_owners'
|
||||
ContentActionBanUser, 'notify_owners'
|
||||
) as notify_mock:
|
||||
action_mock.return_value = (True, mock.Mock(id=999))
|
||||
cinder_job.process_decision(
|
||||
|
@ -1209,9 +1210,9 @@ class TestCinderJob(TestCase):
|
|||
)
|
||||
|
||||
with mock.patch.object(
|
||||
CinderActionBanUser, 'process_action'
|
||||
ContentActionBanUser, 'process_action'
|
||||
) as action_mock, mock.patch.object(
|
||||
CinderActionBanUser, 'notify_owners'
|
||||
ContentActionBanUser, 'notify_owners'
|
||||
) as notify_mock:
|
||||
action_mock.return_value = (True, None)
|
||||
cinder_job.process_decision(
|
||||
|
@ -1420,7 +1421,7 @@ class TestCinderJob(TestCase):
|
|||
)
|
||||
CinderJob.objects.create(
|
||||
job_id='998',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_APPROVE, appeal_job=appeal_job
|
||||
),
|
||||
)
|
||||
|
@ -1490,7 +1491,7 @@ class TestCinderJob(TestCase):
|
|||
)
|
||||
CinderJob.objects.create(
|
||||
job_id='998',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_APPROVE, appeal_job=appeal_job
|
||||
),
|
||||
)
|
||||
|
@ -1622,7 +1623,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
appeal_job = CinderJob.objects.create(job_id='fake_appeal_job_id')
|
||||
job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=addon,
|
||||
appeal_job=appeal_job,
|
||||
|
@ -1634,7 +1635,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
appeal_appeal_job = CinderJob.objects.create(job_id='fake_appeal_appeal_job_id')
|
||||
appeal_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=addon,
|
||||
appeal_job=appeal_appeal_job,
|
||||
|
@ -1664,7 +1665,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
appeal = CinderJob.objects.create(job_id='an appeal job')
|
||||
job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=addon_factory(),
|
||||
appeal_job=appeal,
|
||||
|
@ -1685,7 +1686,7 @@ class TestCinderJob(TestCase):
|
|||
job_id='1',
|
||||
target_addon=addon,
|
||||
resolvable_in_reviewer_tools=True,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
),
|
||||
)
|
||||
|
@ -1729,7 +1730,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
# unless the other job is closed too
|
||||
other_forward.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
)
|
||||
)
|
||||
|
@ -1746,7 +1747,7 @@ class TestCinderJob(TestCase):
|
|||
CinderJob.objects.create(
|
||||
job_id='5',
|
||||
target_addon=addon,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon, appeal_job=job
|
||||
),
|
||||
)
|
||||
|
@ -1759,7 +1760,7 @@ class TestCinderJob(TestCase):
|
|||
CinderJob.objects.create(
|
||||
job_id='7',
|
||||
target_addon=addon,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=addon,
|
||||
appeal_job=other_appeal,
|
||||
|
@ -1772,7 +1773,7 @@ class TestCinderJob(TestCase):
|
|||
|
||||
# unless the other job is closed too
|
||||
other_appeal.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
)
|
||||
)
|
||||
|
@ -1782,12 +1783,12 @@ class TestCinderJob(TestCase):
|
|||
assert not nhr_exists(NeedsHumanReview.REASONS.ADDON_REVIEW_APPEAL)
|
||||
|
||||
|
||||
class TestCinderDecisionCanBeAppealed(TestCase):
|
||||
class TestContentDecisionCanBeAppealed(TestCase):
|
||||
def setUp(self):
|
||||
self.reporter = user_factory()
|
||||
self.author = user_factory()
|
||||
self.addon = addon_factory(users=[self.author])
|
||||
self.decision = CinderDecision.objects.create(
|
||||
self.decision = ContentDecision.objects.create(
|
||||
cinder_id='fake_decision_id',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -1804,7 +1805,7 @@ class TestCinderDecisionCanBeAppealed(TestCase):
|
|||
assert not self.decision.appealed_decision_already_made()
|
||||
|
||||
appeal_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='appeal decision id',
|
||||
addon=self.addon,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -1907,7 +1908,7 @@ class TestCinderDecisionCanBeAppealed(TestCase):
|
|||
)
|
||||
appeal_job = CinderJob.objects.create(
|
||||
job_id='fake_appeal_job_id',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='fake_appeal_decision_id',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -1928,7 +1929,7 @@ class TestCinderDecisionCanBeAppealed(TestCase):
|
|||
def test_reporter_can_appeal_appealed_decision(self):
|
||||
appeal_job = CinderJob.objects.create(
|
||||
job_id='fake_appeal_job_id',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='fake_appeal_decision_id',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -2028,7 +2029,7 @@ class TestCinderDecisionCanBeAppealed(TestCase):
|
|||
def test_author_can_appeal_appealed_decision(self):
|
||||
appeal_job = CinderJob.objects.create(
|
||||
job_id='fake_appeal_job_id',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='fake_appeal_decision_id',
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -2159,7 +2160,7 @@ class TestCinderPolicy(TestCase):
|
|||
|
||||
@override_switch('dsa-abuse-reports-review', active=True)
|
||||
@override_switch('dsa-appeals-review', active=True)
|
||||
class TestCinderDecision(TestCase):
|
||||
class TestContentDecision(TestCase):
|
||||
def setUp(self):
|
||||
# It's the webhook's responsibility to do this before calling the
|
||||
# action. We need it for the ActivityLog creation to work.
|
||||
|
@ -2167,7 +2168,7 @@ class TestCinderDecision(TestCase):
|
|||
set_user(self.task_user)
|
||||
|
||||
def test_get_reference_id(self):
|
||||
decision = CinderDecision()
|
||||
decision = ContentDecision()
|
||||
assert decision.get_reference_id() == 'NoClass#None'
|
||||
assert decision.get_reference_id(short=False) == 'Decision "" for NoClass #None'
|
||||
|
||||
|
@ -2187,7 +2188,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_target(self):
|
||||
addon = addon_factory(guid='@lol')
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon
|
||||
)
|
||||
assert decision.target == addon
|
||||
|
@ -2206,7 +2207,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_is_third_party_initiated(self):
|
||||
addon = addon_factory()
|
||||
current_decision = CinderDecision.objects.create(
|
||||
current_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon
|
||||
)
|
||||
assert not current_decision.is_third_party_initiated
|
||||
|
@ -2223,7 +2224,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_is_third_party_initiated_appeal(self):
|
||||
addon = addon_factory()
|
||||
current_decision = CinderDecision.objects.create(
|
||||
current_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=addon,
|
||||
)
|
||||
|
@ -2232,7 +2233,7 @@ class TestCinderDecision(TestCase):
|
|||
)
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='456',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon, appeal_job=current_job
|
||||
),
|
||||
)
|
||||
|
@ -2243,29 +2244,29 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_get_action_helper(self):
|
||||
addon = addon_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon
|
||||
)
|
||||
targets = {
|
||||
CinderActionBanUser: {'user': user_factory()},
|
||||
CinderActionDisableAddon: {'addon': addon},
|
||||
CinderActionRejectVersion: {'addon': addon},
|
||||
CinderActionRejectVersionDelayed: {'addon': addon},
|
||||
CinderActionEscalateAddon: {'addon': addon},
|
||||
CinderActionDeleteCollection: {'collection': collection_factory()},
|
||||
CinderActionDeleteRating: {
|
||||
ContentActionBanUser: {'user': user_factory()},
|
||||
ContentActionDisableAddon: {'addon': addon},
|
||||
ContentActionRejectVersion: {'addon': addon},
|
||||
ContentActionRejectVersionDelayed: {'addon': addon},
|
||||
ContentActionEscalateAddon: {'addon': addon},
|
||||
ContentActionDeleteCollection: {'collection': collection_factory()},
|
||||
ContentActionDeleteRating: {
|
||||
'rating': Rating.objects.create(addon=addon, user=user_factory())
|
||||
},
|
||||
CinderActionApproveInitialDecision: {'addon': addon},
|
||||
CinderActionApproveNoAction: {'addon': addon},
|
||||
CinderActionOverrideApprove: {'addon': addon},
|
||||
CinderActionTargetAppealApprove: {'addon': addon},
|
||||
CinderActionTargetAppealRemovalAffirmation: {'addon': addon},
|
||||
CinderActionIgnore: {'addon': addon},
|
||||
CinderActionAlreadyRemoved: {'addon': addon},
|
||||
ContentActionApproveInitialDecision: {'addon': addon},
|
||||
ContentActionApproveNoAction: {'addon': addon},
|
||||
ContentActionOverrideApprove: {'addon': addon},
|
||||
ContentActionTargetAppealApprove: {'addon': addon},
|
||||
ContentActionTargetAppealRemovalAffirmation: {'addon': addon},
|
||||
ContentActionIgnore: {'addon': addon},
|
||||
ContentActionAlreadyRemoved: {'addon': addon},
|
||||
}
|
||||
action_to_class = [
|
||||
(decision_action, CinderDecision.get_action_helper_class(decision_action))
|
||||
(decision_action, ContentDecision.get_action_helper_class(decision_action))
|
||||
for decision_action in DECISION_ACTIONS.values
|
||||
]
|
||||
# base cases, where it's a decision without an override or appeal involved
|
||||
|
@ -2277,22 +2278,22 @@ class TestCinderDecision(TestCase):
|
|||
for action in DECISION_ACTIONS.REMOVING.values:
|
||||
# add appeal success cases
|
||||
action_existing_to_class[(DECISION_ACTIONS.AMO_APPROVE, None, action)] = (
|
||||
CinderActionTargetAppealApprove
|
||||
ContentActionTargetAppealApprove
|
||||
)
|
||||
action_existing_to_class[
|
||||
(DECISION_ACTIONS.AMO_APPROVE_VERSION, None, action)
|
||||
] = CinderActionTargetAppealApprove
|
||||
] = ContentActionTargetAppealApprove
|
||||
# add appeal denial cases
|
||||
action_existing_to_class[(action, None, action)] = (
|
||||
CinderActionTargetAppealRemovalAffirmation
|
||||
ContentActionTargetAppealRemovalAffirmation
|
||||
)
|
||||
# add override from takedown to approve cases
|
||||
action_existing_to_class[(DECISION_ACTIONS.AMO_APPROVE, action, None)] = (
|
||||
CinderActionOverrideApprove
|
||||
ContentActionOverrideApprove
|
||||
)
|
||||
action_existing_to_class[
|
||||
(DECISION_ACTIONS.AMO_APPROVE_VERSION, action, None)
|
||||
] = CinderActionOverrideApprove
|
||||
] = ContentActionOverrideApprove
|
||||
|
||||
for (
|
||||
new_action,
|
||||
|
@ -2321,7 +2322,7 @@ class TestCinderDecision(TestCase):
|
|||
)
|
||||
|
||||
action_existing_to_class_no_reporter_emails = {
|
||||
(action, action): CinderDecision.get_action_helper_class(action)
|
||||
(action, action): ContentDecision.get_action_helper_class(action)
|
||||
for action in DECISION_ACTIONS.REMOVING.values
|
||||
}
|
||||
for (
|
||||
|
@ -2358,7 +2359,7 @@ class TestCinderDecision(TestCase):
|
|||
cinder_job=CinderJob.objects.create(
|
||||
target_addon=addon,
|
||||
resolvable_in_reviewer_tools=resolvable_in_reviewer_tools,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -2420,7 +2421,7 @@ class TestCinderDecision(TestCase):
|
|||
reason=AbuseReport.REASONS.ILLEGAL,
|
||||
reporter=user_factory(),
|
||||
cinder_job=CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -2458,7 +2459,7 @@ class TestCinderDecision(TestCase):
|
|||
reason=AbuseReport.REASONS.ILLEGAL,
|
||||
reporter=user_factory(),
|
||||
cinder_job=CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
# This (target is an add-on, decision is a user ban) shouldn't
|
||||
|
@ -2502,7 +2503,7 @@ class TestCinderDecision(TestCase):
|
|||
reason=AbuseReport.REASONS.ILLEGAL,
|
||||
reporter=user_factory(),
|
||||
cinder_job=CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_BAN_USER,
|
||||
|
@ -2543,7 +2544,7 @@ class TestCinderDecision(TestCase):
|
|||
abuse_report.update(
|
||||
cinder_job=CinderJob.objects.create(
|
||||
target_addon=addon,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -2587,7 +2588,7 @@ class TestCinderDecision(TestCase):
|
|||
abuse_report.update(
|
||||
cinder_job=CinderJob.objects.create(
|
||||
target_addon=addon,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -2630,7 +2631,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_appeal_improperly_configured_reporter(self):
|
||||
cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -2653,7 +2654,7 @@ class TestCinderDecision(TestCase):
|
|||
reporter=user_factory(),
|
||||
)
|
||||
cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-lost',
|
||||
action_date=self.days_ago(179),
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -2734,7 +2735,7 @@ class TestCinderDecision(TestCase):
|
|||
]
|
||||
self.assertCloseToNow(decision.action_date)
|
||||
assert list(decision.policies.all()) == policies
|
||||
assert CinderDecision.objects.count() == 1
|
||||
assert ContentDecision.objects.count() == 1
|
||||
assert decision.id
|
||||
elif expect_create_job_decision_call:
|
||||
assert create_decision_response.call_count == 0
|
||||
|
@ -2749,12 +2750,12 @@ class TestCinderDecision(TestCase):
|
|||
]
|
||||
self.assertCloseToNow(decision.action_date)
|
||||
assert list(decision.policies.all()) == policies
|
||||
assert CinderDecision.objects.count() == 1
|
||||
assert ContentDecision.objects.count() == 1
|
||||
assert decision.id
|
||||
else:
|
||||
assert create_decision_response.call_count == 0
|
||||
assert create_job_decision_response.call_count == 0
|
||||
assert CinderPolicy.cinderdecision_set.through.objects.count() == 0
|
||||
assert CinderPolicy.contentdecision_set.through.objects.count() == 0
|
||||
assert not decision.id
|
||||
if expect_email:
|
||||
assert len(mail.outbox) == 1
|
||||
|
@ -2782,7 +2783,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_new_decision(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.REJECT_VERSION,
|
||||
|
@ -2796,7 +2797,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_updated_decision(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON
|
||||
)
|
||||
self._test_notify_reviewer_decision(
|
||||
|
@ -2814,7 +2815,7 @@ class TestCinderDecision(TestCase):
|
|||
addon = addon_factory(
|
||||
users=[addon_developer], version_kw={'channel': amo.CHANNEL_UNLISTED}
|
||||
)
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.REJECT_VERSION,
|
||||
|
@ -2831,7 +2832,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_new_decision_no_email_to_owner(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
decision.cinder_job = CinderJob.objects.create(job_id='1234')
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
|
@ -2845,7 +2846,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_updated_decision_no_email_to_owner(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON
|
||||
)
|
||||
decision.cinder_job = CinderJob.objects.create(job_id='1234')
|
||||
|
@ -2861,7 +2862,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_no_create_decision_for_approve_without_a_job(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
assert not hasattr(decision, 'cinder_job')
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
|
@ -2875,7 +2876,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_auto_approve_email_for_non_human_review(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.APPROVE_VERSION,
|
||||
|
@ -2890,7 +2891,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_auto_approve_email_for_human_review(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.APPROVE_VERSION,
|
||||
|
@ -2913,7 +2914,7 @@ class TestCinderDecision(TestCase):
|
|||
)
|
||||
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
CinderDecision().notify_reviewer_decision(
|
||||
ContentDecision().notify_reviewer_decision(
|
||||
log_entry=log_entry, entity_helper=None
|
||||
)
|
||||
|
||||
|
@ -2928,14 +2929,14 @@ class TestCinderDecision(TestCase):
|
|||
)
|
||||
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
CinderDecision().notify_reviewer_decision(
|
||||
ContentDecision().notify_reviewer_decision(
|
||||
log_entry=log_entry, entity_helper=None
|
||||
)
|
||||
|
||||
def test_notify_reviewer_decision_rejection_blocking(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.REJECT_VERSION,
|
||||
|
@ -2963,7 +2964,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_rejection_blocking_addon_being_disabled(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer])
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.REJECT_VERSION,
|
||||
|
@ -2991,7 +2992,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_notify_reviewer_decision_rejection_addon_already_disabled(self):
|
||||
addon_developer = user_factory()
|
||||
addon = addon_factory(users=[addon_developer], status=amo.STATUS_DISABLED)
|
||||
decision = CinderDecision(addon=addon)
|
||||
decision = ContentDecision(addon=addon)
|
||||
self._test_notify_reviewer_decision(
|
||||
decision,
|
||||
amo.LOG.REJECT_VERSION,
|
||||
|
@ -3014,7 +3015,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_ban_user_held(self):
|
||||
user = user_factory(email='superstarops@mozilla.com')
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
user=user, action=DECISION_ACTIONS.AMO_BAN_USER
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3030,7 +3031,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_ban_user(self):
|
||||
user = user_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
user=user, action=DECISION_ACTIONS.AMO_BAN_USER
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3044,7 +3045,7 @@ class TestCinderDecision(TestCase):
|
|||
def test_process_action_disable_addon_held(self):
|
||||
addon = addon_factory()
|
||||
self.make_addon_promoted(addon, RECOMMENDED, approve_version=True)
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3060,7 +3061,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_disable_addon(self):
|
||||
addon = addon_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3071,7 +3072,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_delete_collection_held(self):
|
||||
collection = collection_factory(author=self.task_user)
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
collection=collection, action=DECISION_ACTIONS.AMO_DELETE_COLLECTION
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3087,7 +3088,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_delete_collection(self):
|
||||
collection = collection_factory(author=user_factory())
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
collection=collection, action=DECISION_ACTIONS.AMO_DELETE_COLLECTION
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3110,7 +3111,7 @@ class TestCinderDecision(TestCase):
|
|||
addon=addon, user=user_factory(), body='sdsd'
|
||||
),
|
||||
)
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
rating=rating, action=DECISION_ACTIONS.AMO_DELETE_RATING
|
||||
)
|
||||
self.make_addon_promoted(rating.addon, RECOMMENDED, approve_version=True)
|
||||
|
@ -3127,7 +3128,7 @@ class TestCinderDecision(TestCase):
|
|||
|
||||
def test_process_action_delete_rating(self):
|
||||
rating = Rating.objects.create(addon=addon_factory(), user=user_factory())
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
rating=rating, action=DECISION_ACTIONS.AMO_DELETE_RATING
|
||||
)
|
||||
assert decision.action_date is None
|
||||
|
@ -3136,6 +3137,59 @@ class TestCinderDecision(TestCase):
|
|||
assert rating.reload().deleted
|
||||
assert ActivityLog.objects.filter(action=amo.LOG.DELETE_RATING.id).count() == 1
|
||||
|
||||
def test_get_target_review_url(self):
|
||||
addon = addon_factory()
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon, action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
assert decision.get_target_review_url() == reverse(
|
||||
'reviewers.review', args=(addon.id,)
|
||||
)
|
||||
|
||||
decision.update(addon=None, user=user_factory())
|
||||
assert decision.get_target_review_url() == ''
|
||||
|
||||
def test_get_target_type(self):
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon_factory(), action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
assert decision.get_target_type() == 'Extension'
|
||||
|
||||
decision.update(addon=None, user=user_factory())
|
||||
assert decision.get_target_type() == 'User profile'
|
||||
|
||||
decision.update(user=None, collection=collection_factory())
|
||||
assert decision.get_target_type() == 'Collection'
|
||||
|
||||
decision.update(
|
||||
collection=None,
|
||||
rating=Rating.objects.create(addon=addon_factory(), user=user_factory()),
|
||||
)
|
||||
assert decision.get_target_type() == 'Rating'
|
||||
|
||||
def test_get_target_name(self):
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=addon_factory(), action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
assert decision.get_target_name() == str(decision.addon.name)
|
||||
|
||||
decision.update(addon=None, user=user_factory())
|
||||
assert decision.get_target_name() == decision.user.name
|
||||
|
||||
decision.update(user=None, collection=collection_factory())
|
||||
assert decision.get_target_name() == decision.collection.name
|
||||
|
||||
decision.update(
|
||||
collection=None,
|
||||
rating=Rating.objects.create(
|
||||
addon=addon_factory(), user=user_factory(), body='something'
|
||||
),
|
||||
)
|
||||
assert (
|
||||
decision.get_target_name()
|
||||
== f'"something" for {decision.rating.addon.name}'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -25,7 +25,7 @@ from olympia.reviewers.models import NeedsHumanReview, ReviewActionReason, Usage
|
|||
from olympia.versions.models import Version
|
||||
from olympia.zadmin.models import set_config
|
||||
|
||||
from ..models import AbuseReport, CinderDecision, CinderJob, CinderPolicy
|
||||
from ..models import AbuseReport, CinderJob, CinderPolicy, ContentDecision
|
||||
from ..tasks import (
|
||||
appeal_to_cinder,
|
||||
handle_escalate_action,
|
||||
|
@ -430,7 +430,7 @@ def test_addon_report_to_cinder_different_locale():
|
|||
def test_addon_appeal_to_cinder_reporter(statsd_incr_mock):
|
||||
addon = addon_factory()
|
||||
cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-abc',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=addon,
|
||||
|
@ -492,7 +492,7 @@ def test_addon_appeal_to_cinder_reporter(statsd_incr_mock):
|
|||
def test_addon_appeal_to_cinder_reporter_exception(statsd_incr_mock):
|
||||
addon = addon_factory()
|
||||
cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-abc',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=addon,
|
||||
|
@ -534,7 +534,7 @@ def test_addon_appeal_to_cinder_authenticated_reporter():
|
|||
user = user_factory(fxa_id='fake-fxa-id')
|
||||
addon = addon_factory()
|
||||
cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='4815162342-abc',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=addon,
|
||||
|
@ -592,7 +592,7 @@ def test_addon_appeal_to_cinder_authenticated_reporter():
|
|||
def test_addon_appeal_to_cinder_authenticated_author():
|
||||
user = user_factory(fxa_id='fake-fxa-id')
|
||||
addon = addon_factory(users=[user])
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
cinder_id='4815162342-abc',
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=addon,
|
||||
|
@ -749,7 +749,7 @@ def test_notify_addon_decision_to_cinder(statsd_incr_mock):
|
|||
assert request_body['policy_uuids'] == ['12345678']
|
||||
assert request_body['reasoning'] == 'some review text'
|
||||
assert request_body['entity']['id'] == str(addon.id)
|
||||
assert CinderDecision.objects.get().action == DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
assert ContentDecision.objects.get().action == DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
|
||||
assert statsd_incr_mock.call_count == 1
|
||||
assert statsd_incr_mock.call_args[0] == (
|
||||
|
@ -899,7 +899,7 @@ class TestSyncCinderPolicies(TestCase):
|
|||
text='Old policy, but with linked decision',
|
||||
)
|
||||
old_policy_with_decision.update(modified=days_ago(1))
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=addon_factory()
|
||||
)
|
||||
decision.policies.add(old_policy_with_decision)
|
||||
|
@ -1015,7 +1015,7 @@ class TestSyncCinderPolicies(TestCase):
|
|||
@pytest.mark.django_db
|
||||
def test_handle_escalate_action():
|
||||
addon = addon_factory()
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON, addon=addon, notes='blah'
|
||||
)
|
||||
job = CinderJob.objects.create(job_id='1234', target_addon=addon, decision=decision)
|
||||
|
|
|
@ -15,27 +15,27 @@ from olympia.constants.promoted import RECOMMENDED
|
|||
from olympia.core import set_user
|
||||
from olympia.ratings.models import Rating
|
||||
|
||||
from ..models import AbuseReport, CinderAppeal, CinderDecision, CinderJob, CinderPolicy
|
||||
from ..utils import (
|
||||
CinderActionApproveInitialDecision,
|
||||
CinderActionApproveNoAction,
|
||||
CinderActionBanUser,
|
||||
CinderActionDeleteCollection,
|
||||
CinderActionDeleteRating,
|
||||
CinderActionDisableAddon,
|
||||
CinderActionIgnore,
|
||||
CinderActionOverrideApprove,
|
||||
CinderActionRejectVersion,
|
||||
CinderActionRejectVersionDelayed,
|
||||
CinderActionTargetAppealApprove,
|
||||
CinderActionTargetAppealRemovalAffirmation,
|
||||
from ..actions import (
|
||||
ContentActionApproveInitialDecision,
|
||||
ContentActionApproveNoAction,
|
||||
ContentActionBanUser,
|
||||
ContentActionDeleteCollection,
|
||||
ContentActionDeleteRating,
|
||||
ContentActionDisableAddon,
|
||||
ContentActionIgnore,
|
||||
ContentActionOverrideApprove,
|
||||
ContentActionRejectVersion,
|
||||
ContentActionRejectVersionDelayed,
|
||||
ContentActionTargetAppealApprove,
|
||||
ContentActionTargetAppealRemovalAffirmation,
|
||||
)
|
||||
from ..models import AbuseReport, CinderAppeal, CinderJob, CinderPolicy, ContentDecision
|
||||
|
||||
|
||||
class BaseTestCinderAction:
|
||||
class BaseTestContentAction:
|
||||
def setUp(self):
|
||||
addon = addon_factory()
|
||||
self.decision = CinderDecision.objects.create(
|
||||
self.decision = ContentDecision.objects.create(
|
||||
cinder_id='ab89',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
notes="extra note's",
|
||||
|
@ -218,21 +218,21 @@ class BaseTestCinderAction:
|
|||
assert ''' not in mail_item.body
|
||||
assert self.decision.notes in mail_item.body
|
||||
|
||||
def _test_approve_appeal_or_override(CinderActionClass):
|
||||
def _test_approve_appeal_or_override(ContentActionClass):
|
||||
raise NotImplementedError
|
||||
|
||||
def test_approve_appeal_success(self):
|
||||
self._test_approve_appeal_or_override(CinderActionTargetAppealApprove)
|
||||
self._test_approve_appeal_or_override(ContentActionTargetAppealApprove)
|
||||
assert 'After reviewing your appeal' in mail.outbox[0].body
|
||||
|
||||
def test_approve_override(self):
|
||||
self._test_approve_appeal_or_override(CinderActionOverrideApprove)
|
||||
self._test_approve_appeal_or_override(ContentActionOverrideApprove)
|
||||
assert 'After reviewing your appeal' not in mail.outbox[0].body
|
||||
|
||||
def _test_reporter_no_action_taken(
|
||||
self,
|
||||
*,
|
||||
ActionClass=CinderActionApproveNoAction,
|
||||
ActionClass=ContentActionApproveNoAction,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
):
|
||||
raise NotImplementedError
|
||||
|
@ -245,7 +245,7 @@ class BaseTestCinderAction:
|
|||
def test_reporter_appeal_approve(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
addon=self.decision.addon,
|
||||
user=self.decision.user,
|
||||
rating=self.decision.rating,
|
||||
|
@ -267,7 +267,7 @@ class BaseTestCinderAction:
|
|||
def test_owner_content_approve_report_email(self):
|
||||
# This isn't called by cinder actions, but is triggered by reviewer actions
|
||||
subject = self._test_reporter_no_action_taken(
|
||||
ActionClass=CinderActionApproveInitialDecision
|
||||
ActionClass=ContentActionApproveInitialDecision
|
||||
)
|
||||
assert len(mail.outbox) == 3
|
||||
self._test_reporter_content_approve_email(subject)
|
||||
|
@ -287,7 +287,7 @@ class BaseTestCinderAction:
|
|||
def test_reporter_ignore_invalid_report(self):
|
||||
self.decision.policies.first().update()
|
||||
subject = self._test_reporter_no_action_taken(
|
||||
ActionClass=CinderActionIgnore, action=DECISION_ACTIONS.AMO_IGNORE
|
||||
ActionClass=ContentActionIgnore, action=DECISION_ACTIONS.AMO_IGNORE
|
||||
)
|
||||
assert len(mail.outbox) == 2
|
||||
assert mail.outbox[0].to == ['email@domain.com']
|
||||
|
@ -315,7 +315,7 @@ class BaseTestCinderAction:
|
|||
action.notify_owners()
|
||||
assert unsafe_str in mail.outbox[0].body
|
||||
|
||||
action = CinderActionApproveNoAction(self.decision)
|
||||
action = ContentActionApproveNoAction(self.decision)
|
||||
mail.outbox.clear()
|
||||
action.notify_reporters(
|
||||
reporter_abuse_reports=[self.abuse_report_auth], is_appeal=True
|
||||
|
@ -323,8 +323,8 @@ class BaseTestCinderAction:
|
|||
assert unsafe_str in mail.outbox[0].body
|
||||
|
||||
|
||||
class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
||||
ActionClass = CinderActionBanUser
|
||||
class TestContentActionUser(BaseTestContentAction, TestCase):
|
||||
ActionClass = ContentActionBanUser
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -363,7 +363,7 @@ class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
|||
def test_ban_user_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
user=self.user, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
),
|
||||
)
|
||||
|
@ -380,7 +380,7 @@ class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
|||
def _test_reporter_no_action_taken(
|
||||
self,
|
||||
*,
|
||||
ActionClass=CinderActionApproveNoAction,
|
||||
ActionClass=ContentActionApproveNoAction,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
):
|
||||
self.decision.update(action=action)
|
||||
|
@ -395,10 +395,10 @@ class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
|||
action.notify_owners()
|
||||
return f'Mozilla Add-ons: {self.user.name}'
|
||||
|
||||
def _test_approve_appeal_or_override(self, CinderActionClass):
|
||||
def _test_approve_appeal_or_override(self, ContentActionClass):
|
||||
self.decision.update(action=DECISION_ACTIONS.AMO_APPROVE)
|
||||
self.user.update(banned=self.days_ago(1), deleted=True)
|
||||
action = CinderActionClass(self.decision)
|
||||
action = ContentActionClass(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.user.reload()
|
||||
|
@ -415,7 +415,7 @@ class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
|||
|
||||
def test_target_appeal_decline(self):
|
||||
self.user.update(banned=self.days_ago(1), deleted=True)
|
||||
action = CinderActionTargetAppealRemovalAffirmation(self.decision)
|
||||
action = ContentActionTargetAppealRemovalAffirmation(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.user.reload()
|
||||
|
@ -467,8 +467,8 @@ class TestCinderActionUser(BaseTestCinderAction, TestCase):
|
|||
|
||||
@override_switch('dsa-cinder-forwarded-review', active=True)
|
||||
@override_switch('dsa-appeals-review', active=True)
|
||||
class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
||||
ActionClass = CinderActionDisableAddon
|
||||
class TestContentActionAddon(BaseTestContentAction, TestCase):
|
||||
ActionClass = ContentActionDisableAddon
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -505,7 +505,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
def test_disable_addon_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
addon=self.addon, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
),
|
||||
)
|
||||
|
@ -519,10 +519,10 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
assert len(mail.outbox) == 2
|
||||
self._test_reporter_appeal_takedown_email(subject)
|
||||
|
||||
def _test_approve_appeal_or_override(self, CinderActionClass):
|
||||
def _test_approve_appeal_or_override(self, ContentActionClass):
|
||||
self.addon.update(status=amo.STATUS_DISABLED)
|
||||
ActivityLog.objects.all().delete()
|
||||
action = CinderActionClass(self.decision)
|
||||
action = ContentActionClass(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
assert self.addon.reload().status == amo.STATUS_APPROVED
|
||||
|
@ -539,7 +539,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
def _test_reporter_no_action_taken(
|
||||
self,
|
||||
*,
|
||||
ActionClass=CinderActionApproveNoAction,
|
||||
ActionClass=ContentActionApproveNoAction,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
):
|
||||
self.decision.update(action=action)
|
||||
|
@ -556,7 +556,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
def test_target_appeal_decline(self):
|
||||
self.addon.update(status=amo.STATUS_DISABLED)
|
||||
ActivityLog.objects.all().delete()
|
||||
action = CinderActionTargetAppealRemovalAffirmation(self.decision)
|
||||
action = ContentActionTargetAppealRemovalAffirmation(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.addon.reload()
|
||||
|
@ -572,7 +572,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
self.addon.update(status=amo.STATUS_DISABLED)
|
||||
ActivityLog.objects.all().delete()
|
||||
self.decision.update(notes='')
|
||||
action = CinderActionTargetAppealRemovalAffirmation(self.decision)
|
||||
action = ContentActionTargetAppealRemovalAffirmation(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.addon.reload()
|
||||
|
@ -652,7 +652,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
|
||||
def _test_reject_version(self):
|
||||
self.decision.update(action=DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON)
|
||||
action = CinderActionRejectVersion(self.decision)
|
||||
action = ContentActionRejectVersion(self.decision)
|
||||
# process_action isn't implemented for this action currently.
|
||||
with self.assertRaises(NotImplementedError):
|
||||
action.process_action()
|
||||
|
@ -687,7 +687,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
def test_reject_version_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
addon=self.addon, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
),
|
||||
)
|
||||
|
@ -705,7 +705,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
self.decision.update(
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
)
|
||||
action = CinderActionRejectVersionDelayed(self.decision)
|
||||
action = ContentActionRejectVersionDelayed(self.decision)
|
||||
# note: process_action isn't implemented for this action currently.
|
||||
|
||||
subject = f'Mozilla Add-ons: {self.addon.name}'
|
||||
|
@ -760,7 +760,7 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
def test_reject_version_delayed_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_APPROVE, addon=self.addon
|
||||
),
|
||||
)
|
||||
|
@ -831,8 +831,8 @@ class TestCinderActionAddon(BaseTestCinderAction, TestCase):
|
|||
}
|
||||
|
||||
|
||||
class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
||||
ActionClass = CinderActionDeleteCollection
|
||||
class TestContentActionCollection(BaseTestContentAction, TestCase):
|
||||
ActionClass = ContentActionDeleteCollection
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -874,7 +874,7 @@ class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
|||
def test_delete_collection_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
collection=self.collection, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
),
|
||||
)
|
||||
|
@ -891,7 +891,7 @@ class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
|||
def _test_reporter_no_action_taken(
|
||||
self,
|
||||
*,
|
||||
ActionClass=CinderActionApproveNoAction,
|
||||
ActionClass=ContentActionApproveNoAction,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
):
|
||||
self.decision.update(action=action)
|
||||
|
@ -907,9 +907,9 @@ class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
|||
action.notify_owners()
|
||||
return f'Mozilla Add-ons: {self.collection.name}'
|
||||
|
||||
def _test_approve_appeal_or_override(self, CinderActionClass):
|
||||
def _test_approve_appeal_or_override(self, ContentActionClass):
|
||||
self.collection.update(deleted=True)
|
||||
action = CinderActionClass(self.decision)
|
||||
action = ContentActionClass(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
assert self.collection.reload()
|
||||
|
@ -926,7 +926,7 @@ class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
|||
|
||||
def test_target_appeal_decline(self):
|
||||
self.collection.update(deleted=True)
|
||||
action = CinderActionTargetAppealRemovalAffirmation(self.decision)
|
||||
action = ContentActionTargetAppealRemovalAffirmation(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.collection.reload()
|
||||
|
@ -963,8 +963,8 @@ class TestCinderActionCollection(BaseTestCinderAction, TestCase):
|
|||
}
|
||||
|
||||
|
||||
class TestCinderActionRating(BaseTestCinderAction, TestCase):
|
||||
ActionClass = CinderActionDeleteRating
|
||||
class TestContentActionRating(BaseTestContentAction, TestCase):
|
||||
ActionClass = ContentActionDeleteRating
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -1010,7 +1010,7 @@ class TestCinderActionRating(BaseTestCinderAction, TestCase):
|
|||
def test_delete_rating_after_reporter_appeal(self):
|
||||
original_job = CinderJob.objects.create(
|
||||
job_id='original',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
rating=self.rating, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
),
|
||||
)
|
||||
|
@ -1027,7 +1027,7 @@ class TestCinderActionRating(BaseTestCinderAction, TestCase):
|
|||
def _test_reporter_no_action_taken(
|
||||
self,
|
||||
*,
|
||||
ActionClass=CinderActionApproveNoAction,
|
||||
ActionClass=ContentActionApproveNoAction,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
):
|
||||
self.decision.update(action=action)
|
||||
|
@ -1042,10 +1042,10 @@ class TestCinderActionRating(BaseTestCinderAction, TestCase):
|
|||
action.notify_owners()
|
||||
return f'Mozilla Add-ons: "Saying ..." for {self.rating.addon.name}'
|
||||
|
||||
def _test_approve_appeal_or_override(self, CinderActionClass):
|
||||
def _test_approve_appeal_or_override(self, ContentActionClass):
|
||||
self.rating.delete()
|
||||
ActivityLog.objects.all().delete()
|
||||
action = CinderActionClass(self.decision)
|
||||
action = ContentActionClass(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
assert not self.rating.reload().deleted
|
||||
|
@ -1064,7 +1064,7 @@ class TestCinderActionRating(BaseTestCinderAction, TestCase):
|
|||
def test_target_appeal_decline(self):
|
||||
self.rating.delete()
|
||||
ActivityLog.objects.all().delete()
|
||||
action = CinderActionTargetAppealRemovalAffirmation(self.decision)
|
||||
action = ContentActionTargetAppealRemovalAffirmation(self.decision)
|
||||
assert action.process_action() is None
|
||||
|
||||
self.rating.reload()
|
||||
|
|
|
@ -32,13 +32,13 @@ from olympia.constants.abuse import DECISION_ACTIONS
|
|||
from olympia.core import get_user, set_user
|
||||
from olympia.ratings.models import Rating
|
||||
|
||||
from ..models import AbuseReport, CinderAppeal, CinderDecision, CinderJob
|
||||
from ..utils import (
|
||||
CinderActionApproveNoAction,
|
||||
CinderActionDisableAddon,
|
||||
CinderActionTargetAppealApprove,
|
||||
CinderActionTargetAppealRemovalAffirmation,
|
||||
from ..actions import (
|
||||
ContentActionApproveNoAction,
|
||||
ContentActionDisableAddon,
|
||||
ContentActionTargetAppealApprove,
|
||||
ContentActionTargetAppealRemovalAffirmation,
|
||||
)
|
||||
from ..models import AbuseReport, CinderAppeal, CinderJob, ContentDecision
|
||||
from ..views import CinderInboundPermission, cinder_webhook, filter_enforcement_actions
|
||||
|
||||
|
||||
|
@ -1285,7 +1285,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid)
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
appeal_job=CinderJob.objects.create(
|
||||
|
@ -1321,7 +1321,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid)
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action_date=datetime(2023, 10, 12, 9, 8, 37, 4789),
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -1354,7 +1354,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid, users=[author])
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action_date=datetime(2023, 10, 12, 9, 8, 37, 4789),
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -1366,7 +1366,7 @@ class TestCinderWebhook(TestCase):
|
|||
)
|
||||
req = self.get_request(data=data)
|
||||
with mock.patch.object(
|
||||
CinderActionTargetAppealRemovalAffirmation, 'process_action'
|
||||
ContentActionTargetAppealRemovalAffirmation, 'process_action'
|
||||
) as process_mock:
|
||||
cinder_webhook(req)
|
||||
process_mock.assert_called()
|
||||
|
@ -1381,7 +1381,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid, users=[author])
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action_date=datetime(2023, 10, 12, 9, 8, 37, 4789),
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -1393,7 +1393,7 @@ class TestCinderWebhook(TestCase):
|
|||
)
|
||||
req = self.get_request(data=data)
|
||||
with mock.patch.object(
|
||||
CinderActionTargetAppealApprove, 'process_action'
|
||||
ContentActionTargetAppealApprove, 'process_action'
|
||||
) as process_mock:
|
||||
cinder_webhook(req)
|
||||
process_mock.assert_called()
|
||||
|
@ -1408,7 +1408,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid, users=[author])
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action_date=datetime(2023, 10, 12, 9, 8, 37, 4789),
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -1426,7 +1426,7 @@ class TestCinderWebhook(TestCase):
|
|||
)
|
||||
req = self.get_request(data=data)
|
||||
with mock.patch.object(
|
||||
CinderActionDisableAddon, 'process_action'
|
||||
ContentActionDisableAddon, 'process_action'
|
||||
) as process_mock:
|
||||
cinder_webhook(req)
|
||||
process_mock.assert_called()
|
||||
|
@ -1443,7 +1443,7 @@ class TestCinderWebhook(TestCase):
|
|||
addon = addon_factory(guid=abuse_report.guid, users=[author])
|
||||
original_cinder_job = CinderJob.objects.get()
|
||||
original_cinder_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action_date=datetime(2023, 10, 12, 9, 8, 37, 4789),
|
||||
cinder_id='d1f01fae-3bce-41d5-af8a-e0b4b5ceaaed',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
|
@ -1461,7 +1461,7 @@ class TestCinderWebhook(TestCase):
|
|||
)
|
||||
req = self.get_request(data=data)
|
||||
with mock.patch.object(
|
||||
CinderActionApproveNoAction, 'process_action'
|
||||
ContentActionApproveNoAction, 'process_action'
|
||||
) as process_mock:
|
||||
cinder_webhook(req)
|
||||
process_mock.assert_called()
|
||||
|
@ -2465,7 +2465,7 @@ class TestAppeal(TestCase):
|
|||
def setUp(self):
|
||||
self.addon = addon_factory()
|
||||
self.cinder_job = CinderJob.objects.create(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='my-decision-id',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
action_date=self.days_ago(1),
|
||||
|
@ -2712,7 +2712,7 @@ class TestAppeal(TestCase):
|
|||
user = user_factory()
|
||||
self.addon.authors.add(user)
|
||||
self.client.force_login(user)
|
||||
decision = CinderDecision.objects.create(
|
||||
decision = ContentDecision.objects.create(
|
||||
addon=self.addon,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
cinder_id='some-decision-id',
|
||||
|
@ -2845,7 +2845,7 @@ class TestAppeal(TestCase):
|
|||
# specific error message (in this case we confirmed the original
|
||||
# decision).
|
||||
appeal_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='appeal decision id',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -2896,7 +2896,7 @@ class TestAppeal(TestCase):
|
|||
# the content is already supposed to be disabled but the reporter might
|
||||
# not have noticed).
|
||||
appeal_job.update(
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='appeal decision id',
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
|
|
|
@ -33,7 +33,7 @@ from olympia.ratings.views import RatingViewSet
|
|||
from olympia.users.models import UserProfile
|
||||
|
||||
from .forms import AbuseAppealEmailForm, AbuseAppealForm
|
||||
from .models import AbuseReport, CinderDecision, CinderJob
|
||||
from .models import AbuseReport, CinderJob, ContentDecision
|
||||
from .serializers import (
|
||||
AddonAbuseReportSerializer,
|
||||
CollectionAbuseReportSerializer,
|
||||
|
@ -178,7 +178,7 @@ def filter_enforcement_actions(enforcement_actions, cinder_job):
|
|||
if DECISION_ACTIONS.has_api_value(action_slug)
|
||||
and (action := DECISION_ACTIONS.for_api_value(action_slug))
|
||||
and target.__class__
|
||||
in CinderDecision.get_action_helper_class(action.value).valid_targets
|
||||
in ContentDecision.get_action_helper_class(action.value).valid_targets
|
||||
]
|
||||
|
||||
|
||||
|
@ -269,7 +269,7 @@ def appeal(request, *, abuse_report_id, decision_cinder_id, **kwargs):
|
|||
DECISION_ACTIONS.APPEALABLE_BY_REPORTER.values
|
||||
)
|
||||
cinder_decision = get_object_or_404(
|
||||
CinderDecision.objects.filter(action__in=appealable_decisions),
|
||||
ContentDecision.objects.filter(action__in=appealable_decisions),
|
||||
cinder_id=decision_cinder_id,
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-21 13:48
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import olympia.translations.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('translations', '0001_initial'),
|
||||
('addons', '0052_auto_20240927_1810'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='addon',
|
||||
name='summary',
|
||||
field=olympia.translations.fields.NoURLsField(blank=True, db_column='summary', max_length=250, null=True, on_delete=django.db.models.deletion.SET_NULL, require_locale=True, short=True, to='translations.NoURLsTranslation', to_field='id', unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='addon',
|
||||
name='type',
|
||||
field=models.PositiveIntegerField(choices=[(1, 'Extension'), (2, 'Deprecated Complete Theme'), (3, 'Dictionary'), (4, 'Deprecated Search Engine'), (5, 'Language Pack'), (6, 'Deprecated Language Pack (Add-on)'), (7, 'Deprecated Plugin'), (9, 'Deprecated LWT'), (10, 'Theme'), (12, 'Deprecated Site Permission')], db_column='addontype_id', default=1),
|
||||
),
|
||||
]
|
|
@ -4,8 +4,8 @@ from django.core.management.base import BaseCommand
|
|||
|
||||
import olympia.core.logger
|
||||
from olympia import amo
|
||||
from olympia.abuse.models import CinderDecision, CinderJob
|
||||
from olympia.abuse.utils import CinderActionRejectVersionDelayed
|
||||
from olympia.abuse.actions import ContentActionRejectVersionDelayed
|
||||
from olympia.abuse.models import CinderJob, ContentDecision
|
||||
from olympia.activity.models import ActivityLog
|
||||
from olympia.addons.models import Addon, AddonReviewerFlags
|
||||
from olympia.constants.abuse import DECISION_ACTIONS
|
||||
|
@ -95,13 +95,13 @@ class Command(BaseCommand):
|
|||
cinder_job.decision
|
||||
if cinder_job
|
||||
# Fake a decision if there isn't a job
|
||||
else CinderDecision(
|
||||
else ContentDecision(
|
||||
addon=addon,
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
)
|
||||
)
|
||||
decision.notes = relevant_activity_log.details.get('comments', '')
|
||||
action_helper = CinderActionRejectVersionDelayed(decision)
|
||||
action_helper = ContentActionRejectVersionDelayed(decision)
|
||||
action_helper.notify_owners(
|
||||
log_entry_id=relevant_activity_log.id,
|
||||
extra_context={
|
||||
|
|
|
@ -77,6 +77,30 @@
|
|||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% elif tab == 'held_actions' %}
|
||||
<table id="held-action-queue" class="data-grid">
|
||||
<thead>
|
||||
<tr class="listing-header">
|
||||
<th>Type</th>
|
||||
<th>Target</th>
|
||||
<th>Action</th>
|
||||
<th>Decision Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for decision in page.object_list %}
|
||||
<tr id="{{ decision.get_reference_id(short=True) }}" class="held-item">
|
||||
<td><div class="app-icon ed-sprite-action-target-{{ decision.get_target_type() }}" title="{{ decision.get_target_type() }}"></div></td>
|
||||
<td>{{ decision.get_target_name() }}</td>
|
||||
<td><a href="{{ decision.get_target_review_url() }}">{{ decision.get_action_display() }}</a></td>
|
||||
<td>{{ decision.created|datetime }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if page.paginator.count == 0 %}
|
||||
<div class="no-results">There are currently no held actions.</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div id="addon-queue-filter-form">
|
||||
<button class="show-hide-toggle">Show/Hide Filter Selections</button>
|
||||
|
|
|
@ -26,6 +26,7 @@ def queue_tabnav(context, reviewer_tables_registry):
|
|||
'moderated',
|
||||
'content_review',
|
||||
'pending_rejection',
|
||||
'held_actions',
|
||||
):
|
||||
if acl.action_allowed_for(
|
||||
request.user, reviewer_tables_registry[queue].permission
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.test.testcases import TransactionTestCase
|
|||
import responses
|
||||
|
||||
from olympia import amo
|
||||
from olympia.abuse.models import AbuseReport, CinderDecision, CinderJob, CinderPolicy
|
||||
from olympia.abuse.models import AbuseReport, CinderJob, CinderPolicy, ContentDecision
|
||||
from olympia.activity.models import ActivityLog
|
||||
from olympia.addons.models import AddonApprovalsCounter, AddonReviewerFlags
|
||||
from olympia.amo.tests import (
|
||||
|
@ -870,7 +870,7 @@ class TestSendPendingRejectionLastWarningNotification(TestCase):
|
|||
version_factory(addon=addon, version='42.1')
|
||||
cinder_job = CinderJob.objects.create(
|
||||
job_id='1',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13579',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
addon=addon,
|
||||
|
@ -1374,7 +1374,7 @@ class TestAutoReject(AutoRejectTestsMixin, TestCase):
|
|||
def test_reject_versions_with_resolved_cinder_job(self):
|
||||
cinder_job = CinderJob.objects.create(
|
||||
job_id='1',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13579',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -1411,7 +1411,7 @@ class TestAutoReject(AutoRejectTestsMixin, TestCase):
|
|||
def test_reject_versions_with_resolved_cinder_job_no_third_party(self):
|
||||
cinder_job = CinderJob.objects.create(
|
||||
job_id='2',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13579',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -1419,7 +1419,7 @@ class TestAutoReject(AutoRejectTestsMixin, TestCase):
|
|||
)
|
||||
CinderJob.objects.create(
|
||||
job_id='1',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13578',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -1456,7 +1456,7 @@ class TestAutoReject(AutoRejectTestsMixin, TestCase):
|
|||
def test_reject_versions_with_multiple_delayed_rejections(self):
|
||||
cinder_job = CinderJob.objects.create(
|
||||
job_id='2',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13579',
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_WARNING_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -1464,7 +1464,7 @@ class TestAutoReject(AutoRejectTestsMixin, TestCase):
|
|||
)
|
||||
CinderJob.objects.create(
|
||||
job_id='1',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
cinder_id='13578',
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
|
|
@ -10,9 +10,9 @@ from olympia import amo
|
|||
from olympia.abuse.models import (
|
||||
AbuseReport,
|
||||
CinderAppeal,
|
||||
CinderDecision,
|
||||
CinderJob,
|
||||
CinderPolicy,
|
||||
ContentDecision,
|
||||
)
|
||||
from olympia.addons.models import Addon
|
||||
from olympia.amo.tests import (
|
||||
|
@ -431,7 +431,7 @@ class TestReviewForm(TestCase):
|
|||
job = CinderJob.objects.create(
|
||||
job_id='1', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=job, addon=self.addon, action=DECISION_ACTIONS.AMO_DISABLE_ADDON
|
||||
)
|
||||
form = self.get_form()
|
||||
|
@ -512,7 +512,7 @@ class TestReviewForm(TestCase):
|
|||
appeal_job = CinderJob.objects.create(
|
||||
job_id='1', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job,
|
||||
addon=self.addon,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
|
@ -1039,7 +1039,7 @@ class TestReviewForm(TestCase):
|
|||
|
||||
cinder_job_appealed = CinderJob.objects.create(
|
||||
job_id='appealed',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
),
|
||||
|
@ -1078,7 +1078,7 @@ class TestReviewForm(TestCase):
|
|||
CinderJob.objects.create(
|
||||
job_id='forwarded_from',
|
||||
forwarded_to_job=cinder_job_forwarded,
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_ESCALATE_ADDON,
|
||||
notes='Why o why',
|
||||
addon=self.addon,
|
||||
|
@ -1105,7 +1105,7 @@ class TestReviewForm(TestCase):
|
|||
message='fff',
|
||||
cinder_job=CinderJob.objects.create(
|
||||
job_id='already resovled',
|
||||
decision=CinderDecision.objects.create(
|
||||
decision=ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
),
|
||||
|
|
|
@ -14,7 +14,7 @@ import pytest
|
|||
import responses
|
||||
|
||||
from olympia import amo
|
||||
from olympia.abuse.models import AbuseReport, CinderDecision, CinderJob, CinderPolicy
|
||||
from olympia.abuse.models import AbuseReport, CinderJob, CinderPolicy, ContentDecision
|
||||
from olympia.activity.models import (
|
||||
ActivityLog,
|
||||
ActivityLogToken,
|
||||
|
@ -172,7 +172,7 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
self.sign_file_mock = patcher.start()
|
||||
|
||||
def check_subject(self, msg):
|
||||
decision = CinderDecision.objects.first() or CinderDecision(
|
||||
decision = ContentDecision.objects.first() or ContentDecision(
|
||||
addon=self.addon, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
)
|
||||
assert msg.subject == (
|
||||
|
@ -972,7 +972,7 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
)
|
||||
assert expected == actions
|
||||
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=self.addon, appeal_job=job
|
||||
)
|
||||
expected = [
|
||||
|
@ -1272,7 +1272,9 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
|
||||
assert len(mail.outbox) == 1
|
||||
message = mail.outbox[0]
|
||||
decision = CinderDecision(addon=self.addon, action=DECISION_ACTIONS.AMO_APPROVE)
|
||||
decision = ContentDecision(
|
||||
addon=self.addon, action=DECISION_ACTIONS.AMO_APPROVE
|
||||
)
|
||||
assert (
|
||||
message.subject
|
||||
== f'Mozilla Add-ons: None [ref:{decision.get_reference_id()}]'
|
||||
|
@ -3243,7 +3245,7 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
def test_clear_needs_human_review_multiple_versions_not_abuse(self):
|
||||
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
|
||||
NeedsHumanReview.objects.create(version=self.review_version)
|
||||
# abuse or appeal related NHR are cleared in CinderDecision so aren't cleared
|
||||
# abuse or appeal related NHR are cleared in ContentDecision so aren't cleared
|
||||
NeedsHumanReview.objects.create(
|
||||
version=self.review_version,
|
||||
reason=NeedsHumanReview.REASONS.ABUSE_ADDON_VIOLATION,
|
||||
|
@ -3617,12 +3619,12 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
appeal_job1 = CinderJob.objects.create(
|
||||
job_id='1', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job1,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
).policies.add(policy_a, policy_b)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job1,
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -3636,7 +3638,7 @@ class TestReviewHelper(TestReviewHelperBase):
|
|||
appeal_job2 = CinderJob.objects.create(
|
||||
job_id='2', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job2,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
|
|
@ -27,7 +27,7 @@ from rest_framework.test import APIRequestFactory
|
|||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo, core, ratings
|
||||
from olympia.abuse.models import AbuseReport, CinderDecision, CinderJob, CinderPolicy
|
||||
from olympia.abuse.models import AbuseReport, CinderJob, CinderPolicy, ContentDecision
|
||||
from olympia.access import acl
|
||||
from olympia.access.models import Group, GroupUser
|
||||
from olympia.accounts.serializers import BaseUserSerializer
|
||||
|
@ -52,6 +52,7 @@ from olympia.amo.tests import (
|
|||
addon_factory,
|
||||
block_factory,
|
||||
check_links,
|
||||
collection_factory,
|
||||
formset,
|
||||
initial,
|
||||
reverse_ns,
|
||||
|
@ -712,6 +713,11 @@ class TestDashboard(TestCase):
|
|||
)
|
||||
rating.ratingflag_set.create()
|
||||
|
||||
# a held decision
|
||||
ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon1
|
||||
)
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
doc = pq(response.content)
|
||||
|
@ -730,6 +736,7 @@ class TestDashboard(TestCase):
|
|||
'https://wiki.mozilla.org/Add-ons/Reviewers/Guide/Moderation',
|
||||
reverse('reviewers.motd'),
|
||||
reverse('reviewers.queue_pending_rejection'),
|
||||
reverse('reviewers.queue_held_actions'),
|
||||
]
|
||||
links = [link.attrib['href'] for link in doc('.dashboard a')]
|
||||
assert links == expected_links
|
||||
|
@ -743,6 +750,7 @@ class TestDashboard(TestCase):
|
|||
assert doc('.dashboard a')[8].text == 'Ratings Awaiting Moderation (1)'
|
||||
# admin tools
|
||||
assert doc('.dashboard a')[12].text == 'Add-ons Pending Rejection (1)'
|
||||
assert doc('.dashboard a')[13].text == 'Held Actions for 2nd Level Approval (1)'
|
||||
|
||||
def test_can_see_all_through_reviewer_view_all_permission(self):
|
||||
self.grant_permission(self.user, 'ReviewerTools:View')
|
||||
|
@ -764,6 +772,7 @@ class TestDashboard(TestCase):
|
|||
'https://wiki.mozilla.org/Add-ons/Reviewers/Guide/Moderation',
|
||||
reverse('reviewers.motd'),
|
||||
reverse('reviewers.queue_pending_rejection'),
|
||||
reverse('reviewers.queue_held_actions'),
|
||||
]
|
||||
links = [link.attrib['href'] for link in doc('.dashboard a')]
|
||||
assert links == expected_links
|
||||
|
@ -1312,6 +1321,7 @@ class TestQueueBasics(QueueTest):
|
|||
expected.extend(
|
||||
[
|
||||
reverse('reviewers.queue_pending_rejection'),
|
||||
reverse('reviewers.queue_held_actions'),
|
||||
]
|
||||
)
|
||||
assert links == expected
|
||||
|
@ -2562,12 +2572,12 @@ class TestReview(ReviewBase):
|
|||
appeal_job1 = CinderJob.objects.create(
|
||||
job_id='1', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job1,
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON,
|
||||
addon=self.addon,
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job1,
|
||||
action=DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON,
|
||||
addon=self.addon,
|
||||
|
@ -2576,7 +2586,7 @@ class TestReview(ReviewBase):
|
|||
appeal_job2 = CinderJob.objects.create(
|
||||
job_id='2', resolvable_in_reviewer_tools=True, target_addon=self.addon
|
||||
)
|
||||
CinderDecision.objects.create(
|
||||
ContentDecision.objects.create(
|
||||
appeal_job=appeal_job2,
|
||||
action=DECISION_ACTIONS.AMO_APPROVE,
|
||||
addon=self.addon,
|
||||
|
@ -8942,3 +8952,66 @@ class TestReviewVersionRedirect(ReviewerTest):
|
|||
).status_code
|
||||
== 404
|
||||
)
|
||||
|
||||
|
||||
class TestHeldActionQueue(ReviewerTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.url = reverse('reviewers.queue_held_actions')
|
||||
|
||||
self.addon_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DISABLE_ADDON, addon=addon_factory()
|
||||
)
|
||||
self.user_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_BAN_USER, user=user_factory()
|
||||
)
|
||||
self.collection_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DELETE_COLLECTION,
|
||||
collection=collection_factory(),
|
||||
)
|
||||
self.rating_decision = ContentDecision.objects.create(
|
||||
action=DECISION_ACTIONS.AMO_DELETE_RATING,
|
||||
rating=Rating.objects.create(addon=addon_factory(), user=user_factory()),
|
||||
)
|
||||
self.login_as_admin()
|
||||
|
||||
def test_results(self):
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
doc = pq(response.content)('#held-action-queue')
|
||||
|
||||
rows = doc('tr.held-item')
|
||||
assert rows.length == 4
|
||||
links = [link.attrib['href'] for link in doc('a')]
|
||||
assert links == [
|
||||
self.addon_decision.get_target_review_url(),
|
||||
self.user_decision.get_target_review_url(),
|
||||
self.collection_decision.get_target_review_url(),
|
||||
self.rating_decision.get_target_review_url(),
|
||||
]
|
||||
assert doc('tr.held-item').attr('id') == self.addon_decision.get_reference_id(
|
||||
short=True
|
||||
)
|
||||
assert (
|
||||
doc('tr.held-item td div').attr('class')
|
||||
== 'app-icon ed-sprite-action-target-Extension'
|
||||
)
|
||||
assert doc('tr.held-item td div').attr('title') == 'Extension'
|
||||
assert doc('tr.held-item td').eq(1).text() == str(
|
||||
self.addon_decision.addon.name
|
||||
)
|
||||
assert doc('tr.held-item td').eq(2).text() == 'Add-on disable'
|
||||
|
||||
def test_non_admin_cannot_access(self):
|
||||
self.login_as_reviewer()
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 403
|
||||
|
||||
def test_reviewer_viewer_can_access(self):
|
||||
user = user_factory()
|
||||
self.grant_permission(user, 'ReviewerTools:View')
|
||||
self.client.force_login(user)
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
assert pq(response.content)('#held-action-queue')
|
||||
|
|
|
@ -13,7 +13,7 @@ import markupsafe
|
|||
|
||||
import olympia.core.logger
|
||||
from olympia import amo
|
||||
from olympia.abuse.models import CinderJob, CinderPolicy
|
||||
from olympia.abuse.models import CinderJob, CinderPolicy, ContentDecision
|
||||
from olympia.abuse.tasks import notify_addon_decision_to_cinder, resolve_job_in_cinder
|
||||
from olympia.access import acl
|
||||
from olympia.activity.models import ActivityLog, AttachmentLog
|
||||
|
@ -295,6 +295,19 @@ class ModerationQueueTable:
|
|||
view_name = 'queue_moderated'
|
||||
|
||||
|
||||
class HeldActionQueueTable:
|
||||
title = 'Held Actions for 2nd Level Approval'
|
||||
urlname = 'queue_held_actions'
|
||||
url = r'^held_actions$'
|
||||
permission = amo.permissions.REVIEWS_ADMIN
|
||||
show_count_in_dashboard = True
|
||||
view_name = 'queue_held_actions'
|
||||
|
||||
@classmethod
|
||||
def get_queryset(cls, request, **kw):
|
||||
return ContentDecision.objects.filter(action_date=None).order_by('created')
|
||||
|
||||
|
||||
class ReviewHelper:
|
||||
"""
|
||||
A class that builds enough to render the form back to the user and
|
||||
|
@ -1080,7 +1093,7 @@ class ReviewBase:
|
|||
for job in self.data.get('cinder_jobs_to_resolve', ()):
|
||||
# collect all the policies we made decisions under
|
||||
previous_policies = CinderPolicy.objects.filter(
|
||||
cinderdecision__appeal_job=job
|
||||
contentdecision__appeal_job=job
|
||||
).distinct()
|
||||
# we just need a single action for this appeal
|
||||
# - use min() to favor AMO_DISABLE_ADDON over AMO_REJECT_VERSION_ADDON
|
||||
|
|
|
@ -104,6 +104,7 @@ from olympia.reviewers.serializers import (
|
|||
)
|
||||
from olympia.reviewers.utils import (
|
||||
ContentReviewTable,
|
||||
HeldActionQueueTable,
|
||||
MadReviewTable,
|
||||
ModerationQueueTable,
|
||||
PendingManualApprovalQueueTable,
|
||||
|
@ -280,7 +281,13 @@ def dashboard(request):
|
|||
queue_counts['pending_rejection']
|
||||
),
|
||||
reverse('reviewers.queue_pending_rejection'),
|
||||
)
|
||||
),
|
||||
(
|
||||
'Held Actions for 2nd Level Approval ({0})'.format(
|
||||
queue_counts['held_actions']
|
||||
),
|
||||
reverse('reviewers.queue_held_actions'),
|
||||
),
|
||||
]
|
||||
return TemplateResponse(
|
||||
request,
|
||||
|
@ -431,6 +438,7 @@ reviewer_tables_registry = {
|
|||
'mad': MadReviewTable,
|
||||
'pending_rejection': PendingRejectionTable,
|
||||
'moderated': ModerationQueueTable,
|
||||
'held_actions': HeldActionQueueTable,
|
||||
}
|
||||
|
||||
|
||||
|
@ -1578,3 +1586,21 @@ def review_version_redirect(request, addon, version):
|
|||
)
|
||||
url = reverse('reviewers.review', args=(channel_text, addon.pk))
|
||||
return redirect(url + page_param + f'#version-{to_dom_id(version)}')
|
||||
|
||||
|
||||
@permission_or_tools_listed_view_required(amo.permissions.REVIEWS_ADMIN)
|
||||
def queue_held_actions(request, tab):
|
||||
TableObj = reviewer_tables_registry[tab]
|
||||
qs = TableObj.get_queryset(request)
|
||||
page = paginate(request, qs, per_page=20)
|
||||
|
||||
return TemplateResponse(
|
||||
request,
|
||||
'reviewers/queue.html',
|
||||
context=context(
|
||||
tab=tab,
|
||||
page=page,
|
||||
registry=reviewer_tables_registry,
|
||||
title=TableObj.title,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -57,6 +57,13 @@
|
|||
.ed-sprite-needs-human-review-from-cinder { background-position: 0 -484px; }
|
||||
.ed-sprite-needs-human-review-from-appeal { background-position: 0 -500px; }
|
||||
|
||||
.ed-sprite-action-target-Extension { background-position: 0 -532px; }
|
||||
.ed-sprite-action-target-Theme { background-position: 0 -564px; }
|
||||
.ed-sprite-action-target-Collection { background-position: 0 -548px; }
|
||||
.ed-sprite-action-target-Rating { background-position: 0 -112px; }
|
||||
.ed-sprite-action-target-User { background-position: 0 -516px; }
|
||||
|
||||
|
||||
.platform-icon {
|
||||
background: url(../../img/developers/platforms.png?8) no-repeat top left;
|
||||
}
|
||||
|
|
Двоичные данные
static/img/developers/reviewer-sprite.png
Двоичные данные
static/img/developers/reviewer-sprite.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 15 KiB После Ширина: | Высота: | Размер: 17 KiB |
Загрузка…
Ссылка в новой задаче