1
0
Fork 0

Merge remote-tracking branch 'upstream/main' into images-django-imagekit

This commit is contained in:
Joachim 2021-06-19 19:32:56 +02:00
commit e251b687dc
171 changed files with 3674 additions and 2175 deletions

View file

@ -2,11 +2,14 @@
import re
from django.db import models
from django.dispatch import receiver
from model_utils import FieldTracker
from model_utils.managers import InheritanceManager
from imagekit.models import ImageSpecField
from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE, ENABLE_THUMBNAIL_GENERATION
from bookwyrm.preview_images import generate_edition_preview_image_task
from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE, ENABLE_PREVIEW_IMAGES, ENABLE_THUMBNAIL_GENERATION
from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
from .base_model import BookWyrmModel
@ -83,10 +86,14 @@ class Book(BookDataModel):
cover = fields.ImageField(
upload_to="covers/", blank=True, null=True, alt_field="alt_text"
)
preview_image = models.ImageField(
upload_to="previews/covers/", blank=True, null=True
)
first_published_date = fields.DateTimeField(blank=True, null=True)
published_date = fields.DateTimeField(blank=True, null=True)
objects = InheritanceManager()
field_tracker = FieldTracker(fields=["authors", "title", "subtitle", "cover"])
if ENABLE_THUMBNAIL_GENERATION:
cover_bw_book_xsmall_webp = ImageSpecField(
@ -328,3 +335,17 @@ def isbn_13_to_10(isbn_13):
if checkdigit == 10:
checkdigit = "X"
return converted + str(checkdigit)
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=Edition)
def preview_image(instance, *args, **kwargs):
"""create preview image on book create"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = {}
if instance.field_tracker:
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_edition_preview_image_task.delay(instance.id)

View file

@ -1,5 +1,6 @@
""" activitypub-aware django model fields """
from dataclasses import MISSING
import imghdr
import re
from uuid import uuid4
@ -9,7 +10,7 @@ from django.contrib.postgres.fields import ArrayField as DjangoArrayField
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.db import models
from django.forms import ClearableFileInput, ImageField
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
@ -201,6 +202,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
*args, max_length=255, choices=PrivacyLevels.choices, default="public"
)
# pylint: disable=invalid-name
def set_field_from_activity(self, instance, data):
to = data.to
cc = data.cc
@ -219,6 +221,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
if hasattr(instance, "mention_users"):
mentions = [u.remote_id for u in instance.mention_users.all()]
# this is a link to the followers list
# pylint: disable=protected-access
followers = instance.user.__class__._meta.get_field(
"followers"
).field_to_activity(instance.user.followers)
@ -334,10 +337,14 @@ class TagField(ManyToManyField):
class ClearableFileInputWithWarning(ClearableFileInput):
"""max file size warning"""
template_name = "widgets/clearable_file_input_with_warning.html"
class CustomImageField(ImageField):
class CustomImageField(DjangoImageField):
"""overwrites image field for form"""
widget = ClearableFileInputWithWarning
@ -400,11 +407,12 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
if not response:
return None
image_name = str(uuid4()) + "." + url.split(".")[-1]
image_content = ContentFile(response.content)
image_name = str(uuid4()) + "." + imghdr.what(None, image_content.read())
return [image_name, image_content]
def formfield(self, **kwargs):
"""special case for forms"""
return super().formfield(
**{
"form_class": CustomImageField,

View file

@ -75,7 +75,12 @@ class ImportItem(models.Model):
def resolve(self):
"""try various ways to lookup a book"""
self.book = self.get_book_from_isbn() or self.get_book_from_title_author()
if self.isbn:
self.book = self.get_book_from_isbn()
else:
# don't fall back on title/author search is isbn is present.
# you're too likely to mismatch
self.get_book_from_title_author()
def get_book_from_isbn(self):
"""search by isbn"""

View file

@ -93,7 +93,8 @@ class ListItem(CollectionItemMixin, BookWyrmModel):
)
class Meta:
# A book may only be placed into a list once, and each order in the list may be used only
# once
"""A book may only be placed into a list once,
and each order in the list may be used only once"""
unique_together = (("book", "book_list"), ("order", "book_list"))
ordering = ("-created_date",)

View file

@ -99,7 +99,7 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
status = "follow_request"
activity_serializer = activitypub.Follow
def save(self, *args, broadcast=True, **kwargs):
def save(self, *args, broadcast=True, **kwargs): # pylint: disable=arguments-differ
"""make sure the follow or block relationship doesn't already exist"""
# if there's a request for a follow that already exists, accept it
# without changing the local database state

View file

@ -4,9 +4,12 @@ import datetime
from Crypto import Random
from django.db import models, IntegrityError
from django.dispatch import receiver
from django.utils import timezone
from model_utils import FieldTracker
from bookwyrm.settings import DOMAIN
from bookwyrm.preview_images import generate_site_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES
from .base_model import BookWyrmModel
from .user import User
@ -35,6 +38,9 @@ class SiteSettings(models.Model):
logo = models.ImageField(upload_to="logos/", null=True, blank=True)
logo_small = models.ImageField(upload_to="logos/", null=True, blank=True)
favicon = models.ImageField(upload_to="logos/", null=True, blank=True)
preview_image = models.ImageField(
upload_to="previews/logos/", null=True, blank=True
)
# footer
support_link = models.CharField(max_length=255, null=True, blank=True)
@ -42,6 +48,8 @@ class SiteSettings(models.Model):
admin_email = models.EmailField(max_length=255, null=True, blank=True)
footer_item = models.TextField(null=True, blank=True)
field_tracker = FieldTracker(fields=["name", "instance_tagline", "logo"])
@classmethod
def get(cls):
"""gets the site settings db entry or defaults"""
@ -119,3 +127,15 @@ class PasswordReset(models.Model):
def link(self):
"""formats the invite link"""
return "https://{}/password-reset/{}".format(DOMAIN, self.code)
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=SiteSettings)
def preview_image(instance, *args, **kwargs):
"""Update image preview for the default site image"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_site_preview_image_task.delay()

View file

@ -5,11 +5,15 @@ import re
from django.apps import apps
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.dispatch import receiver
from django.template.loader import get_template
from django.utils import timezone
from model_utils import FieldTracker
from model_utils.managers import InheritanceManager
from bookwyrm import activitypub
from bookwyrm.preview_images import generate_edition_preview_image_task
from bookwyrm.settings import ENABLE_PREVIEW_IMAGES
from .activitypub_mixin import ActivitypubMixin, ActivityMixin
from .activitypub_mixin import OrderedCollectionPageMixin
from .base_model import BookWyrmModel
@ -304,6 +308,8 @@ class Review(Status):
max_digits=3,
)
field_tracker = FieldTracker(fields=["rating"])
@property
def pure_name(self):
"""clarify review names for mastodon serialization"""
@ -398,3 +404,17 @@ class Boost(ActivityMixin, Status):
# This constraint can't work as it would cross tables.
# class Meta:
# unique_together = ('user', 'boosted_status')
# pylint: disable=unused-argument
@receiver(models.signals.post_save)
def preview_image(instance, sender, *args, **kwargs):
"""Updates book previews if the rating has changed"""
if not ENABLE_PREVIEW_IMAGES or sender not in (Review, ReviewRating):
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
edition = instance.book
generate_edition_preview_image_task.delay(edition.id)

View file

@ -6,15 +6,18 @@ from django.apps import apps
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.utils import timezone
from model_utils import FieldTracker
import pytz
from bookwyrm import activitypub
from bookwyrm.connectors import get_data, ConnectorException
from bookwyrm.models.shelf import Shelf
from bookwyrm.models.status import Status, Review
from bookwyrm.settings import DOMAIN
from bookwyrm.preview_images import generate_user_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES
from bookwyrm.signatures import create_key_pair
from bookwyrm.tasks import app
from bookwyrm.utils import regex
@ -70,6 +73,9 @@ class User(OrderedCollectionPageMixin, AbstractUser):
activitypub_field="icon",
alt_field="alt_text",
)
preview_image = models.ImageField(
upload_to="previews/avatars/", blank=True, null=True
)
followers = fields.ManyToManyField(
"self",
link_only=True,
@ -117,6 +123,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
name_field = "username"
property_fields = [("following_link", "following")]
field_tracker = FieldTracker(fields=["name", "avatar"])
@property
def following_link(self):
@ -232,7 +239,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
def save(self, *args, **kwargs):
"""populate fields for new local users"""
created = not bool(self.id)
if not self.local and not re.match(regex.full_username, self.username):
if not self.local and not re.match(regex.FULL_USERNAME, self.username):
# generate a username that uses the domain (webfinger format)
actor_parts = urlparse(self.remote_id)
self.username = "%s@%s" % (self.username, actor_parts.netloc)
@ -356,7 +363,7 @@ class AnnualGoal(BookWyrmModel):
def get_remote_id(self):
"""put the year in the path"""
return "%s/goal/%d" % (self.user.remote_id, self.year)
return "{:s}/goal/{:d}".format(self.user.remote_id, self.year)
@property
def books(self):
@ -443,3 +450,15 @@ def get_remote_reviews(outbox):
if not activity["type"] == "Review":
continue
activitypub.Review(**activity).to_model()
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=User)
def preview_image(instance, *args, **kwargs):
"""create preview images when user is updated"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_user_preview_image_task.delay(instance.id)