mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-04 23:36:32 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
e5b850299c
12 changed files with 101 additions and 58 deletions
|
@ -1,5 +1,5 @@
|
|||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY=7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr
|
||||
SECRET_KEY="7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG=false
|
||||
|
@ -27,7 +27,7 @@ REDIS_PASSWORD=redispassword123
|
|||
CELERY_BROKER=redis://:${REDIS_PASSWORD}@redis:6379/0
|
||||
CELERY_RESULT_BACKEND=redis://:${REDIS_PASSWORD}@redis:6379/0
|
||||
|
||||
EMAIL_HOST='smtp.mailgun.org'
|
||||
EMAIL_HOST="smtp.mailgun.org"
|
||||
EMAIL_PORT=587
|
||||
EMAIL_HOST_USER=mail@your.domain.here
|
||||
EMAIL_HOST_PASSWORD=emailpassword123
|
||||
|
|
|
@ -4,7 +4,7 @@ import sys
|
|||
|
||||
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
|
||||
from .base_activity import Link, Mention
|
||||
from .base_activity import ActivitySerializerError
|
||||
from .base_activity import ActivitySerializerError, tag_formatter
|
||||
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
|
||||
from .note import Tombstone
|
||||
from .interaction import Boost, Like
|
||||
|
|
|
@ -5,7 +5,7 @@ from json import JSONEncoder
|
|||
from bookwyrm import books_manager, models
|
||||
|
||||
from django.db.models.fields.related_descriptors \
|
||||
import ForwardManyToOneDescriptor
|
||||
import ForwardManyToOneDescriptor, ManyToManyDescriptor
|
||||
|
||||
|
||||
class ActivitySerializerError(ValueError):
|
||||
|
@ -90,6 +90,7 @@ class ActivityObject:
|
|||
|
||||
model_fields = [m.name for m in model._meta.get_fields()]
|
||||
mapped_fields = {}
|
||||
many_to_many_fields = {}
|
||||
|
||||
for mapping in model.activity_mappings:
|
||||
if mapping.model_key not in model_fields:
|
||||
|
@ -106,7 +107,11 @@ class ActivityObject:
|
|||
fk_model = model_field.field.related_model
|
||||
value = resolve_foreign_key(fk_model, value)
|
||||
|
||||
mapped_fields[mapping.model_key] = mapping.model_formatter(value)
|
||||
formatted_value = mapping.model_formatter(value)
|
||||
if isinstance(model_field, ManyToManyDescriptor):
|
||||
many_to_many_fields[mapping.model_key] = formatted_value
|
||||
else:
|
||||
mapped_fields[mapping.model_key] = formatted_value
|
||||
|
||||
|
||||
# updating an existing model isntance
|
||||
|
@ -114,10 +119,14 @@ class ActivityObject:
|
|||
for k, v in mapped_fields.items():
|
||||
setattr(instance, k, v)
|
||||
instance.save()
|
||||
return instance
|
||||
else:
|
||||
# creating a new model instance
|
||||
instance = model.objects.create(**mapped_fields)
|
||||
|
||||
# creating a new model instance
|
||||
return model.objects.create(**mapped_fields)
|
||||
for (model_key, values) in many_to_many_fields.items():
|
||||
getattr(instance, model_key).set(values)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
def serialize(self):
|
||||
|
@ -129,7 +138,7 @@ class ActivityObject:
|
|||
|
||||
def resolve_foreign_key(model, remote_id):
|
||||
''' look up the remote_id on an activity json field '''
|
||||
if model in [models.Edition, models.Work]:
|
||||
if model in [models.Edition, models.Work, models.Book]:
|
||||
return books_manager.get_or_create_book(remote_id)
|
||||
|
||||
result = model.objects
|
||||
|
@ -145,3 +154,23 @@ def resolve_foreign_key(model, remote_id):
|
|||
'Could not resolve remote_id in %s model: %s' % \
|
||||
(model.__name__, remote_id))
|
||||
return result
|
||||
|
||||
|
||||
def tag_formatter(tags):
|
||||
''' helper function to extract foreign keys from tag activity json '''
|
||||
items = []
|
||||
types = {
|
||||
'Book': models.Book,
|
||||
'Mention': models.User,
|
||||
}
|
||||
for tag in tags:
|
||||
tag_type = tag.get('type')
|
||||
if not tag_type in types:
|
||||
continue
|
||||
remote_id = tag.get('href')
|
||||
try:
|
||||
item = resolve_foreign_key(types[tag_type], remote_id)
|
||||
except ActivitySerializerError:
|
||||
continue
|
||||
items.append(item)
|
||||
return items
|
||||
|
|
|
@ -253,7 +253,6 @@ def handle_delete_status(activity):
|
|||
status_builder.delete_status(status)
|
||||
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_favorite(activity):
|
||||
''' approval of your good good post '''
|
||||
|
|
|
@ -72,7 +72,14 @@ class ActivitypubMixin:
|
|||
value = value.remote_id
|
||||
if isinstance(value, datetime):
|
||||
value = value.isoformat()
|
||||
fields[mapping.activity_key] = mapping.activity_formatter(value)
|
||||
result = mapping.activity_formatter(value)
|
||||
if mapping.activity_key in fields and \
|
||||
isinstance(fields[mapping.activity_key], list):
|
||||
# there are two database fields that map to the same AP list
|
||||
# this happens in status, which combines user and book tags
|
||||
fields[mapping.activity_key] += result
|
||||
else:
|
||||
fields[mapping.activity_key] = result
|
||||
|
||||
if pure:
|
||||
return self.pure_activity_serializer(
|
||||
|
@ -242,3 +249,15 @@ class ActivityMapping:
|
|||
model_key: str
|
||||
activity_formatter: Callable = lambda x: x
|
||||
model_formatter: Callable = lambda x: x
|
||||
|
||||
|
||||
def tag_formatter(items, name_field, activity_type):
|
||||
''' helper function to format lists of foreign keys into Tags '''
|
||||
tags = []
|
||||
for item in items.all():
|
||||
tags.append(activitypub.Link(
|
||||
href=item.remote_id,
|
||||
name=getattr(item, name_field),
|
||||
type=activity_type
|
||||
))
|
||||
return tags
|
||||
|
|
|
@ -7,6 +7,7 @@ from model_utils.managers import InheritanceManager
|
|||
from bookwyrm import activitypub
|
||||
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
|
||||
from .base_model import ActivityMapping, BookWyrmModel, PrivacyLevels
|
||||
from .base_model import tag_formatter
|
||||
|
||||
|
||||
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||
|
@ -57,24 +58,6 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
''' structured replies block '''
|
||||
return self.to_replies()
|
||||
|
||||
@property
|
||||
def ap_tag(self):
|
||||
''' references to books and/or users '''
|
||||
|
||||
tags = []
|
||||
for book in self.mention_books.all():
|
||||
tags.append(activitypub.Link(
|
||||
href=book.remote_id,
|
||||
name=book.title,
|
||||
type='Book'
|
||||
))
|
||||
for user in self.mention_users.all():
|
||||
tags.append(activitypub.Mention(
|
||||
href=user.remote_id,
|
||||
name=user.username,
|
||||
))
|
||||
return tags
|
||||
|
||||
@property
|
||||
def ap_status_image(self):
|
||||
''' attach a book cover, if relevent '''
|
||||
|
@ -94,7 +77,16 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
ActivityMapping('to', 'ap_to'),
|
||||
ActivityMapping('cc', 'ap_cc'),
|
||||
ActivityMapping('replies', 'ap_replies'),
|
||||
ActivityMapping('tag', 'ap_tag'),
|
||||
ActivityMapping(
|
||||
'tag', 'mention_books',
|
||||
lambda x: tag_formatter(x, 'title', 'Book'),
|
||||
activitypub.tag_formatter
|
||||
),
|
||||
ActivityMapping(
|
||||
'tag', 'mention_users',
|
||||
lambda x: tag_formatter(x, 'username', 'Mention'),
|
||||
activitypub.tag_formatter
|
||||
),
|
||||
]
|
||||
|
||||
# serializing to bookwyrm expanded activitypub
|
||||
|
|
|
@ -40,6 +40,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'django_rename_app',
|
||||
'bookwyrm',
|
||||
'celery',
|
||||
]
|
||||
|
|
|
@ -25,14 +25,18 @@
|
|||
<a class="navbar-item" href="/">
|
||||
<img class="image logo" src="/static/images/logo-small.png" alt="Home page">
|
||||
</a>
|
||||
<form class="navbar-item" action="/search/">
|
||||
<div class="field is-grouped">
|
||||
<input aria-label="Search for a book or user" id="search-input" class="input" type="text" name="q" placeholder="Search for a book or user" value="{{ query }}">
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-search">
|
||||
<span class="is-sr-only">search</span>
|
||||
</span>
|
||||
</button>
|
||||
<form class="navbar-item column" action="/search/">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input aria-label="Search for a book or user" id="search-input" class="input" type="text" name="q" placeholder="Search for a book or user" value="{{ query }}">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-search">
|
||||
<span class="is-sr-only">search</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
{% if request.user.is_authenticated %}
|
||||
|
||||
{% with book.id|uuid as uuid %}
|
||||
{% active_shelf book as active_shelf %}
|
||||
<div class="field is-grouped">
|
||||
{% if active_shelf.identifier == 'read' %}
|
||||
<button class="button is-small" disabled>
|
||||
|
|
|
@ -168,25 +168,6 @@ def active_shelf(context, book):
|
|||
return shelf.shelf if shelf else None
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def shelve_button_text(context, book):
|
||||
''' check what shelf a user has a book on, if any '''
|
||||
#TODO: books can be on multiple shelves
|
||||
shelf = models.ShelfBook.objects.filter(
|
||||
shelf__user=context['request'].user,
|
||||
book=book
|
||||
).first()
|
||||
if not shelf:
|
||||
return 'Want to read'
|
||||
|
||||
identifier = shelf.shelf.identifier
|
||||
if identifier == 'to-read':
|
||||
return 'Start reading'
|
||||
if identifier == 'reading':
|
||||
return 'I\'m done!'
|
||||
return 'Read'
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=False)
|
||||
def latest_read_through(book, user):
|
||||
''' the most recent read activity '''
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
# exit on errors
|
||||
set -e
|
||||
|
||||
# import our ENV variables
|
||||
# catch exits and give a friendly error message
|
||||
function showerr {
|
||||
echo "Failed to load configuration! You may need to update your .env and quote values with special characters in them."
|
||||
}
|
||||
trap showerr EXIT
|
||||
source .env
|
||||
trap - EXIT
|
||||
|
||||
# show commands as they're executed
|
||||
set -x
|
||||
|
||||
function execdb {
|
||||
|
@ -20,6 +32,9 @@ case "$1" in
|
|||
up)
|
||||
docker-compose up --build
|
||||
;;
|
||||
run)
|
||||
docker-compose run --rm --service-ports web
|
||||
;;
|
||||
initdb)
|
||||
initdb
|
||||
;;
|
||||
|
@ -27,6 +42,7 @@ case "$1" in
|
|||
execweb python manage.py makemigrations
|
||||
;;
|
||||
migrate)
|
||||
execweb python manage.py rename_app fedireads bookwyrm
|
||||
execweb python manage.py migrate
|
||||
;;
|
||||
bash)
|
||||
|
@ -36,7 +52,7 @@ case "$1" in
|
|||
execweb python manage.py shell
|
||||
;;
|
||||
dbshell)
|
||||
execdb psql -U fedireads fedireads
|
||||
execdb psql -U ${POSTGRES_USER} ${POSTGRES_DB}
|
||||
;;
|
||||
restart_celery)
|
||||
docker-compose restart celery_worker
|
|
@ -14,3 +14,4 @@ python-dateutil==2.8.1
|
|||
redis==3.4.1
|
||||
requests==2.22.0
|
||||
responses==0.10.14
|
||||
django-rename-app==0.1.2
|
||||
|
|
Loading…
Reference in a new issue