Merge branch 'main' into notifications
This commit is contained in:
commit
a718a168a3
168 changed files with 4564 additions and 3141 deletions
|
@ -8,6 +8,7 @@ from django.db.models import Q
|
|||
from django.dispatch import receiver
|
||||
from django.http import Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.text import slugify
|
||||
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from .fields import RemoteIdField
|
||||
|
@ -35,10 +36,11 @@ class BookWyrmModel(models.Model):
|
|||
remote_id = RemoteIdField(null=True, activitypub_field="id")
|
||||
|
||||
def get_remote_id(self):
|
||||
"""generate a url that resolves to the local object"""
|
||||
"""generate the url that resolves to the local object, without a slug"""
|
||||
base_path = f"https://{DOMAIN}"
|
||||
if hasattr(self, "user"):
|
||||
base_path = f"{base_path}{self.user.local_path}"
|
||||
|
||||
model_name = type(self).__name__.lower()
|
||||
return f"{base_path}/{model_name}/{self.id}"
|
||||
|
||||
|
@ -49,8 +51,20 @@ class BookWyrmModel(models.Model):
|
|||
|
||||
@property
|
||||
def local_path(self):
|
||||
"""how to link to this object in the local app"""
|
||||
return self.get_remote_id().replace(f"https://{DOMAIN}", "")
|
||||
"""how to link to this object in the local app, with a slug"""
|
||||
local = self.get_remote_id().replace(f"https://{DOMAIN}", "")
|
||||
|
||||
name = None
|
||||
if hasattr(self, "name_field"):
|
||||
name = getattr(self, self.name_field)
|
||||
elif hasattr(self, "name"):
|
||||
name = self.name
|
||||
|
||||
if name:
|
||||
slug = slugify(name)
|
||||
local = f"{local}/s/{slug}"
|
||||
|
||||
return local
|
||||
|
||||
def raise_visible_to_user(self, viewer):
|
||||
"""is a user authorized to view an object?"""
|
||||
|
|
|
@ -176,8 +176,8 @@ class Book(BookDataModel):
|
|||
"""properties of this edition, as a string"""
|
||||
items = [
|
||||
self.physical_format if hasattr(self, "physical_format") else None,
|
||||
self.languages[0] + " language"
|
||||
if self.languages and self.languages[0] != "English"
|
||||
f"{self.languages[0]} language"
|
||||
if self.languages and self.languages[0] and self.languages[0] != "English"
|
||||
else None,
|
||||
str(self.published_date.year) if self.published_date else None,
|
||||
", ".join(self.publishers) if hasattr(self, "publishers") else None,
|
||||
|
|
|
@ -16,7 +16,7 @@ from django.utils.encoding import filepath_to_uri
|
|||
|
||||
from bookwyrm import activitypub
|
||||
from bookwyrm.connectors import get_image
|
||||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
from bookwyrm.utils.sanitizer import clean
|
||||
from bookwyrm.settings import MEDIA_FULL_URL
|
||||
|
||||
|
||||
|
@ -497,9 +497,7 @@ class HtmlField(ActivitypubFieldMixin, models.TextField):
|
|||
def field_from_activity(self, value):
|
||||
if not value or value == MISSING:
|
||||
return None
|
||||
sanitizer = InputHtmlParser()
|
||||
sanitizer.feed(value)
|
||||
return sanitizer.get_output()
|
||||
return clean(value)
|
||||
|
||||
|
||||
class ArrayField(ActivitypubFieldMixin, DjangoArrayField):
|
||||
|
|
|
@ -175,9 +175,15 @@ class ImportItem(models.Model):
|
|||
def date_added(self):
|
||||
"""when the book was added to this dataset"""
|
||||
if self.normalized_data.get("date_added"):
|
||||
return timezone.make_aware(
|
||||
dateutil.parser.parse(self.normalized_data.get("date_added"))
|
||||
parsed_date_added = dateutil.parser.parse(
|
||||
self.normalized_data.get("date_added")
|
||||
)
|
||||
|
||||
if timezone.is_aware(parsed_date_added):
|
||||
# Keep timezone if import already had one
|
||||
return parsed_date_added
|
||||
|
||||
return timezone.make_aware(parsed_date_added)
|
||||
return None
|
||||
|
||||
@property
|
||||
|
|
|
@ -129,7 +129,7 @@ class List(OrderedCollectionMixin, BookWyrmModel):
|
|||
"""on save, update embed_key and avoid clash with existing code"""
|
||||
if not self.embed_key:
|
||||
self.embed_key = uuid.uuid4()
|
||||
return super().save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ListItem(CollectionItemMixin, BookWyrmModel):
|
||||
|
@ -156,7 +156,7 @@ class ListItem(CollectionItemMixin, BookWyrmModel):
|
|||
super().save(*args, **kwargs)
|
||||
# tick the updated date on the parent list
|
||||
self.book_list.updated_date = timezone.now()
|
||||
self.book_list.save(broadcast=False)
|
||||
self.book_list.save(broadcast=False, update_fields=["updated_date"])
|
||||
|
||||
list_owner = self.book_list.user
|
||||
model = apps.get_model("bookwyrm.Notification", require_ready=True)
|
||||
|
|
|
@ -27,6 +27,7 @@ class ReadThrough(BookWyrmModel):
|
|||
)
|
||||
start_date = models.DateTimeField(blank=True, null=True)
|
||||
finish_date = models.DateTimeField(blank=True, null=True)
|
||||
stopped_date = models.DateTimeField(blank=True, null=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
@ -34,7 +35,7 @@ class ReadThrough(BookWyrmModel):
|
|||
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:
|
||||
if self.finish_date or self.stopped_date:
|
||||
self.is_active = False
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ def clear_cache(user_subject, user_object):
|
|||
"""clear relationship cache"""
|
||||
cache.delete_many(
|
||||
[
|
||||
f"relationship-{user_subject.id}-{user_object.id}",
|
||||
f"relationship-{user_object.id}-{user_subject.id}",
|
||||
f"cached-relationship-{user_subject.id}-{user_object.id}",
|
||||
f"cached-relationship-{user_object.id}-{user_subject.id}",
|
||||
]
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.db import models
|
|||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import activitypub
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin
|
||||
from .base_model import BookWyrmModel
|
||||
from . import fields
|
||||
|
@ -17,8 +18,9 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
|||
TO_READ = "to-read"
|
||||
READING = "reading"
|
||||
READ_FINISHED = "read"
|
||||
STOPPED_READING = "stopped-reading"
|
||||
|
||||
READ_STATUS_IDENTIFIERS = (TO_READ, READING, READ_FINISHED)
|
||||
READ_STATUS_IDENTIFIERS = (TO_READ, READING, READ_FINISHED, STOPPED_READING)
|
||||
|
||||
name = fields.CharField(max_length=100)
|
||||
identifier = models.CharField(max_length=100)
|
||||
|
@ -65,6 +67,11 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
|||
identifier = self.identifier or self.get_identifier()
|
||||
return f"{base_path}/books/{identifier}"
|
||||
|
||||
@property
|
||||
def local_path(self):
|
||||
"""No slugs"""
|
||||
return self.get_remote_id().replace(f"https://{DOMAIN}", "")
|
||||
|
||||
def raise_not_deletable(self, viewer):
|
||||
"""don't let anyone delete a default shelf"""
|
||||
super().raise_not_deletable(viewer)
|
||||
|
|
|
@ -116,11 +116,8 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
def ignore_activity(cls, activity): # pylint: disable=too-many-return-statements
|
||||
"""keep notes if they are replies to existing statuses"""
|
||||
if activity.type == "Announce":
|
||||
try:
|
||||
boosted = activitypub.resolve_remote_id(
|
||||
activity.object, get_activity=True
|
||||
)
|
||||
except activitypub.ActivitySerializerError:
|
||||
boosted = activitypub.resolve_remote_id(activity.object, get_activity=True)
|
||||
if not boosted:
|
||||
# if we can't load the status, definitely ignore it
|
||||
return True
|
||||
# keep the boost if we would keep the status
|
||||
|
@ -265,7 +262,7 @@ class GeneratedNote(Status):
|
|||
|
||||
|
||||
ReadingStatusChoices = models.TextChoices(
|
||||
"ReadingStatusChoices", ["to-read", "reading", "read"]
|
||||
"ReadingStatusChoices", ["to-read", "reading", "read", "stopped-reading"]
|
||||
)
|
||||
|
||||
|
||||
|
@ -306,10 +303,17 @@ class Comment(BookStatus):
|
|||
@property
|
||||
def pure_content(self):
|
||||
"""indicate the book in question for mastodon (or w/e) users"""
|
||||
return (
|
||||
f'{self.content}<p>(comment on <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a>)</p>'
|
||||
)
|
||||
if self.progress_mode == "PG" and self.progress and (self.progress > 0):
|
||||
return_value = (
|
||||
f'{self.content}<p>(comment on <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a>, page {self.progress})</p>'
|
||||
)
|
||||
else:
|
||||
return_value = (
|
||||
f'{self.content}<p>(comment on <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a>)</p>'
|
||||
)
|
||||
return return_value
|
||||
|
||||
activity_serializer = activitypub.Comment
|
||||
|
||||
|
@ -335,10 +339,17 @@ class Quotation(BookStatus):
|
|||
"""indicate the book in question for mastodon (or w/e) users"""
|
||||
quote = re.sub(r"^<p>", '<p>"', self.quote)
|
||||
quote = re.sub(r"</p>$", '"</p>', quote)
|
||||
return (
|
||||
f'{quote} <p>-- <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a></p>{self.content}'
|
||||
)
|
||||
if self.position_mode == "PG" and self.position and (self.position > 0):
|
||||
return_value = (
|
||||
f'{quote} <p>-- <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a>, page {self.position}</p>{self.content}'
|
||||
)
|
||||
else:
|
||||
return_value = (
|
||||
f'{quote} <p>-- <a href="{self.book.remote_id}">'
|
||||
f'"{self.book.title}"</a></p>{self.content}'
|
||||
)
|
||||
return return_value
|
||||
|
||||
activity_serializer = activitypub.Quotation
|
||||
|
||||
|
@ -377,7 +388,7 @@ class Review(BookStatus):
|
|||
def save(self, *args, **kwargs):
|
||||
"""clear rating caches"""
|
||||
if self.book.parent_work:
|
||||
cache.delete(f"book-rating-{self.book.parent_work.id}-*")
|
||||
cache.delete(f"book-rating-{self.book.parent_work.id}")
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
|
@ -374,6 +374,10 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
|||
"name": "Read",
|
||||
"identifier": "read",
|
||||
},
|
||||
{
|
||||
"name": "Stopped Reading",
|
||||
"identifier": "stopped-reading",
|
||||
},
|
||||
]
|
||||
|
||||
for shelf in shelves:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue