Merge branch 'main' into book-format-choices
This commit is contained in:
@ -30,6 +30,7 @@ logger = logging.getLogger(__name__)
|
||||
PropertyField = namedtuple("PropertyField", ("set_activity_from_field"))
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def set_activity_from_property_field(activity, obj, field):
|
||||
"""assign a model property value to the activity json"""
|
||||
activity[field[1]] = getattr(obj, field[0])
|
||||
@ -318,7 +319,9 @@ class OrderedCollectionPageMixin(ObjectMixin):
|
||||
|
||||
remote_id = remote_id or self.remote_id
|
||||
if page:
|
||||
return to_ordered_collection_page(queryset, remote_id, **kwargs)
|
||||
if isinstance(page, list) and len(page) > 0:
|
||||
page = page[0]
|
||||
return to_ordered_collection_page(queryset, remote_id, page=page, **kwargs)
|
||||
|
||||
if collection_only or not hasattr(self, "activity_serializer"):
|
||||
serializer = activitypub.OrderedCollection
|
||||
|
@ -1,4 +1,5 @@
|
||||
""" database schema for info about authors """
|
||||
from django.contrib.postgres.indexes import GinIndex
|
||||
from django.db import models
|
||||
|
||||
from bookwyrm import activitypub
|
||||
@ -37,3 +38,8 @@ class Author(BookDataModel):
|
||||
return "https://%s/author/%s" % (DOMAIN, self.id)
|
||||
|
||||
activity_serializer = activitypub.Author
|
||||
|
||||
class Meta:
|
||||
"""sets up postgres GIN index field"""
|
||||
|
||||
indexes = (GinIndex(fields=["search_vector"]),)
|
||||
|
@ -1,6 +1,8 @@
|
||||
""" database schema for books and shelves """
|
||||
import re
|
||||
|
||||
from django.contrib.postgres.search import SearchVectorField
|
||||
from django.contrib.postgres.indexes import GinIndex
|
||||
from django.db import models
|
||||
from django.dispatch import receiver
|
||||
from model_utils import FieldTracker
|
||||
@ -34,6 +36,7 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
|
||||
bnf_id = fields.CharField( # Bibliothèque nationale de France
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
search_vector = SearchVectorField(null=True)
|
||||
|
||||
last_edited_by = fields.ForeignKey(
|
||||
"User",
|
||||
@ -142,6 +145,11 @@ class Book(BookDataModel):
|
||||
self.title,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""sets up postgres GIN index field"""
|
||||
|
||||
indexes = (GinIndex(fields=["search_vector"]),)
|
||||
|
||||
|
||||
class Work(OrderedCollectionPageMixin, Book):
|
||||
"""a work (an abstract concept of a book that manifests in an edition)"""
|
||||
|
@ -30,7 +30,7 @@ class Favorite(ActivityMixin, BookWyrmModel):
|
||||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if self.status.user.local and self.status.user != self.user:
|
||||
|
@ -13,6 +13,7 @@ from django.db import models
|
||||
from django.forms import ClearableFileInput, ImageField as DjangoImageField
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bookwyrm import activitypub
|
||||
from bookwyrm.connectors import get_image
|
||||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
@ -66,7 +67,7 @@ class ActivitypubFieldMixin:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def set_field_from_activity(self, instance, data):
|
||||
"""helper function for assinging a value to the field"""
|
||||
"""helper function for assinging a value to the field. Returns if changed"""
|
||||
try:
|
||||
value = getattr(data, self.get_activitypub_field())
|
||||
except AttributeError:
|
||||
@ -76,8 +77,14 @@ class ActivitypubFieldMixin:
|
||||
value = getattr(data, "actor")
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING or formatted == {}:
|
||||
return
|
||||
return False
|
||||
|
||||
# the field is unchanged
|
||||
if hasattr(instance, self.name) and getattr(instance, self.name) == formatted:
|
||||
return False
|
||||
|
||||
setattr(instance, self.name, formatted)
|
||||
return True
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
"""update the json object"""
|
||||
@ -204,6 +211,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def set_field_from_activity(self, instance, data):
|
||||
original = getattr(instance, self.name)
|
||||
to = data.to
|
||||
cc = data.cc
|
||||
if to == [self.public]:
|
||||
@ -214,6 +222,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||
setattr(instance, self.name, "unlisted")
|
||||
else:
|
||||
setattr(instance, self.name, "followers")
|
||||
return original == getattr(instance, self.name)
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
# explicitly to anyone mentioned (statuses only)
|
||||
@ -269,9 +278,10 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField):
|
||||
value = getattr(data, self.get_activitypub_field())
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING:
|
||||
return
|
||||
return False
|
||||
getattr(instance, self.name).set(formatted)
|
||||
instance.save(broadcast=False)
|
||||
return True
|
||||
|
||||
def field_to_activity(self, value):
|
||||
if self.link_only:
|
||||
@ -354,7 +364,8 @@ def image_serializer(value, alt):
|
||||
url = value.url
|
||||
else:
|
||||
return None
|
||||
url = "https://%s%s" % (DOMAIN, url)
|
||||
if not url[:4] == "http":
|
||||
url = "https://{:s}{:s}".format(DOMAIN, url)
|
||||
return activitypub.Document(url=url, name=alt)
|
||||
|
||||
|
||||
@ -371,8 +382,10 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
||||
value = getattr(data, self.get_activitypub_field())
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING:
|
||||
return
|
||||
return False
|
||||
|
||||
getattr(instance, self.name).save(*formatted, save=save)
|
||||
return True
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
value = getattr(instance, self.name)
|
||||
@ -408,7 +421,8 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
||||
return None
|
||||
|
||||
image_content = ContentFile(response.content)
|
||||
image_name = str(uuid4()) + "." + imghdr.what(None, image_content.read())
|
||||
extension = imghdr.what(None, image_content.read()) or ""
|
||||
image_name = "{:s}.{:s}".format(str(uuid4()), extension)
|
||||
return [image_name, image_content]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
|
@ -30,7 +30,7 @@ class ReadThrough(BookWyrmModel):
|
||||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def create_update(self):
|
||||
@ -55,5 +55,5 @@ class ProgressUpdate(BookWyrmModel):
|
||||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
@ -1,6 +1,7 @@
|
||||
""" puttin' books on shelves """
|
||||
import re
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import activitypub
|
||||
from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin
|
||||
@ -69,6 +70,7 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel):
|
||||
"Edition", on_delete=models.PROTECT, activitypub_field="book"
|
||||
)
|
||||
shelf = models.ForeignKey("Shelf", on_delete=models.PROTECT)
|
||||
shelved_date = models.DateTimeField(default=timezone.now)
|
||||
user = fields.ForeignKey(
|
||||
"User", on_delete=models.PROTECT, activitypub_field="actor"
|
||||
)
|
||||
@ -86,4 +88,4 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel):
|
||||
you can't put a book on shelf twice"""
|
||||
|
||||
unique_together = ("book", "shelf")
|
||||
ordering = ("-created_date",)
|
||||
ordering = ("-shelved_date", "-created_date", "-updated_date")
|
||||
|
@ -7,7 +7,7 @@ from django.contrib.auth.models import AbstractUser, Group
|
||||
from django.contrib.postgres.fields import CICharField
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.dispatch import receiver
|
||||
from django.db import models
|
||||
from django.db import models, transaction
|
||||
from django.utils import timezone
|
||||
from model_utils import FieldTracker
|
||||
import pytz
|
||||
@ -105,6 +105,9 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||
through_fields=("user", "status"),
|
||||
related_name="favorite_statuses",
|
||||
)
|
||||
default_post_privacy = models.CharField(
|
||||
max_length=255, default="public", choices=fields.PrivacyLevels.choices
|
||||
)
|
||||
remote_id = fields.RemoteIdField(null=True, unique=True, activitypub_field="id")
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
updated_date = models.DateTimeField(auto_now=True)
|
||||
@ -243,7 +246,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||
# generate a username that uses the domain (webfinger format)
|
||||
actor_parts = urlparse(self.remote_id)
|
||||
self.username = "%s@%s" % (self.username, actor_parts.netloc)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# this user already exists, no need to populate fields
|
||||
if not created:
|
||||
@ -253,7 +255,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||
# this is a new remote user, we need to set their remote server field
|
||||
if not self.local:
|
||||
super().save(*args, **kwargs)
|
||||
set_remote_server.delay(self.id)
|
||||
transaction.on_commit(lambda: set_remote_server.delay(self.id))
|
||||
return
|
||||
|
||||
# populate fields for local users
|
||||
@ -276,7 +278,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||
self.key_pair = KeyPair.objects.create(
|
||||
remote_id="%s/#main-key" % self.remote_id
|
||||
)
|
||||
self.save(broadcast=False)
|
||||
self.save(broadcast=False, update_fields=["key_pair"])
|
||||
|
||||
shelves = [
|
||||
{
|
||||
@ -406,7 +408,7 @@ def set_remote_server(user_id):
|
||||
user = User.objects.get(id=user_id)
|
||||
actor_parts = urlparse(user.remote_id)
|
||||
user.federated_server = get_or_create_remote_server(actor_parts.netloc)
|
||||
user.save(broadcast=False)
|
||||
user.save(broadcast=False, update_fields=["federated_server"])
|
||||
if user.bookwyrm_user and user.outbox:
|
||||
get_remote_reviews.delay(user.outbox)
|
||||
|
||||
|
Reference in New Issue
Block a user