193 lines
6.4 KiB
Python
193 lines
6.4 KiB
Python
from django.contrib import admin
|
|
from django.template.defaultfilters import truncatechars
|
|
from django.utils.translation import gettext
|
|
|
|
from rangefilter.filter import DateRangeFilter
|
|
|
|
from .models import AbuseReport
|
|
from access import acl
|
|
from common.admin import CommaSearchInAdminMixin
|
|
from constants import permissions
|
|
|
|
|
|
class AbuseReportTypeFilter(admin.SimpleListFilter):
|
|
# Human-readable title to be displayed in the sidebar just above the filter options.
|
|
# L10n: label for the list of abuse report types: extensions, users
|
|
title = gettext('type')
|
|
|
|
# Parameter for the filter that will be used in the URL query.
|
|
parameter_name = 'type'
|
|
|
|
def lookups(self, request, model_admin):
|
|
"""
|
|
Returns a list of tuples. The first element in each
|
|
tuple is the coded value for the option that will
|
|
appear in the URL query. The second element is the
|
|
human-readable name for the option that will appear
|
|
in the right sidebar.
|
|
"""
|
|
return (
|
|
('user', gettext('Users')),
|
|
('extension', gettext('Extensions')),
|
|
)
|
|
|
|
def queryset(self, request, queryset):
|
|
"""
|
|
Returns the filtered queryset based on the value
|
|
provided in the query string and retrievable via
|
|
`self.value()`.
|
|
"""
|
|
if self.value() == 'user':
|
|
return queryset.filter(user__isnull=False)
|
|
elif self.value() == 'extension':
|
|
return queryset.filter(extension_id__isnull=False)
|
|
return queryset
|
|
|
|
|
|
class AbuseReportAdmin(CommaSearchInAdminMixin, admin.ModelAdmin):
|
|
view_on_site = True
|
|
actions = ('delete_selected', 'mark_as_valid', 'mark_as_suspicious')
|
|
date_hierarchy = 'date_modified'
|
|
list_display = (
|
|
'date_created',
|
|
'name',
|
|
'type',
|
|
'status',
|
|
'reason',
|
|
'message_excerpt',
|
|
)
|
|
list_filter = (
|
|
AbuseReportTypeFilter,
|
|
'status',
|
|
'reason',
|
|
('date_created', DateRangeFilter),
|
|
)
|
|
list_select_related = ('user',)
|
|
# Shouldn't be needed because those fields should all be readonly, but just
|
|
# in case we change our mind, FKs should be raw id fields as usual in our
|
|
# admin tools.
|
|
raw_id_fields = ('user', 'reporter')
|
|
# All fields except status must be readonly - the submitted data should
|
|
# not be changed, only the status for triage.
|
|
readonly_fields = (
|
|
'date_created',
|
|
'date_modified',
|
|
'reporter',
|
|
'user',
|
|
'message',
|
|
'extension_version',
|
|
'version',
|
|
)
|
|
fieldsets = (
|
|
('Abuse Report Core Information', {'fields': ('status', 'reason', 'message')}),
|
|
(
|
|
'Abuse Report Data',
|
|
{
|
|
'fields': (
|
|
'date_created',
|
|
'date_modified',
|
|
'reporter',
|
|
'version',
|
|
)
|
|
},
|
|
),
|
|
)
|
|
# The first fieldset is going to be dynamically added through
|
|
# get_fieldsets() depending on the target (add-on, user or unknown add-on),
|
|
# using the fields below:
|
|
dynamic_fieldset_fields = {
|
|
# User
|
|
'user': (('User', {'fields': ('user',)}),),
|
|
# FIXME
|
|
# Extension, we only have the extension slug and maybe some extra extension_*
|
|
# fields that were submitted with the report, we'll try to display the
|
|
# extension card if we can find a matching add-on in the database though.
|
|
'extension': (('Extension', {'fields': ('extension', 'extension_version')}),),
|
|
}
|
|
|
|
def has_add_permission(self, request):
|
|
# Adding new abuse reports through the admin is useless, so we prevent it.
|
|
return False
|
|
|
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
|
extra_context = extra_context or {}
|
|
extra_context['show_save_and_continue'] = False # Don't need this.
|
|
return super().change_view(
|
|
request,
|
|
object_id,
|
|
form_url,
|
|
extra_context=extra_context,
|
|
)
|
|
|
|
def get_actions(self, request):
|
|
actions = super().get_actions(request)
|
|
if not acl.action_allowed_for(request.user, permissions.ABUSEREPORTS_EDIT):
|
|
# You need AbuseReports:Edit for the extra actions.
|
|
actions.pop('mark_as_valid')
|
|
actions.pop('mark_as_suspicious')
|
|
return actions
|
|
|
|
def get_search_fields(self, request):
|
|
"""Return search fields according to the type filter."""
|
|
type_ = request.GET.get('type')
|
|
if type_ == 'extension':
|
|
search_fields = (
|
|
'extension__name',
|
|
'extension__slug',
|
|
'message',
|
|
)
|
|
elif type_ == 'user':
|
|
search_fields = (
|
|
'message',
|
|
'=user__id',
|
|
'^user__username',
|
|
'^user__email',
|
|
)
|
|
else:
|
|
search_fields = ()
|
|
return search_fields
|
|
|
|
def get_search_id_field(self, request):
|
|
"""Return the field to use when all search terms are numeric, match the type filter."""
|
|
type_ = request.GET.get('type')
|
|
if type_ == 'user':
|
|
search_field = 'user_id'
|
|
else:
|
|
search_field = super().get_search_id_field(request)
|
|
return search_field
|
|
|
|
def get_fieldsets(self, request, obj=None):
|
|
if obj.user:
|
|
target = 'user'
|
|
else:
|
|
target = 'extension'
|
|
return self.dynamic_fieldset_fields[target] + self.fieldsets
|
|
|
|
def message_excerpt(self, obj):
|
|
return truncatechars(obj.message, 140) if obj.message else ''
|
|
|
|
message_excerpt.short_description = gettext('Message excerpt')
|
|
|
|
def mark_as_valid(self, request, qs):
|
|
for obj in qs:
|
|
obj.update(status=AbuseReport.STATUSES.VALID)
|
|
self.message_user(
|
|
request,
|
|
gettext('The %d selected reports have been marked as valid.' % (qs.count())),
|
|
)
|
|
|
|
mark_as_valid.short_description = 'Mark selected abuse reports as valid'
|
|
|
|
def mark_as_suspicious(self, request, qs):
|
|
for obj in qs:
|
|
obj.update(status=AbuseReport.STATUSES.SUSPICIOUS)
|
|
self.message_user(
|
|
request,
|
|
gettext('The %d selected reports have been marked as suspicious.' % (qs.count())),
|
|
)
|
|
|
|
mark_as_suspicious.short_description = gettext('Mark selected abuse reports as suspicious')
|
|
|
|
|
|
admin.site.register(AbuseReport, AbuseReportAdmin)
|