Merge branch 'main' into misc/add_signatures_to_requests_for_masto_compat
This commit is contained in:
commit
8cc4427e60
272 changed files with 17785 additions and 5282 deletions
|
@ -4,6 +4,7 @@ import sys
|
|||
|
||||
from .book import Book, Work, Edition, BookDataModel
|
||||
from .author import Author
|
||||
from .link import Link, FileLink, LinkDomain
|
||||
from .connector import Connector
|
||||
|
||||
from .shelf import Shelf, ShelfBook
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
""" database schema for info about authors """
|
||||
import re
|
||||
from django.contrib.postgres.indexes import GinIndex
|
||||
from django.core.cache import cache
|
||||
from django.core.cache.utils import make_template_fragment_key
|
||||
from django.db import models
|
||||
|
||||
from bookwyrm import activitypub
|
||||
|
@ -34,6 +36,17 @@ class Author(BookDataModel):
|
|||
)
|
||||
bio = fields.HtmlField(null=True, blank=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""clear related template caches"""
|
||||
# clear template caches
|
||||
if self.id:
|
||||
cache_keys = [
|
||||
make_template_fragment_key("titleby", [book])
|
||||
for book in self.book_set.values_list("id", flat=True)
|
||||
]
|
||||
cache.delete_many(cache_keys)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def isni_link(self):
|
||||
"""generate the url from the isni id"""
|
||||
|
|
|
@ -3,6 +3,8 @@ import re
|
|||
|
||||
from django.contrib.postgres.search import SearchVectorField
|
||||
from django.contrib.postgres.indexes import GinIndex
|
||||
from django.core.cache import cache
|
||||
from django.core.cache.utils import make_template_fragment_key
|
||||
from django.db import models, transaction
|
||||
from django.db.models import Prefetch
|
||||
from django.dispatch import receiver
|
||||
|
@ -185,6 +187,11 @@ class Book(BookDataModel):
|
|||
"""can't be abstract for query reasons, but you shouldn't USE it"""
|
||||
if not isinstance(self, Edition) and not isinstance(self, Work):
|
||||
raise ValueError("Books should be added as Editions or Works")
|
||||
|
||||
# clear template caches
|
||||
cache_key = make_template_fragment_key("titleby", [self.id])
|
||||
cache.delete(cache_key)
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def get_remote_id(self):
|
||||
|
@ -234,8 +241,11 @@ class Work(OrderedCollectionPageMixin, Book):
|
|||
)
|
||||
|
||||
activity_serializer = activitypub.Work
|
||||
serialize_reverse_fields = [("editions", "editions", "-edition_rank")]
|
||||
deserialize_reverse_fields = [("editions", "editions")]
|
||||
serialize_reverse_fields = [
|
||||
("editions", "editions", "-edition_rank"),
|
||||
("file_links", "fileLinks", "-created_date"),
|
||||
]
|
||||
deserialize_reverse_fields = [("editions", "editions"), ("file_links", "fileLinks")]
|
||||
|
||||
|
||||
# https://schema.org/BookFormatType
|
||||
|
@ -289,6 +299,8 @@ class Edition(Book):
|
|||
|
||||
activity_serializer = activitypub.Edition
|
||||
name_field = "title"
|
||||
serialize_reverse_fields = [("file_links", "fileLinks", "-created_date")]
|
||||
deserialize_reverse_fields = [("file_links", "fileLinks")]
|
||||
|
||||
def get_rank(self):
|
||||
"""calculate how complete the data is on this edition"""
|
||||
|
|
|
@ -203,9 +203,12 @@ class UsernameField(ActivitypubFieldMixin, models.CharField):
|
|||
return value.split("@")[0]
|
||||
|
||||
|
||||
PrivacyLevels = models.TextChoices(
|
||||
"Privacy", ["public", "unlisted", "followers", "direct"]
|
||||
)
|
||||
PrivacyLevels = [
|
||||
("public", _("Public")),
|
||||
("unlisted", _("Unlisted")),
|
||||
("followers", _("Followers")),
|
||||
("direct", _("Private")),
|
||||
]
|
||||
|
||||
|
||||
class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||
|
@ -214,9 +217,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
|||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(
|
||||
*args, max_length=255, choices=PrivacyLevels.choices, default="public"
|
||||
)
|
||||
super().__init__(*args, max_length=255, choices=PrivacyLevels, default="public")
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def set_field_from_activity(self, instance, data, overwrite=True):
|
||||
|
@ -516,6 +517,10 @@ class CharField(ActivitypubFieldMixin, models.CharField):
|
|||
"""activitypub-aware char field"""
|
||||
|
||||
|
||||
class URLField(ActivitypubFieldMixin, models.URLField):
|
||||
"""activitypub-aware url field"""
|
||||
|
||||
|
||||
class TextField(ActivitypubFieldMixin, models.TextField):
|
||||
"""activitypub-aware text field"""
|
||||
|
||||
|
|
|
@ -40,9 +40,7 @@ class ImportJob(models.Model):
|
|||
mappings = models.JSONField()
|
||||
complete = models.BooleanField(default=False)
|
||||
source = models.CharField(max_length=100)
|
||||
privacy = models.CharField(
|
||||
max_length=255, default="public", choices=PrivacyLevels.choices
|
||||
)
|
||||
privacy = models.CharField(max_length=255, default="public", choices=PrivacyLevels)
|
||||
retry = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
|
|
85
bookwyrm/models/link.py
Normal file
85
bookwyrm/models/link.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
""" outlink data """
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bookwyrm import activitypub
|
||||
from .activitypub_mixin import ActivitypubMixin
|
||||
from .base_model import BookWyrmModel
|
||||
from . import fields
|
||||
|
||||
|
||||
class Link(ActivitypubMixin, BookWyrmModel):
|
||||
"""a link to a website"""
|
||||
|
||||
url = fields.URLField(max_length=255, activitypub_field="href")
|
||||
added_by = fields.ForeignKey(
|
||||
"User", on_delete=models.SET_NULL, null=True, activitypub_field="attributedTo"
|
||||
)
|
||||
domain = models.ForeignKey(
|
||||
"LinkDomain",
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="links",
|
||||
)
|
||||
|
||||
activity_serializer = activitypub.Link
|
||||
reverse_unfurl = True
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""link name via the assocaited domain"""
|
||||
return self.domain.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""create a link"""
|
||||
# get or create the associated domain
|
||||
if not self.domain:
|
||||
domain = urlparse(self.url).netloc
|
||||
self.domain, _ = LinkDomain.objects.get_or_create(domain=domain)
|
||||
|
||||
# this is never broadcast, the owning model broadcasts an update
|
||||
if "broadcast" in kwargs:
|
||||
del kwargs["broadcast"]
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class FileLink(Link):
|
||||
"""a link to a file"""
|
||||
|
||||
book = models.ForeignKey(
|
||||
"Book", on_delete=models.CASCADE, related_name="file_links", null=True
|
||||
)
|
||||
filetype = fields.CharField(max_length=5, activitypub_field="mediaType")
|
||||
|
||||
|
||||
StatusChoices = [
|
||||
("approved", _("Approved")),
|
||||
("blocked", _("Blocked")),
|
||||
("pending", _("Pending")),
|
||||
]
|
||||
|
||||
|
||||
class LinkDomain(BookWyrmModel):
|
||||
"""List of domains used in links"""
|
||||
|
||||
domain = models.CharField(max_length=255, unique=True)
|
||||
status = models.CharField(max_length=50, choices=StatusChoices, default="pending")
|
||||
name = models.CharField(max_length=100)
|
||||
reported_by = models.ForeignKey(
|
||||
"User", blank=True, null=True, on_delete=models.SET_NULL
|
||||
)
|
||||
|
||||
def raise_not_editable(self, viewer):
|
||||
if viewer.has_perm("moderate_post"):
|
||||
return
|
||||
raise PermissionDenied()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""set a default name"""
|
||||
if not self.name:
|
||||
self.name = self.domain
|
||||
super().save(*args, **kwargs)
|
|
@ -1,5 +1,6 @@
|
|||
""" progress in a book """
|
||||
from django.core import validators
|
||||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.db.models import F, Q
|
||||
|
||||
|
@ -30,6 +31,7 @@ class ReadThrough(BookWyrmModel):
|
|||
|
||||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
cache.delete(f"latest_read_through-{self.user.id}-{self.book.id}")
|
||||
self.user.update_active_date()
|
||||
# an active readthrough must have an unset finish date
|
||||
if self.finish_date:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
""" defines relationships between users """
|
||||
from django.apps import apps
|
||||
from django.core.cache import cache
|
||||
from django.db import models, transaction, IntegrityError
|
||||
from django.db.models import Q
|
||||
|
||||
|
@ -36,6 +37,17 @@ class UserRelationship(BookWyrmModel):
|
|||
"""the remote user needs to recieve direct broadcasts"""
|
||||
return [u for u in [self.user_subject, self.user_object] if not u.local]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""clear the template cache"""
|
||||
# invalidate the template cache
|
||||
cache.delete_many(
|
||||
[
|
||||
f"relationship-{self.user_subject.id}-{self.user_object.id}",
|
||||
f"relationship-{self.user_object.id}-{self.user_subject.id}",
|
||||
]
|
||||
)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
"""relationships should be unique"""
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
""" flagged for moderation """
|
||||
from django.db import models
|
||||
from django.db.models import F, Q
|
||||
from .base_model import BookWyrmModel
|
||||
|
||||
|
||||
|
@ -13,14 +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)
|
||||
links = models.ManyToManyField("Link", blank=True)
|
||||
resolved = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
"""don't let users report themselves"""
|
||||
"""set order by default"""
|
||||
|
||||
constraints = [
|
||||
models.CheckConstraint(check=~Q(reporter=F("user")), name="self_report")
|
||||
]
|
||||
ordering = ("-created_date",)
|
||||
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
|
||||
if not self.reply_parent:
|
||||
self.thread_id = self.id
|
||||
|
||||
super().save(broadcast=False, update_fields=["thread_id"])
|
||||
|
||||
def delete(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
|
|
|
@ -129,7 +129,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
|||
related_name="favorite_statuses",
|
||||
)
|
||||
default_post_privacy = models.CharField(
|
||||
max_length=255, default="public", choices=fields.PrivacyLevels.choices
|
||||
max_length=255, default="public", choices=fields.PrivacyLevels
|
||||
)
|
||||
remote_id = fields.RemoteIdField(null=True, unique=True, activitypub_field="id")
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
|
@ -346,6 +346,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
|||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""deactivate rather than delete a user"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.is_active = False
|
||||
# skip the logic in this class's save()
|
||||
super().save(*args, **kwargs)
|
||||
|
@ -406,14 +407,6 @@ class KeyPair(ActivitypubMixin, BookWyrmModel):
|
|||
self.private_key, self.public_key = create_key_pair()
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def to_activity(self, **kwargs):
|
||||
"""override default AP serializer to add context object
|
||||
idk if this is the best way to go about this"""
|
||||
activity_object = super().to_activity(**kwargs)
|
||||
del activity_object["@context"]
|
||||
del activity_object["type"]
|
||||
return activity_object
|
||||
|
||||
|
||||
def get_current_year():
|
||||
"""sets default year for annual goal to this year"""
|
||||
|
@ -427,7 +420,7 @@ class AnnualGoal(BookWyrmModel):
|
|||
goal = models.IntegerField(validators=[MinValueValidator(1)])
|
||||
year = models.IntegerField(default=get_current_year)
|
||||
privacy = models.CharField(
|
||||
max_length=255, default="public", choices=fields.PrivacyLevels.choices
|
||||
max_length=255, default="public", choices=fields.PrivacyLevels
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue