Merge branch 'main' into partially-read-shelf
This commit is contained in:
commit
e9dfa42e11
209 changed files with 14317 additions and 4796 deletions
|
@ -29,7 +29,7 @@ from .import_job import ImportJob, ImportItem
|
|||
from .site import SiteSettings, SiteInvite
|
||||
from .site import PasswordReset, InviteRequest
|
||||
from .announcement import Announcement
|
||||
from .antispam import EmailBlocklist, IPBlocklist
|
||||
from .antispam import EmailBlocklist, IPBlocklist, AutoMod, automod_task
|
||||
|
||||
from .notification import Notification
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
""" Lets try NOT to sell viagra """
|
||||
from django.db import models
|
||||
from functools import reduce
|
||||
import operator
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bookwyrm.tasks import app
|
||||
from .user import User
|
||||
|
||||
|
||||
|
@ -33,3 +40,107 @@ class IPBlocklist(models.Model):
|
|||
"""default sorting"""
|
||||
|
||||
ordering = ("-created_date",)
|
||||
|
||||
|
||||
class AutoMod(models.Model):
|
||||
"""rules to automatically flag suspicious activity"""
|
||||
|
||||
string_match = models.CharField(max_length=200, unique=True)
|
||||
flag_users = models.BooleanField(default=True)
|
||||
flag_statuses = models.BooleanField(default=True)
|
||||
created_by = models.ForeignKey("User", on_delete=models.PROTECT)
|
||||
|
||||
|
||||
@app.task(queue="low_priority")
|
||||
def automod_task():
|
||||
"""Create reports"""
|
||||
if not AutoMod.objects.exists():
|
||||
return
|
||||
reporter = AutoMod.objects.first().created_by
|
||||
reports = automod_users(reporter) + automod_statuses(reporter)
|
||||
if reports:
|
||||
admins = User.objects.filter(
|
||||
models.Q(user_permissions__name__in=["moderate_user", "moderate_post"])
|
||||
| models.Q(is_superuser=True)
|
||||
).all()
|
||||
notification_model = apps.get_model(
|
||||
"bookwyrm", "Notification", require_ready=True
|
||||
)
|
||||
for admin in admins:
|
||||
notification_model.objects.bulk_create(
|
||||
[
|
||||
notification_model(
|
||||
user=admin,
|
||||
related_report=r,
|
||||
notification_type="REPORT",
|
||||
)
|
||||
for r in reports
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def automod_users(reporter):
|
||||
"""check users for moderation flags"""
|
||||
user_rules = AutoMod.objects.filter(flag_users=True).values_list(
|
||||
"string_match", flat=True
|
||||
)
|
||||
if not user_rules:
|
||||
return []
|
||||
|
||||
filters = []
|
||||
for field in ["username", "summary", "name"]:
|
||||
filters += [{f"{field}__icontains": r} for r in user_rules]
|
||||
users = User.objects.filter(
|
||||
reduce(operator.or_, (Q(**f) for f in filters)),
|
||||
is_active=True,
|
||||
local=True,
|
||||
report__isnull=True, # don't flag users that already have reports
|
||||
).distinct()
|
||||
|
||||
report_model = apps.get_model("bookwyrm", "Report", require_ready=True)
|
||||
|
||||
return report_model.objects.bulk_create(
|
||||
[
|
||||
report_model(
|
||||
reporter=reporter,
|
||||
note=_("Automatically generated report"),
|
||||
user=u,
|
||||
)
|
||||
for u in users
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def automod_statuses(reporter):
|
||||
"""check statues for moderation flags"""
|
||||
status_rules = AutoMod.objects.filter(flag_statuses=True).values_list(
|
||||
"string_match", flat=True
|
||||
)
|
||||
|
||||
if not status_rules:
|
||||
return []
|
||||
|
||||
filters = []
|
||||
for field in ["content", "content_warning", "quotation__quote", "review__name"]:
|
||||
filters += [{f"{field}__icontains": r} for r in status_rules]
|
||||
|
||||
status_model = apps.get_model("bookwyrm", "Status", require_ready=True)
|
||||
statuses = status_model.objects.filter(
|
||||
reduce(operator.or_, (Q(**f) for f in filters)),
|
||||
deleted=False,
|
||||
local=True,
|
||||
report__isnull=True, # don't flag statuses that already have reports
|
||||
).distinct()
|
||||
|
||||
report_model = apps.get_model("bookwyrm", "Report", require_ready=True)
|
||||
return report_model.objects.bulk_create(
|
||||
[
|
||||
report_model(
|
||||
reporter=reporter,
|
||||
note=_("Automatically generated report"),
|
||||
user=s.user,
|
||||
status=s,
|
||||
)
|
||||
for s in statuses
|
||||
]
|
||||
)
|
||||
|
|
|
@ -21,9 +21,6 @@ class Author(BookDataModel):
|
|||
isni = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
viaf_id = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
gutenberg_id = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
|
|
|
@ -46,6 +46,15 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
|
|||
bnf_id = fields.CharField( # Bibliothèque nationale de France
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
viaf = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
wikidata = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
asin = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
search_vector = SearchVectorField(null=True)
|
||||
|
||||
last_edited_by = fields.ForeignKey(
|
||||
|
@ -271,9 +280,6 @@ class Edition(Book):
|
|||
oclc_number = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
asin = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
pages = fields.IntegerField(blank=True, null=True)
|
||||
physical_format = fields.CharField(
|
||||
max_length=255, choices=FormatChoices, null=True, blank=True
|
||||
|
|
|
@ -12,7 +12,12 @@ class Report(BookWyrmModel):
|
|||
)
|
||||
note = models.TextField(null=True, blank=True)
|
||||
user = models.ForeignKey("User", on_delete=models.PROTECT)
|
||||
statuses = models.ManyToManyField("Status", blank=True)
|
||||
status = models.ForeignKey(
|
||||
"Status",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
links = models.ManyToManyField("Link", blank=True)
|
||||
resolved = models.BooleanField(default=False)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
""" the particulars for this instance of BookWyrm """
|
||||
import datetime
|
||||
from urllib.parse import urljoin
|
||||
import uuid
|
||||
|
||||
from django.db import models, IntegrityError
|
||||
from django.dispatch import receiver
|
||||
|
@ -24,6 +25,10 @@ class SiteSettings(models.Model):
|
|||
instance_description = models.TextField(default="This instance has no description.")
|
||||
instance_short_description = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
# admin setup options
|
||||
install_mode = models.BooleanField(default=False)
|
||||
admin_code = models.CharField(max_length=50, default=uuid.uuid4)
|
||||
|
||||
# about page
|
||||
registration_closed_text = models.TextField(
|
||||
default="We aren't taking new users at this time. You can find an open "
|
||||
|
@ -38,7 +43,7 @@ class SiteSettings(models.Model):
|
|||
privacy_policy = models.TextField(default="Add a privacy policy here.")
|
||||
|
||||
# registration
|
||||
allow_registration = models.BooleanField(default=True)
|
||||
allow_registration = models.BooleanField(default=False)
|
||||
allow_invite_requests = models.BooleanField(default=True)
|
||||
require_confirm_email = models.BooleanField(default=True)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue