commit
d033848d3f
58 changed files with 85 additions and 85 deletions
|
@ -8,7 +8,7 @@ USE_HTTPS=true
|
||||||
DOMAIN=your.domain.here
|
DOMAIN=your.domain.here
|
||||||
EMAIL=your@email.here
|
EMAIL=your@email.here
|
||||||
|
|
||||||
# Instance defualt language (see options at bookwyrm/settings.py "LANGUAGES"
|
# Instance default language (see options at bookwyrm/settings.py "LANGUAGES"
|
||||||
LANGUAGE_CODE="en-us"
|
LANGUAGE_CODE="en-us"
|
||||||
# Used for deciding which editions to prefer
|
# Used for deciding which editions to prefer
|
||||||
DEFAULT_LANGUAGE="English"
|
DEFAULT_LANGUAGE="English"
|
||||||
|
|
|
@ -104,7 +104,7 @@ class ActivityStream(RedisStore):
|
||||||
|
|
||||||
def _get_audience(self, status): # pylint: disable=no-self-use
|
def _get_audience(self, status): # pylint: disable=no-self-use
|
||||||
"""given a status, what users should see it"""
|
"""given a status, what users should see it"""
|
||||||
# direct messages don't appeard in feeds, direct comments/reviews/etc do
|
# direct messages don't appear in feeds, direct comments/reviews/etc do
|
||||||
if status.privacy == "direct" and status.status_type == "Note":
|
if status.privacy == "direct" and status.status_type == "Note":
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ class AbstractMinimalConnector(ABC):
|
||||||
return f"{self.search_url}{quote_plus(query)}"
|
return f"{self.search_url}{quote_plus(query)}"
|
||||||
|
|
||||||
def process_search_response(self, query, data, min_confidence):
|
def process_search_response(self, query, data, min_confidence):
|
||||||
"""Format the search results based on the formt of the query"""
|
"""Format the search results based on the format of the query"""
|
||||||
if maybe_isbn(query):
|
if maybe_isbn(query):
|
||||||
return list(self.parse_isbn_search_data(data))[:10]
|
return list(self.parse_isbn_search_data(data))[:10]
|
||||||
return list(self.parse_search_data(data, min_confidence))[:10]
|
return list(self.parse_search_data(data, min_confidence))[:10]
|
||||||
|
@ -321,7 +321,7 @@ def infer_physical_format(format_text):
|
||||||
|
|
||||||
|
|
||||||
def unique_physical_format(format_text):
|
def unique_physical_format(format_text):
|
||||||
"""only store the format if it isn't diretly in the format mappings"""
|
"""only store the format if it isn't directly in the format mappings"""
|
||||||
format_text = format_text.lower()
|
format_text = format_text.lower()
|
||||||
if format_text in format_mappings:
|
if format_text in format_mappings:
|
||||||
# try a direct match, so saving this would be redundant
|
# try a direct match, so saving this would be redundant
|
||||||
|
|
|
@ -73,7 +73,7 @@ async def async_connector_search(query, items, min_confidence):
|
||||||
|
|
||||||
|
|
||||||
def search(query, min_confidence=0.1, return_first=False):
|
def search(query, min_confidence=0.1, return_first=False):
|
||||||
"""find books based on arbitary keywords"""
|
"""find books based on arbitrary keywords"""
|
||||||
if not query:
|
if not query:
|
||||||
return []
|
return []
|
||||||
results = []
|
results = []
|
||||||
|
|
|
@ -97,7 +97,7 @@ class Connector(AbstractConnector):
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse_isbn_search_data(self, data):
|
def parse_isbn_search_data(self, data):
|
||||||
"""got some daaaata"""
|
"""got some data"""
|
||||||
results = data.get("entities")
|
results = data.get("entities")
|
||||||
if not results:
|
if not results:
|
||||||
return
|
return
|
||||||
|
|
|
@ -15,7 +15,7 @@ from .custom_form import CustomForm, StyledForm
|
||||||
# pylint: disable=missing-class-docstring
|
# pylint: disable=missing-class-docstring
|
||||||
class ExpiryWidget(widgets.Select):
|
class ExpiryWidget(widgets.Select):
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
"""human-readable exiration time buckets"""
|
"""human-readable expiration time buckets"""
|
||||||
selected_string = super().value_from_datadict(data, files, name)
|
selected_string = super().value_from_datadict(data, files, name)
|
||||||
|
|
||||||
if selected_string == "day":
|
if selected_string == "day":
|
||||||
|
|
|
@ -86,14 +86,14 @@ class ListsStream(RedisStore):
|
||||||
if group:
|
if group:
|
||||||
audience = audience.filter(
|
audience = audience.filter(
|
||||||
Q(id=book_list.user.id) # if the user is the list's owner
|
Q(id=book_list.user.id) # if the user is the list's owner
|
||||||
| Q(following=book_list.user) # if the user is following the pwmer
|
| Q(following=book_list.user) # if the user is following the owner
|
||||||
# if a user is in the group
|
# if a user is in the group
|
||||||
| Q(memberships__group__id=book_list.group.id)
|
| Q(memberships__group__id=book_list.group.id)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
audience = audience.filter(
|
audience = audience.filter(
|
||||||
Q(id=book_list.user.id) # if the user is the list's owner
|
Q(id=book_list.user.id) # if the user is the list's owner
|
||||||
| Q(following=book_list.user) # if the user is following the pwmer
|
| Q(following=book_list.user) # if the user is following the owner
|
||||||
)
|
)
|
||||||
return audience.distinct()
|
return audience.distinct()
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,12 @@ def dedupe_model(model):
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""dedplucate allllll the book data models"""
|
"""deduplicate allllll the book data models"""
|
||||||
|
|
||||||
help = "merges duplicate book data"
|
help = "merges duplicate book data"
|
||||||
# pylint: disable=no-self-use,unused-argument
|
# pylint: disable=no-self-use,unused-argument
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""run deudplications"""
|
"""run deduplications"""
|
||||||
dedupe_model(models.Edition)
|
dedupe_model(models.Edition)
|
||||||
dedupe_model(models.Work)
|
dedupe_model(models.Work)
|
||||||
dedupe_model(models.Author)
|
dedupe_model(models.Author)
|
||||||
|
|
|
@ -33,10 +33,10 @@ def remove_editions():
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""dedplucate allllll the book data models"""
|
"""deduplicate allllll the book data models"""
|
||||||
|
|
||||||
help = "merges duplicate book data"
|
help = "merges duplicate book data"
|
||||||
# pylint: disable=no-self-use,unused-argument
|
# pylint: disable=no-self-use,unused-argument
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""run deudplications"""
|
"""run deduplications"""
|
||||||
remove_editions()
|
remove_editions()
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""reveoke nonessential low priority tasks"""
|
"""revoke nonessential low priority tasks"""
|
||||||
types = [
|
types = [
|
||||||
"bookwyrm.preview_images.generate_edition_preview_image_task",
|
"bookwyrm.preview_images.generate_edition_preview_image_task",
|
||||||
"bookwyrm.preview_images.generate_user_preview_image_task",
|
"bookwyrm.preview_images.generate_user_preview_image_task",
|
||||||
|
|
|
@ -1467,7 +1467,7 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"expiry",
|
"expiry",
|
||||||
models.DateTimeField(
|
models.DateTimeField(
|
||||||
default=bookwyrm.models.site.get_passowrd_reset_expiry
|
default=bookwyrm.models.site.get_password_reset_expiry
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
@ -6,7 +6,7 @@ from bookwyrm.connectors.abstract_connector import infer_physical_format
|
||||||
|
|
||||||
|
|
||||||
def infer_format(app_registry, schema_editor):
|
def infer_format(app_registry, schema_editor):
|
||||||
"""set the new phsyical format field based on existing format data"""
|
"""set the new physical format field based on existing format data"""
|
||||||
db_alias = schema_editor.connection.alias
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
editions = (
|
editions = (
|
||||||
|
|
|
@ -5,7 +5,7 @@ from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
def remove_self_connector(app_registry, schema_editor):
|
def remove_self_connector(app_registry, schema_editor):
|
||||||
"""set the new phsyical format field based on existing format data"""
|
"""set the new physical format field based on existing format data"""
|
||||||
db_alias = schema_editor.connection.alias
|
db_alias = schema_editor.connection.alias
|
||||||
app_registry.get_model("bookwyrm", "Connector").objects.using(db_alias).filter(
|
app_registry.get_model("bookwyrm", "Connector").objects.using(db_alias).filter(
|
||||||
connector_file="self_connector"
|
connector_file="self_connector"
|
||||||
|
|
|
@ -25,7 +25,7 @@ from bookwyrm.tasks import app, MEDIUM, BROADCAST
|
||||||
from bookwyrm.models.fields import ImageField, ManyToManyField
|
from bookwyrm.models.fields import ImageField, ManyToManyField
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
# I tried to separate these classes into mutliple files but I kept getting
|
# I tried to separate these classes into multiple files but I kept getting
|
||||||
# circular import errors so I gave up. I'm sure it could be done though!
|
# circular import errors so I gave up. I'm sure it could be done though!
|
||||||
|
|
||||||
PropertyField = namedtuple("PropertyField", ("set_activity_from_field"))
|
PropertyField = namedtuple("PropertyField", ("set_activity_from_field"))
|
||||||
|
@ -91,7 +91,7 @@ class ActivitypubMixin:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_existing(cls, data):
|
def find_existing(cls, data):
|
||||||
"""compare data to fields that can be used for deduplation.
|
"""compare data to fields that can be used for deduplication.
|
||||||
This always includes remote_id, but can also be unique identifiers
|
This always includes remote_id, but can also be unique identifiers
|
||||||
like an isbn for an edition"""
|
like an isbn for an edition"""
|
||||||
filters = []
|
filters = []
|
||||||
|
@ -234,8 +234,8 @@ class ObjectMixin(ActivitypubMixin):
|
||||||
activity = self.to_create_activity(user)
|
activity = self.to_create_activity(user)
|
||||||
self.broadcast(activity, user, software=software, queue=priority)
|
self.broadcast(activity, user, software=software, queue=priority)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# janky as heck, this catches the mutliple inheritence chain
|
# janky as heck, this catches the multiple inheritance chain
|
||||||
# for boosts and ignores this auxilliary broadcast
|
# for boosts and ignores this auxiliary broadcast
|
||||||
return
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ class OrderedCollectionPageMixin(ObjectMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def collection_remote_id(self):
|
def collection_remote_id(self):
|
||||||
"""this can be overriden if there's a special remote id, ie outbox"""
|
"""this can be overridden if there's a special remote id, ie outbox"""
|
||||||
return self.remote_id
|
return self.remote_id
|
||||||
|
|
||||||
def to_ordered_collection(
|
def to_ordered_collection(
|
||||||
|
@ -339,7 +339,7 @@ class OrderedCollectionPageMixin(ObjectMixin):
|
||||||
activity["id"] = remote_id
|
activity["id"] = remote_id
|
||||||
|
|
||||||
paginated = Paginator(queryset, PAGE_LENGTH)
|
paginated = Paginator(queryset, PAGE_LENGTH)
|
||||||
# add computed fields specific to orderd collections
|
# add computed fields specific to ordered collections
|
||||||
activity["totalItems"] = paginated.count
|
activity["totalItems"] = paginated.count
|
||||||
activity["first"] = f"{remote_id}?page=1"
|
activity["first"] = f"{remote_id}?page=1"
|
||||||
activity["last"] = f"{remote_id}?page={paginated.num_pages}"
|
activity["last"] = f"{remote_id}?page={paginated.num_pages}"
|
||||||
|
@ -405,7 +405,7 @@ class CollectionItemMixin(ActivitypubMixin):
|
||||||
# first off, we want to save normally no matter what
|
# first off, we want to save normally no matter what
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
# list items can be updateda, normally you would only broadcast on created
|
# list items can be updated, normally you would only broadcast on created
|
||||||
if not broadcast or not self.user.local:
|
if not broadcast or not self.user.local:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -565,7 +565,7 @@ async def sign_and_send(
|
||||||
def to_ordered_collection_page(
|
def to_ordered_collection_page(
|
||||||
queryset, remote_id, id_only=False, page=1, pure=False, **kwargs
|
queryset, remote_id, id_only=False, page=1, pure=False, **kwargs
|
||||||
):
|
):
|
||||||
"""serialize and pagiante a queryset"""
|
"""serialize and paginate a queryset"""
|
||||||
paginated = Paginator(queryset, PAGE_LENGTH)
|
paginated = Paginator(queryset, PAGE_LENGTH)
|
||||||
|
|
||||||
activity_page = paginated.get_page(page)
|
activity_page = paginated.get_page(page)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class AnnualGoal(BookWyrmModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""unqiueness constraint"""
|
"""uniqueness constraint"""
|
||||||
|
|
||||||
unique_together = ("user", "year")
|
unique_together = ("user", "year")
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,11 @@ class ActivitypubFieldMixin:
|
||||||
def set_field_from_activity(
|
def set_field_from_activity(
|
||||||
self, instance, data, overwrite=True, allow_external_connections=True
|
self, instance, data, overwrite=True, allow_external_connections=True
|
||||||
):
|
):
|
||||||
"""helper function for assinging a value to the field. Returns if changed"""
|
"""helper function for assigning a value to the field. Returns if changed"""
|
||||||
try:
|
try:
|
||||||
value = getattr(data, self.get_activitypub_field())
|
value = getattr(data, self.get_activitypub_field())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# masssively hack-y workaround for boosts
|
# massively hack-y workaround for boosts
|
||||||
if self.get_activitypub_field() != "attributedTo":
|
if self.get_activitypub_field() != "attributedTo":
|
||||||
raise
|
raise
|
||||||
value = getattr(data, "actor")
|
value = getattr(data, "actor")
|
||||||
|
@ -221,7 +221,7 @@ PrivacyLevels = [
|
||||||
|
|
||||||
|
|
||||||
class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||||
"""this maps to two differente activitypub fields"""
|
"""this maps to two different activitypub fields"""
|
||||||
|
|
||||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
@ -431,7 +431,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
||||||
def set_field_from_activity(
|
def set_field_from_activity(
|
||||||
self, instance, data, save=True, overwrite=True, allow_external_connections=True
|
self, instance, data, save=True, overwrite=True, allow_external_connections=True
|
||||||
):
|
):
|
||||||
"""helper function for assinging a value to the field"""
|
"""helper function for assigning a value to the field"""
|
||||||
value = getattr(data, self.get_activitypub_field())
|
value = getattr(data, self.get_activitypub_field())
|
||||||
formatted = self.field_from_activity(
|
formatted = self.field_from_activity(
|
||||||
value, allow_external_connections=allow_external_connections
|
value, allow_external_connections=allow_external_connections
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Link(ActivitypubMixin, BookWyrmModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""link name via the assocaited domain"""
|
"""link name via the associated domain"""
|
||||||
return self.domain.name
|
return self.domain.name
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
|
@ -284,7 +284,7 @@ def notify_user_on_list_item_add(sender, instance, created, *args, **kwargs):
|
||||||
return
|
return
|
||||||
|
|
||||||
list_owner = instance.book_list.user
|
list_owner = instance.book_list.user
|
||||||
# create a notification if somoene ELSE added to a local user's list
|
# create a notification if someone ELSE added to a local user's list
|
||||||
if list_owner.local and list_owner != instance.user:
|
if list_owner.local and list_owner != instance.user:
|
||||||
# keep the related_user singular, group the items
|
# keep the related_user singular, group the items
|
||||||
Notification.notify_list_item(list_owner, instance)
|
Notification.notify_list_item(list_owner, instance)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .base_model import BookWyrmModel
|
||||||
|
|
||||||
|
|
||||||
class ProgressMode(models.TextChoices):
|
class ProgressMode(models.TextChoices):
|
||||||
"""types of prgress available"""
|
"""types of progress available"""
|
||||||
|
|
||||||
PAGE = "PG", "page"
|
PAGE = "PG", "page"
|
||||||
PERCENT = "PCT", "percent"
|
PERCENT = "PCT", "percent"
|
||||||
|
|
|
@ -34,7 +34,7 @@ class UserRelationship(BookWyrmModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def recipients(self):
|
def recipients(self):
|
||||||
"""the remote user needs to recieve direct broadcasts"""
|
"""the remote user needs to receive direct broadcasts"""
|
||||||
return [u for u in [self.user_subject, self.user_object] if not u.local]
|
return [u for u in [self.user_subject, self.user_object] if not u.local]
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
|
@ -80,7 +80,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""user/shelf unqiueness"""
|
"""user/shelf uniqueness"""
|
||||||
|
|
||||||
unique_together = ("user", "identifier")
|
unique_together = ("user", "identifier")
|
||||||
|
|
||||||
|
|
|
@ -209,7 +209,7 @@ class InviteRequest(BookWyrmModel):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_passowrd_reset_expiry():
|
def get_password_reset_expiry():
|
||||||
"""give people a limited time to use the link"""
|
"""give people a limited time to use the link"""
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
return now + datetime.timedelta(days=1)
|
return now + datetime.timedelta(days=1)
|
||||||
|
@ -219,7 +219,7 @@ class PasswordReset(models.Model):
|
||||||
"""gives someone access to create an account on the instance"""
|
"""gives someone access to create an account on the instance"""
|
||||||
|
|
||||||
code = models.CharField(max_length=32, default=new_access_code)
|
code = models.CharField(max_length=32, default=new_access_code)
|
||||||
expiry = models.DateTimeField(default=get_passowrd_reset_expiry)
|
expiry = models.DateTimeField(default=get_password_reset_expiry)
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
def valid(self):
|
def valid(self):
|
||||||
|
|
|
@ -34,7 +34,7 @@ class RedisStore(ABC):
|
||||||
|
|
||||||
def remove_object_from_related_stores(self, obj, stores=None):
|
def remove_object_from_related_stores(self, obj, stores=None):
|
||||||
"""remove an object from all stores"""
|
"""remove an object from all stores"""
|
||||||
# if the stoers are provided, the object can just be an id
|
# if the stores are provided, the object can just be an id
|
||||||
if stores and isinstance(obj, int):
|
if stores and isinstance(obj, int):
|
||||||
obj_id = obj
|
obj_id = obj
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* - .book-cover is positioned and sized based on its container.
|
* - .book-cover is positioned and sized based on its container.
|
||||||
*
|
*
|
||||||
* To have the cover within specific dimensions, specify a width or height for
|
* To have the cover within specific dimensions, specify a width or height for
|
||||||
* standard bulma’s named breapoints:
|
* standard bulma’s named breakpoints:
|
||||||
*
|
*
|
||||||
* `is-(w|h)-(auto|xs|s|m|l|xl|xxl)[-(mobile|tablet|desktop)]`
|
* `is-(w|h)-(auto|xs|s|m|l|xl|xxl)[-(mobile|tablet|desktop)]`
|
||||||
*
|
*
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
|
||||||
/* Useful when stretching under-sized images. */
|
/* Useful when stretching under-sized images. */
|
||||||
image-rendering: optimizequality;
|
image-rendering: optimizeQuality;
|
||||||
image-rendering: smooth;
|
image-rendering: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ let BookWyrm = new (class {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.MAX_FILE_SIZE_BYTES = 10 * 1000000;
|
this.MAX_FILE_SIZE_BYTES = 10 * 1000000;
|
||||||
this.initOnDOMLoaded();
|
this.initOnDOMLoaded();
|
||||||
this.initReccuringTasks();
|
this.initRecurringTasks();
|
||||||
this.initEventListeners();
|
this.initEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ let BookWyrm = new (class {
|
||||||
/**
|
/**
|
||||||
* Execute recurring tasks.
|
* Execute recurring tasks.
|
||||||
*/
|
*/
|
||||||
initReccuringTasks() {
|
initRecurringTasks() {
|
||||||
// Polling
|
// Polling
|
||||||
document.querySelectorAll("[data-poll]").forEach((liveArea) => this.polling(liveArea));
|
document.querySelectorAll("[data-poll]").forEach((liveArea) => this.polling(liveArea));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remoev input field
|
* Remove input field
|
||||||
*
|
*
|
||||||
* @param {event} the button click event
|
* @param {event} the button click event
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% url 'user-shelves' request.user.localname as path %}
|
{% url 'user-shelves' request.user.localname as path %}
|
||||||
<p class="notification is-light">
|
<p class="notification is-light">
|
||||||
{% blocktrans %}Looking for shelf privacy? You can set a sepearate visibility level for each of your shelves. Go to <a href="{{ path }}">Your Books</a>, pick a shelf from the tab bar, and click "Edit shelf."{% endblocktrans %}
|
{% blocktrans %}Looking for shelf privacy? You can set a separate visibility level for each of your shelves. Go to <a href="{{ path }}">Your Books</a>, pick a shelf from the tab bar, and click "Edit shelf."{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<p class="title is-5">{{ users|intcomma }}</p>
|
<p class="title is-5">{{ users|intcomma }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-3-desktop is-6-mobil is-flexe">
|
<div class="column is-3-desktop is-6-mobile is-flex">
|
||||||
<div class="notification is-flex-grow-1">
|
<div class="notification is-flex-grow-1">
|
||||||
<h3>{% trans "Active this month" %}</h3>
|
<h3>{% trans "Active this month" %}</h3>
|
||||||
<p class="title is-5">{{ active_users|intcomma }}</p>
|
<p class="title is-5">{{ active_users|intcomma }}</p>
|
||||||
|
|
|
@ -18,7 +18,7 @@ def get_book_description(book):
|
||||||
if book.description:
|
if book.description:
|
||||||
return book.description
|
return book.description
|
||||||
if book.parent_work:
|
if book.parent_work:
|
||||||
# this shoud always be true
|
# this should always be true
|
||||||
return book.parent_work.description
|
return book.parent_work.description
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ def get_next_shelf(current_shelf):
|
||||||
|
|
||||||
@register.filter(name="translate_shelf_name")
|
@register.filter(name="translate_shelf_name")
|
||||||
def get_translated_shelf_name(shelf):
|
def get_translated_shelf_name(shelf):
|
||||||
"""produced translated shelf nidentifierame"""
|
"""produce translated shelf identifiername"""
|
||||||
if not shelf:
|
if not shelf:
|
||||||
return ""
|
return ""
|
||||||
# support obj or dict
|
# support obj or dict
|
||||||
|
|
|
@ -19,7 +19,7 @@ def get_uuid(identifier):
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def join(*args):
|
def join(*args):
|
||||||
"""concatenate an arbitary set of values"""
|
"""concatenate an arbitrary set of values"""
|
||||||
return "_".join(str(a) for a in args)
|
return "_".join(str(a) for a in args)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Author(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_serialize_model(self):
|
def test_serialize_model(self):
|
||||||
"""check presense of author fields"""
|
"""check presence of author fields"""
|
||||||
activity = self.author.to_activity()
|
activity = self.author.to_activity()
|
||||||
self.assertEqual(activity["id"], self.author.remote_id)
|
self.assertEqual(activity["id"], self.author.remote_id)
|
||||||
self.assertIsInstance(activity["aliases"], list)
|
self.assertIsInstance(activity["aliases"], list)
|
||||||
|
|
|
@ -59,7 +59,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertIsInstance(representative, models.User)
|
self.assertIsInstance(representative, models.User)
|
||||||
|
|
||||||
def test_init(self, *_):
|
def test_init(self, *_):
|
||||||
"""simple successfuly init"""
|
"""simple successfully init"""
|
||||||
instance = ActivityObject(id="a", type="b")
|
instance = ActivityObject(id="a", type="b")
|
||||||
self.assertTrue(hasattr(instance, "id"))
|
self.assertTrue(hasattr(instance, "id"))
|
||||||
self.assertTrue(hasattr(instance, "type"))
|
self.assertTrue(hasattr(instance, "type"))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
""" quotation activty object serializer class """
|
""" quotation activity object serializer class """
|
||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
@ -30,7 +30,7 @@ class Quotation(TestCase):
|
||||||
self.status_data = json.loads(datafile.read_bytes())
|
self.status_data = json.loads(datafile.read_bytes())
|
||||||
|
|
||||||
def test_quotation_activity(self):
|
def test_quotation_activity(self):
|
||||||
"""create a Quoteation ap object from json"""
|
"""create a Quotation ap object from json"""
|
||||||
quotation = activitypub.Quotation(**self.status_data)
|
quotation = activitypub.Quotation(**self.status_data)
|
||||||
|
|
||||||
self.assertEqual(quotation.type, "Quotation")
|
self.assertEqual(quotation.type, "Quotation")
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Activitystreams(TestCase):
|
||||||
self.assertEqual(args[1], self.book)
|
self.assertEqual(args[1], self.book)
|
||||||
|
|
||||||
def test_remove_book_statuses_task(self):
|
def test_remove_book_statuses_task(self):
|
||||||
"""remove stauses related to a book"""
|
"""remove statuses related to a book"""
|
||||||
with patch("bookwyrm.activitystreams.BooksStream.remove_book_statuses") as mock:
|
with patch("bookwyrm.activitystreams.BooksStream.remove_book_statuses") as mock:
|
||||||
activitystreams.remove_book_statuses_task(self.local_user.id, self.book.id)
|
activitystreams.remove_book_statuses_task(self.local_user.id, self.book.id)
|
||||||
self.assertTrue(mock.called)
|
self.assertTrue(mock.called)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Openlibrary(TestCase):
|
||||||
data = {"key": "/work/OL1234W"}
|
data = {"key": "/work/OL1234W"}
|
||||||
result = self.connector.get_remote_id_from_data(data)
|
result = self.connector.get_remote_id_from_data(data)
|
||||||
self.assertEqual(result, "https://openlibrary.org/work/OL1234W")
|
self.assertEqual(result, "https://openlibrary.org/work/OL1234W")
|
||||||
# error handlding
|
# error handling
|
||||||
with self.assertRaises(ConnectorException):
|
with self.assertRaises(ConnectorException):
|
||||||
self.connector.get_remote_id_from_data({})
|
self.connector.get_remote_id_from_data({})
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ class ActivitypubMixins(TestCase):
|
||||||
|
|
||||||
# ObjectMixin
|
# ObjectMixin
|
||||||
def test_object_save_create(self, *_):
|
def test_object_save_create(self, *_):
|
||||||
"""should save uneventufully when broadcast is disabled"""
|
"""should save uneventfully when broadcast is disabled"""
|
||||||
|
|
||||||
class Success(Exception):
|
class Success(Exception):
|
||||||
"""this means we got to the right method"""
|
"""this means we got to the right method"""
|
||||||
|
@ -276,7 +276,7 @@ class ActivitypubMixins(TestCase):
|
||||||
ObjectModel(user=None).save()
|
ObjectModel(user=None).save()
|
||||||
|
|
||||||
def test_object_save_update(self, *_):
|
def test_object_save_update(self, *_):
|
||||||
"""should save uneventufully when broadcast is disabled"""
|
"""should save uneventfully when broadcast is disabled"""
|
||||||
|
|
||||||
class Success(Exception):
|
class Success(Exception):
|
||||||
"""this means we got to the right method"""
|
"""this means we got to the right method"""
|
||||||
|
|
|
@ -51,7 +51,7 @@ class BaseModel(TestCase):
|
||||||
|
|
||||||
def test_set_remote_id(self):
|
def test_set_remote_id(self):
|
||||||
"""this function sets remote ids after creation"""
|
"""this function sets remote ids after creation"""
|
||||||
# using Work because it BookWrymModel is abstract and this requires save
|
# using Work because it BookWyrmModel is abstract and this requires save
|
||||||
# Work is a relatively not-fancy model.
|
# Work is a relatively not-fancy model.
|
||||||
instance = models.Work.objects.create(title="work title")
|
instance = models.Work.objects.create(title="work title")
|
||||||
instance.remote_id = None
|
instance.remote_id = None
|
||||||
|
|
|
@ -29,7 +29,7 @@ from bookwyrm.settings import DOMAIN
|
||||||
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
||||||
@patch("bookwyrm.lists_stream.populate_lists_task.delay")
|
@patch("bookwyrm.lists_stream.populate_lists_task.delay")
|
||||||
class ModelFields(TestCase):
|
class ModelFields(TestCase):
|
||||||
"""overwrites standard model feilds to work with activitypub"""
|
"""overwrites standard model fields to work with activitypub"""
|
||||||
|
|
||||||
def test_validate_remote_id(self, *_):
|
def test_validate_remote_id(self, *_):
|
||||||
"""should look like a url"""
|
"""should look like a url"""
|
||||||
|
@ -125,7 +125,7 @@ class ModelFields(TestCase):
|
||||||
instance.run_validators("@example.com")
|
instance.run_validators("@example.com")
|
||||||
instance.run_validators("mouse@examplecom")
|
instance.run_validators("mouse@examplecom")
|
||||||
instance.run_validators("one two@fish.aaaa")
|
instance.run_validators("one two@fish.aaaa")
|
||||||
instance.run_validators("a*&@exampke.com")
|
instance.run_validators("a*&@example.com")
|
||||||
instance.run_validators("trailingwhite@example.com ")
|
instance.run_validators("trailingwhite@example.com ")
|
||||||
self.assertIsNone(instance.run_validators("mouse@example.com"))
|
self.assertIsNone(instance.run_validators("mouse@example.com"))
|
||||||
self.assertIsNone(instance.run_validators("mo-2use@ex3ample.com"))
|
self.assertIsNone(instance.run_validators("mo-2use@ex3ample.com"))
|
||||||
|
@ -292,7 +292,7 @@ class ModelFields(TestCase):
|
||||||
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
||||||
|
|
||||||
def test_foreign_key_from_activity_dict(self, *_):
|
def test_foreign_key_from_activity_dict(self, *_):
|
||||||
"""test recieving activity json"""
|
"""test receiving activity json"""
|
||||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||||
userdata = json.loads(datafile.read_bytes())
|
userdata = json.loads(datafile.read_bytes())
|
||||||
|
|
|
@ -397,7 +397,7 @@ class Status(TestCase):
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def test_create_broadcast(self, one, two, broadcast_mock, *_):
|
def test_create_broadcast(self, one, two, broadcast_mock, *_):
|
||||||
"""should send out two verions of a status on create"""
|
"""should send out two versions of a status on create"""
|
||||||
models.Comment.objects.create(
|
models.Comment.objects.create(
|
||||||
content="hi", user=self.local_user, book=self.book
|
content="hi", user=self.local_user, book=self.book
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,7 +7,7 @@ class MarkdownTags(TestCase):
|
||||||
"""lotta different things here"""
|
"""lotta different things here"""
|
||||||
|
|
||||||
def test_get_markdown(self):
|
def test_get_markdown(self):
|
||||||
"""mardown format data"""
|
"""markdown format data"""
|
||||||
result = markdown.get_markdown("_hi_")
|
result = markdown.get_markdown("_hi_")
|
||||||
self.assertEqual(result, "<p><em>hi</em></p>")
|
self.assertEqual(result, "<p><em>hi</em></p>")
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class RatingTags(TestCase):
|
||||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
|
||||||
def test_get_rating(self, *_):
|
def test_get_rating(self, *_):
|
||||||
"""privacy filtered rating. Commented versions are how it ought to work with
|
"""privacy filtered rating. Commented versions are how it ought to work with
|
||||||
subjective ratings, which are currenly not used for performance reasons."""
|
subjective ratings, which are currently not used for performance reasons."""
|
||||||
# follows-only: not included
|
# follows-only: not included
|
||||||
models.ReviewRating.objects.create(
|
models.ReviewRating.objects.create(
|
||||||
user=self.remote_user,
|
user=self.remote_user,
|
||||||
|
|
|
@ -30,7 +30,7 @@ class PostgresTriggers(TestCase):
|
||||||
title="The Long Goodbye",
|
title="The Long Goodbye",
|
||||||
subtitle="wow cool",
|
subtitle="wow cool",
|
||||||
series="series name",
|
series="series name",
|
||||||
languages=["irrelevent"],
|
languages=["irrelevant"],
|
||||||
)
|
)
|
||||||
book.authors.add(author)
|
book.authors.add(author)
|
||||||
book.refresh_from_db()
|
book.refresh_from_db()
|
||||||
|
@ -40,7 +40,7 @@ class PostgresTriggers(TestCase):
|
||||||
"'cool':5B 'goodby':3A 'long':2A 'name':9 'rays':7C 'seri':8 'the':6C 'wow':4B",
|
"'cool':5B 'goodby':3A 'long':2A 'name':9 'rays':7C 'seri':8 'the':6C 'wow':4B",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_seach_vector_on_author_update(self, _):
|
def test_search_vector_on_author_update(self, _):
|
||||||
"""update search when an author name changes"""
|
"""update search when an author name changes"""
|
||||||
author = models.Author.objects.create(name="The Rays")
|
author = models.Author.objects.create(name="The Rays")
|
||||||
book = models.Edition.objects.create(
|
book = models.Edition.objects.create(
|
||||||
|
@ -53,7 +53,7 @@ class PostgresTriggers(TestCase):
|
||||||
|
|
||||||
self.assertEqual(book.search_vector, "'goodby':3A 'jeremy':4C 'long':2A")
|
self.assertEqual(book.search_vector, "'goodby':3A 'jeremy':4C 'long':2A")
|
||||||
|
|
||||||
def test_seach_vector_on_author_delete(self, _):
|
def test_search_vector_on_author_delete(self, _):
|
||||||
"""update search when an author name changes"""
|
"""update search when an author name changes"""
|
||||||
author = models.Author.objects.create(name="Jeremy")
|
author = models.Author.objects.create(name="Jeremy")
|
||||||
book = models.Edition.objects.create(
|
book = models.Edition.objects.create(
|
||||||
|
|
|
@ -107,7 +107,7 @@ class Signature(TestCase):
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_remote_signer(self):
|
def test_remote_signer(self):
|
||||||
"""signtures for remote users"""
|
"""signatures for remote users"""
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("data/ap_user.json")
|
datafile = pathlib.Path(__file__).parent.joinpath("data/ap_user.json")
|
||||||
data = json.loads(datafile.read_bytes())
|
data = json.loads(datafile.read_bytes())
|
||||||
data["id"] = self.fake_remote.remote_id
|
data["id"] = self.fake_remote.remote_id
|
||||||
|
|
|
@ -58,7 +58,7 @@ class InboxActivities(TestCase):
|
||||||
with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock:
|
with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock:
|
||||||
views.inbox.activity_task(activity)
|
views.inbox.activity_task(activity)
|
||||||
self.assertTrue(redis_mock.called)
|
self.assertTrue(redis_mock.called)
|
||||||
# deletion doens't remove the status, it turns it into a tombstone
|
# deletion doesn't remove the status, it turns it into a tombstone
|
||||||
status = models.Status.objects.get()
|
status = models.Status.objects.get()
|
||||||
self.assertTrue(status.deleted)
|
self.assertTrue(status.deleted)
|
||||||
self.assertIsInstance(status.deleted_date, datetime)
|
self.assertIsInstance(status.deleted_date, datetime)
|
||||||
|
@ -87,7 +87,7 @@ class InboxActivities(TestCase):
|
||||||
with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock:
|
with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock:
|
||||||
views.inbox.activity_task(activity)
|
views.inbox.activity_task(activity)
|
||||||
self.assertTrue(redis_mock.called)
|
self.assertTrue(redis_mock.called)
|
||||||
# deletion doens't remove the status, it turns it into a tombstone
|
# deletion doesn't remove the status, it turns it into a tombstone
|
||||||
status = models.Status.objects.get()
|
status = models.Status.objects.get()
|
||||||
self.assertTrue(status.deleted)
|
self.assertTrue(status.deleted)
|
||||||
self.assertIsInstance(status.deleted_date, datetime)
|
self.assertIsInstance(status.deleted_date, datetime)
|
||||||
|
|
|
@ -114,7 +114,7 @@ class LoginViews(TestCase):
|
||||||
view = views.Login.as_view()
|
view = views.Login.as_view()
|
||||||
form = forms.LoginForm()
|
form = forms.LoginForm()
|
||||||
form.data["localname"] = "mouse"
|
form.data["localname"] = "mouse"
|
||||||
form.data["password"] = "passsword1"
|
form.data["password"] = "password1"
|
||||||
request = self.factory.post("", form.data)
|
request = self.factory.post("", form.data)
|
||||||
request.user = self.anonymous_user
|
request.user = self.anonymous_user
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ class PasswordViews(TestCase):
|
||||||
validate_html(result.render())
|
validate_html(result.render())
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
def test_password_reset_nonexistant_code(self):
|
def test_password_reset_nonexistent_code(self):
|
||||||
"""there are so many views, this just makes sure it LOADS"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.PasswordReset.as_view()
|
view = views.PasswordReset.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
|
|
@ -234,7 +234,7 @@ class StatusViews(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_status_reply_with_mentions(self, *_):
|
def test_create_status_reply_with_mentions(self, *_):
|
||||||
"""reply to a post with an @mention'ed user"""
|
"""reply to a post with an @mention'd user"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
user = models.User.objects.create_user(
|
user = models.User.objects.create_user(
|
||||||
"rat", "rat@rat.com", "password", local=True, localname="rat"
|
"rat", "rat@rat.com", "password", local=True, localname="rat"
|
||||||
|
@ -356,12 +356,12 @@ class StatusViews(TestCase):
|
||||||
self.assertEqual(len(hashtags), 2)
|
self.assertEqual(len(hashtags), 2)
|
||||||
self.assertEqual(list(status.mention_hashtags.all()), list(hashtags))
|
self.assertEqual(list(status.mention_hashtags.all()), list(hashtags))
|
||||||
|
|
||||||
hashtag_exising = models.Hashtag.objects.filter(name="#existing").first()
|
hashtag_existing = models.Hashtag.objects.filter(name="#existing").first()
|
||||||
hashtag_new = models.Hashtag.objects.filter(name="#NewTag").first()
|
hashtag_new = models.Hashtag.objects.filter(name="#NewTag").first()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
status.content,
|
status.content,
|
||||||
"<p>this is an "
|
"<p>this is an "
|
||||||
+ f'<a href="{hashtag_exising.remote_id}" data-mention="hashtag">'
|
+ f'<a href="{hashtag_existing.remote_id}" data-mention="hashtag">'
|
||||||
+ "#EXISTING</a> hashtag but all uppercase, this one is "
|
+ "#EXISTING</a> hashtag but all uppercase, this one is "
|
||||||
+ f'<a href="{hashtag_new.remote_id}" data-mention="hashtag">'
|
+ f'<a href="{hashtag_new.remote_id}" data-mention="hashtag">'
|
||||||
+ "#NewTag</a>.</p>",
|
+ "#NewTag</a>.</p>",
|
||||||
|
|
|
@ -53,7 +53,7 @@ class WellknownViews(TestCase):
|
||||||
data = json.loads(result.getvalue())
|
data = json.loads(result.getvalue())
|
||||||
self.assertEqual(data["subject"], "acct:mouse@local.com")
|
self.assertEqual(data["subject"], "acct:mouse@local.com")
|
||||||
|
|
||||||
def test_webfinger_case_sensitivty(self):
|
def test_webfinger_case_sensitivity(self):
|
||||||
"""ensure that webfinger queries are not case sensitive"""
|
"""ensure that webfinger queries are not case sensitive"""
|
||||||
request = self.factory.get("", {"resource": "acct:MoUsE@local.com"})
|
request = self.factory.get("", {"resource": "acct:MoUsE@local.com"})
|
||||||
request.user = self.anonymous_user
|
request.user = self.anonymous_user
|
||||||
|
|
|
@ -76,7 +76,7 @@ class Dashboard(View):
|
||||||
|
|
||||||
|
|
||||||
def get_charts_and_stats(request):
|
def get_charts_and_stats(request):
|
||||||
"""Defines the dashbaord charts"""
|
"""Defines the dashboard charts"""
|
||||||
interval = int(request.GET.get("days", 1))
|
interval = int(request.GET.get("days", 1))
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
start = request.GET.get("start")
|
start = request.GET.get("start")
|
||||||
|
|
|
@ -154,7 +154,7 @@ def add_authors(request, data):
|
||||||
data["author_matches"] = []
|
data["author_matches"] = []
|
||||||
data["isni_matches"] = []
|
data["isni_matches"] = []
|
||||||
|
|
||||||
# creting a book or adding an author to a book needs another step
|
# creating a book or adding an author to a book needs another step
|
||||||
data["confirm_mode"] = True
|
data["confirm_mode"] = True
|
||||||
# this isn't preserved because it isn't part of the form obj
|
# this isn't preserved because it isn't part of the form obj
|
||||||
data["remove_authors"] = request.POST.getlist("remove_authors")
|
data["remove_authors"] = request.POST.getlist("remove_authors")
|
||||||
|
|
|
@ -104,7 +104,7 @@ def raise_is_blocked_activity(activity_json):
|
||||||
|
|
||||||
def sometimes_async_activity_task(activity_json, queue=MEDIUM):
|
def sometimes_async_activity_task(activity_json, queue=MEDIUM):
|
||||||
"""Sometimes we can effectively respond to a request without queuing a new task,
|
"""Sometimes we can effectively respond to a request without queuing a new task,
|
||||||
and whever that is possible, we should do it."""
|
and whenever that is possible, we should do it."""
|
||||||
activity = activitypub.parse(activity_json)
|
activity = activitypub.parse(activity_json)
|
||||||
|
|
||||||
# try resolving this activity without making any http requests
|
# try resolving this activity without making any http requests
|
||||||
|
|
|
@ -14,7 +14,7 @@ from bookwyrm.views.list.list import normalize_book_list_ordering
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
class Curate(View):
|
class Curate(View):
|
||||||
"""approve or discard list suggestsions"""
|
"""approve or discard list suggestions"""
|
||||||
|
|
||||||
def get(self, request, list_id):
|
def get(self, request, list_id):
|
||||||
"""display a pending list"""
|
"""display a pending list"""
|
||||||
|
|
|
@ -14,7 +14,7 @@ from bookwyrm.settings import PAGE_LENGTH
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
class EmbedList(View):
|
class EmbedList(View):
|
||||||
"""embeded book list page"""
|
"""embedded book list page"""
|
||||||
|
|
||||||
def get(self, request, list_id, list_key):
|
def get(self, request, list_id, list_key):
|
||||||
"""display a book list"""
|
"""display a book list"""
|
||||||
|
|
|
@ -186,7 +186,7 @@ def update_readthrough_on_shelve(
|
||||||
active_readthrough = models.ReadThrough.objects.create(
|
active_readthrough = models.ReadThrough.objects.create(
|
||||||
user=user, book=annotated_book
|
user=user, book=annotated_book
|
||||||
)
|
)
|
||||||
# santiize and set dates
|
# sanitize and set dates
|
||||||
active_readthrough.start_date = load_date_in_user_tz_as_utc(start_date, user)
|
active_readthrough.start_date = load_date_in_user_tz_as_utc(start_date, user)
|
||||||
# if the stop or finish date is set, the readthrough will be set as inactive
|
# if the stop or finish date is set, the readthrough will be set as inactive
|
||||||
active_readthrough.finish_date = load_date_in_user_tz_as_utc(finish_date, user)
|
active_readthrough.finish_date = load_date_in_user_tz_as_utc(finish_date, user)
|
||||||
|
|
|
@ -232,7 +232,7 @@ def find_mentions(user, content):
|
||||||
if not content:
|
if not content:
|
||||||
return {}
|
return {}
|
||||||
# The regex has nested match groups, so the 0th entry has the full (outer) match
|
# The regex has nested match groups, so the 0th entry has the full (outer) match
|
||||||
# And beacuse the strict username starts with @, the username is 1st char onward
|
# And because the strict username starts with @, the username is 1st char onward
|
||||||
usernames = [m[0][1:] for m in re.findall(regex.STRICT_USERNAME, content)]
|
usernames = [m[0][1:] for m in re.findall(regex.STRICT_USERNAME, content)]
|
||||||
|
|
||||||
known_users = (
|
known_users = (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Stub English-language trnaslation file
|
# Stub English-language translation file
|
||||||
# Copyright (C) 2021 Mouse Reeve
|
# Copyright (C) 2021 Mouse Reeve
|
||||||
# This file is distributed under the same license as the BookWyrm package.
|
# This file is distributed under the same license as the BookWyrm package.
|
||||||
# Mouse Reeve <mousereeve@riseup.net>, 2021
|
# Mouse Reeve <mousereeve@riseup.net>, 2021
|
||||||
|
@ -4045,7 +4045,7 @@ msgstr ""
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/edit_user.html:136
|
#: bookwyrm/templates/preferences/edit_user.html:136
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Looking for shelf privacy? You can set a sepearate visibility level for each of your shelves. Go to <a href=\"%(path)s\">Your Books</a>, pick a shelf from the tab bar, and click \"Edit shelf.\""
|
msgid "Looking for shelf privacy? You can set a separate visibility level for each of your shelves. Go to <a href=\"%(path)s\">Your Books</a>, pick a shelf from the tab bar, and click \"Edit shelf.\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/export.html:4
|
#: bookwyrm/templates/preferences/export.html:4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue