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
0574e36602
19 changed files with 194 additions and 223 deletions
|
@ -1,10 +1,15 @@
|
||||||
FROM python:3
|
FROM python:3.9
|
||||||
ENV PYTHONUNBUFFERED 1
|
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
RUN mkdir /app
|
RUN mkdir /app
|
||||||
RUN mkdir /app/static
|
RUN mkdir /app/static
|
||||||
RUN mkdir /app/images
|
RUN mkdir /app/images
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY requirements.txt /app/
|
COPY requirements.txt /app/
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
COPY ./bookwyrm /app
|
COPY ./bookwyrm /app
|
||||||
COPY ./celerywyrm /app
|
COPY ./celerywyrm /app
|
||||||
|
|
|
@ -76,11 +76,12 @@ class ActivityObject:
|
||||||
if not isinstance(self, model.activity_serializer):
|
if not isinstance(self, model.activity_serializer):
|
||||||
raise TypeError('Wrong activity type for model')
|
raise TypeError('Wrong activity type for model')
|
||||||
|
|
||||||
# check for an existing instance
|
# check for an existing instance, if we're not updating a known obj
|
||||||
try:
|
if not instance:
|
||||||
return model.objects.get(remote_id=self.id)
|
try:
|
||||||
except model.DoesNotExist:
|
return model.objects.get(remote_id=self.id)
|
||||||
pass
|
except model.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
model_fields = [m.name for m in model._meta.get_fields()]
|
model_fields = [m.name for m in model._meta.get_fields()]
|
||||||
mapped_fields = {}
|
mapped_fields = {}
|
||||||
|
|
|
@ -269,7 +269,12 @@ def handle_favorite(activity):
|
||||||
@app.task
|
@app.task
|
||||||
def handle_unfavorite(activity):
|
def handle_unfavorite(activity):
|
||||||
''' approval of your good good post '''
|
''' approval of your good good post '''
|
||||||
like = activitypub.Like(**activity['object']).to_model(models.Favorite)
|
try:
|
||||||
|
like = models.Favorite.objects.filter(
|
||||||
|
remote_id=activity['object']['id']
|
||||||
|
).first()
|
||||||
|
except models.Favorite.DoesNotExist:
|
||||||
|
return
|
||||||
like.delete()
|
like.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,7 +299,7 @@ def handle_unboost(activity):
|
||||||
remote_id=activity['object']['id']
|
remote_id=activity['object']['id']
|
||||||
).first()
|
).first()
|
||||||
if boost:
|
if boost:
|
||||||
status_builder.delete_status(boost)
|
boost.delete()
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
|
|
|
@ -299,7 +299,8 @@ def handle_unfavorite(user, status):
|
||||||
# can't find that status, idk
|
# can't find that status, idk
|
||||||
return
|
return
|
||||||
|
|
||||||
fav_activity = activitypub.Undo(actor=user, object=favorite)
|
fav_activity = favorite.to_undo_activity(user)
|
||||||
|
favorite.delete()
|
||||||
broadcast(user, fav_activity, direct_recipients=[status.user])
|
broadcast(user, fav_activity, direct_recipients=[status.user])
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,6 +320,17 @@ def handle_boost(user, status):
|
||||||
broadcast(user, boost_activity)
|
broadcast(user, boost_activity)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_unboost(user, status):
|
||||||
|
''' a user regrets boosting a status '''
|
||||||
|
boost = models.Boost.objects.filter(
|
||||||
|
boosted_status=status, user=user
|
||||||
|
).first()
|
||||||
|
activity = boost.to_undo_activity(user)
|
||||||
|
|
||||||
|
boost.delete()
|
||||||
|
broadcast(user, activity)
|
||||||
|
|
||||||
|
|
||||||
def handle_update_book(user, book):
|
def handle_update_book(user, book):
|
||||||
''' broadcast the news about our book '''
|
''' broadcast the news about our book '''
|
||||||
broadcast(user, book.to_update_activity(user))
|
broadcast(user, book.to_update_activity(user))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% with activity.id|uuid as uuid %}
|
{% with status.id|uuid as uuid %}
|
||||||
<form name="boost" action="/boost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
<form name="boost" action="/boost/{{ status.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }}-{{ uuid }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button is-small" type="submit">
|
<button class="button is-small" type="submit">
|
||||||
<span class="icon icon-boost">
|
<span class="icon icon-boost">
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form name="unboost" action="/unboost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
<form name="unboost" action="/unboost/{{ status.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }}-{{ uuid }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button is-small is-success" type="submit">
|
<button class="button is-small is-success" type="submit">
|
||||||
<span class="icon icon-boost">
|
<span class="icon icon-boost">
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<textarea name="{% if type == 'quote' %}quote{% else %}content{% endif %}" class="textarea" id="id_content_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required></textarea>
|
<textarea name="{% if type == 'quote' %}quote{% else %}content{% endif %}" class="textarea" id="id_quote_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required></textarea>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% if type == 'quote' %}
|
{% if type == 'quote' %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% with activity.id|uuid as uuid %}
|
{% with status.id|uuid as uuid %}
|
||||||
<form name="favorite" action="/favorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
<form name="favorite" action="/favorite/{{ status.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }}-{{ uuid }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button is-small" type="submit">
|
<button class="button is-small" type="submit">
|
||||||
<span class="icon icon-heart">
|
<span class="icon icon-heart">
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form name="unfavorite" action="/unfavorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
<form name="unfavorite" action="/unfavorite/{{ status.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }}-{{ uuid }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button is-success is-small" type="submit">
|
<button class="button is-success is-small" type="submit">
|
||||||
<span class="icon icon-heart">
|
<span class="icon icon-heart">
|
||||||
|
|
|
@ -1,142 +1,11 @@
|
||||||
{% load humanize %}
|
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
|
||||||
{% if not status.deleted %}
|
{% if not status.deleted %}
|
||||||
<div class="card">
|
{% if status.status_type == 'Boost' %}
|
||||||
<header class="card-header">
|
|
||||||
<div class="card-header-title">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-narrow">
|
|
||||||
{% if status.status_type == 'Boost' %}
|
|
||||||
{% include 'snippets/avatar.html' with user=status.user %}
|
|
||||||
{% include 'snippets/username.html' with user=status.user %}
|
|
||||||
boosted
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
{% include 'snippets/status_header.html' with status=status|boosted_status %}
|
|
||||||
{% else %}
|
|
||||||
{% include 'snippets/status_header.html' with status=status %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="card-content">
|
|
||||||
{% if status.status_type == 'Boost' %}
|
|
||||||
{% include 'snippets/status_content.html' with status=status|boosted_status %}
|
|
||||||
{% else %}
|
|
||||||
{% include 'snippets/status_content.html' with status=status %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<input class="toggle-control" type="checkbox" name="show-comment-{{ status.id }}" id="show-comment-{{ status.id }}">
|
|
||||||
<div class="toggle-content hidden">
|
|
||||||
<div class="card-footer">
|
|
||||||
<div class="card-footer-item">
|
|
||||||
{% if status.status_type == 'Boost' %}
|
|
||||||
{% include 'snippets/reply_form.html' with status=status|boosted_status %}
|
|
||||||
{% else %}
|
|
||||||
{% include 'snippets/reply_form.html' with status=status %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="card-footer">
|
|
||||||
<div class="card-footer-item">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
|
|
||||||
<label class="button is-small" for="show-comment-{{ status.id }}">
|
|
||||||
<span class="icon icon-comment"><span class="is-sr-only">Comment</span></span>
|
|
||||||
</label>
|
|
||||||
{% if status.status_type == 'Boost' %}
|
|
||||||
{% include 'snippets/boost_button.html' with status=status|boosted_status %}
|
|
||||||
{% include 'snippets/fav_button.html' with status=status|boosted_status %}
|
|
||||||
{% else %}
|
|
||||||
{% include 'snippets/boost_button.html' with status=status %}
|
|
||||||
{% include 'snippets/fav_button.html' with status=status %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<a href="/login">
|
|
||||||
<span class="icon icon-comment">
|
|
||||||
<span class="is-sr-only">Comment</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="icon icon-boost">
|
|
||||||
<span class="is-sr-only">Boost status</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="icon icon-heart">
|
|
||||||
<span class="is-sr-only">Like status</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-footer-item">
|
|
||||||
{% if status.privacy == 'public' %}
|
|
||||||
<span class="icon icon-globe">
|
|
||||||
<span class="is-sr-only">Public post</span>
|
|
||||||
</span>
|
|
||||||
{% elif status.privacy == 'unlisted' %}
|
|
||||||
<span class="icon icon-unlock">
|
|
||||||
<span class="is-sr-only">Unlisted post</span>
|
|
||||||
</span>
|
|
||||||
{% elif status.privacy == 'followers' %}
|
|
||||||
<span class="icon icon-lock">
|
|
||||||
<span class="is-sr-only">Followers-only post</span>
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="icon icon-envelope">
|
|
||||||
<span class="is-sr-only">Private post</span>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-footer-item">
|
|
||||||
<a href="{{ status.remote_id }}">{{ status.published_date | post_date }}</a>
|
|
||||||
</div>
|
|
||||||
{% if status.user == request.user %}
|
|
||||||
<div class="card-footer-item">
|
|
||||||
<label class="button" for="more-info-{{ status.id }}">
|
|
||||||
<div class="icon icon-dots-three">
|
|
||||||
<span class="is-sr-only">More options</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input class="toggle-control" type="checkbox" name="more-info-{{ status.id }}" id="more-info-{{ status.id }}">
|
|
||||||
<div class="toggle-content hidden card-footer">
|
|
||||||
{% if status.user == request.user %}
|
|
||||||
<div class="card-footer-item">
|
|
||||||
<form name="delete-{{status.id}}" action="/delete-status" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="status" value="{{ status.id }}">
|
|
||||||
<button class="button is-danger" type="submit">
|
|
||||||
Delete post
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="card">
|
|
||||||
<header class="card-header">
|
|
||||||
<p>
|
|
||||||
{% include 'snippets/avatar.html' with user=status.user %}
|
{% include 'snippets/avatar.html' with user=status.user %}
|
||||||
{% include 'snippets/username.html' with user=status.user %}
|
{% include 'snippets/username.html' with user=status.user %}
|
||||||
deleted this status
|
boosted
|
||||||
</p>
|
{% include 'snippets/status_body.html' with status=status|boosted_status %}
|
||||||
</header>
|
{% else %}
|
||||||
</div>
|
{% include 'snippets/status_body.html' with status=status %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
120
bookwyrm/templates/snippets/status_body.html
Normal file
120
bookwyrm/templates/snippets/status_body.html
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
{% load fr_display %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% if not status.deleted %}
|
||||||
|
<div class="card">
|
||||||
|
<header class="card-header">
|
||||||
|
<div class="card-header-title">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-narrow">
|
||||||
|
{% include 'snippets/status_header.html' with status=status %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
{% include 'snippets/status_content.html' with status=status %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<input class="toggle-control" type="checkbox" name="show-comment-{{ status.id }}" id="show-comment-{{ status.id }}">
|
||||||
|
<div class="toggle-content hidden">
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-footer-item">
|
||||||
|
{% include 'snippets/reply_form.html' with status=status %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-footer-item">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
|
||||||
|
<label class="button is-small" for="show-comment-{{ status.id }}">
|
||||||
|
<span class="icon icon-comment"><span class="is-sr-only">Comment</span></span>
|
||||||
|
</label>
|
||||||
|
{% include 'snippets/boost_button.html' with status=status %}
|
||||||
|
{% include 'snippets/fav_button.html' with status=status %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<a href="/login">
|
||||||
|
<span class="icon icon-comment">
|
||||||
|
<span class="is-sr-only">Comment</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="icon icon-boost">
|
||||||
|
<span class="is-sr-only">Boost status</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="icon icon-heart">
|
||||||
|
<span class="is-sr-only">Like status</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer-item">
|
||||||
|
{% if status.privacy == 'public' %}
|
||||||
|
<span class="icon icon-globe">
|
||||||
|
<span class="is-sr-only">Public post</span>
|
||||||
|
</span>
|
||||||
|
{% elif status.privacy == 'unlisted' %}
|
||||||
|
<span class="icon icon-unlock">
|
||||||
|
<span class="is-sr-only">Unlisted post</span>
|
||||||
|
</span>
|
||||||
|
{% elif status.privacy == 'followers' %}
|
||||||
|
<span class="icon icon-lock">
|
||||||
|
<span class="is-sr-only">Followers-only post</span>
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="icon icon-envelope">
|
||||||
|
<span class="is-sr-only">Private post</span>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<a href="{{ status.remote_id }}">{{ status.published_date | post_date }}</a>
|
||||||
|
</div>
|
||||||
|
{% if status.user == request.user %}
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<label class="button" for="more-info-{{ status.id }}">
|
||||||
|
<div class="icon icon-dots-three">
|
||||||
|
<span class="is-sr-only">More options</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input class="toggle-control" type="checkbox" name="more-info-{{ status.id }}" id="more-info-{{ status.id }}">
|
||||||
|
<div class="toggle-content hidden card-footer">
|
||||||
|
{% if status.user == request.user %}
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<form name="delete-{{status.id}}" action="/delete-status" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="status" value="{{ status.id }}">
|
||||||
|
<button class="button is-danger" type="submit">
|
||||||
|
Delete post
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="card">
|
||||||
|
<header class="card-header">
|
||||||
|
<p>
|
||||||
|
{% include 'snippets/avatar.html' with user=status.user %}
|
||||||
|
{% include 'snippets/username.html' with user=status.user %}
|
||||||
|
deleted this status
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
|
@ -64,8 +64,10 @@ class SelfConnector(TestCase):
|
||||||
|
|
||||||
|
|
||||||
def test_search_default_filter(self):
|
def test_search_default_filter(self):
|
||||||
self.edition.default = True
|
''' it should get rid of duplicate editions for the same work '''
|
||||||
self.edition.save()
|
self.work.default_edition = self.edition
|
||||||
|
self.work.save()
|
||||||
|
|
||||||
results = self.connector.search('Anonymous')
|
results = self.connector.search('Anonymous')
|
||||||
self.assertEqual(len(results), 1)
|
self.assertEqual(len(results), 1)
|
||||||
self.assertEqual(results[0].title, 'Edition of Example Work')
|
self.assertEqual(results[0].title, 'Edition of Example Work')
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Favorite(TestCase):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
'mouse', 'mouse@mouse.com', 'mouseword',
|
'mouse', 'mouse@mouse.com', 'mouseword',
|
||||||
remote_id='http://local.com/user/mouse')
|
remote_id='http://local.com/user/mouse')
|
||||||
|
|
||||||
self.status = models.Status.objects.create(
|
self.status = models.Status.objects.create(
|
||||||
user=self.local_user,
|
user=self.local_user,
|
||||||
content='Test status',
|
content='Test status',
|
||||||
|
@ -33,24 +34,13 @@ class Favorite(TestCase):
|
||||||
def test_handle_favorite(self):
|
def test_handle_favorite(self):
|
||||||
activity = {
|
activity = {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': 'http://example.com/activity/1',
|
'id': 'http://example.com/fav/1',
|
||||||
|
|
||||||
'type': 'Create',
|
|
||||||
'actor': 'https://example.com/users/rat',
|
'actor': 'https://example.com/users/rat',
|
||||||
'published': 'Mon, 25 May 2020 19:31:20 GMT',
|
'published': 'Mon, 25 May 2020 19:31:20 GMT',
|
||||||
'to': ['https://example.com/user/rat/followers'],
|
'object': 'http://local.com/status/1',
|
||||||
'cc': ['https://www.w3.org/ns/activitystreams#Public'],
|
|
||||||
'object': {
|
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
||||||
'id': 'http://example.com/fav/1',
|
|
||||||
'type': 'Like',
|
|
||||||
'actor': 'https://example.com/users/rat',
|
|
||||||
'object': 'http://local.com/status/1',
|
|
||||||
},
|
|
||||||
'signature': {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = incoming.handle_favorite(activity)
|
incoming.handle_favorite(activity)
|
||||||
|
|
||||||
fav = models.Favorite.objects.get(remote_id='http://example.com/fav/1')
|
fav = models.Favorite.objects.get(remote_id='http://example.com/fav/1')
|
||||||
self.assertEqual(fav.status, self.status)
|
self.assertEqual(fav.status, self.status)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
''' when a remote user changes their profile '''
|
||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
|
@ -39,17 +39,8 @@ class Book(TestCase):
|
||||||
title='Invalid Book'
|
title='Invalid Book'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_default_edition(self):
|
|
||||||
''' a work should always be able to produce a deafult edition '''
|
|
||||||
self.assertIsInstance(self.work.default_edition, models.Edition)
|
|
||||||
self.assertEqual(self.work.default_edition, self.first_edition)
|
|
||||||
|
|
||||||
self.second_edition.default = True
|
|
||||||
self.second_edition.save()
|
|
||||||
|
|
||||||
self.assertEqual(self.work.default_edition, self.second_edition)
|
|
||||||
|
|
||||||
def test_isbn_10_to_13(self):
|
def test_isbn_10_to_13(self):
|
||||||
|
''' checksums and so on '''
|
||||||
isbn_10 = '178816167X'
|
isbn_10 = '178816167X'
|
||||||
isbn_13 = isbn_10_to_13(isbn_10)
|
isbn_13 = isbn_10_to_13(isbn_10)
|
||||||
self.assertEqual(isbn_13, '9781788161671')
|
self.assertEqual(isbn_13, '9781788161671')
|
||||||
|
@ -59,8 +50,8 @@ class Book(TestCase):
|
||||||
self.assertEqual(isbn_13, '9781788161671')
|
self.assertEqual(isbn_13, '9781788161671')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_isbn_13_to_10(self):
|
def test_isbn_13_to_10(self):
|
||||||
|
''' checksums and so on '''
|
||||||
isbn_13 = '9781788161671'
|
isbn_13 = '9781788161671'
|
||||||
isbn_10 = isbn_13_to_10(isbn_13)
|
isbn_10 = isbn_13_to_10(isbn_13)
|
||||||
self.assertEqual(isbn_10, '178816167X')
|
self.assertEqual(isbn_10, '178816167X')
|
||||||
|
|
|
@ -38,16 +38,6 @@ class Shelving(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
self.assertEqual(shelf.books.get(), self.book)
|
||||||
|
|
||||||
# it should have posted a status about this
|
|
||||||
status = models.GeneratedNote.objects.get()
|
|
||||||
self.assertEqual(status.content, 'wants to read')
|
|
||||||
self.assertEqual(status.user, self.user)
|
|
||||||
self.assertEqual(status.mention_books.count(), 1)
|
|
||||||
self.assertEqual(status.mention_books.first(), self.book)
|
|
||||||
|
|
||||||
# and it should not create a read-through
|
|
||||||
self.assertEqual(models.ReadThrough.objects.count(), 0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_handle_shelve_reading(self):
|
def test_handle_shelve_reading(self):
|
||||||
shelf = models.Shelf.objects.get(identifier='reading')
|
shelf = models.Shelf.objects.get(identifier='reading')
|
||||||
|
@ -56,20 +46,6 @@ class Shelving(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
self.assertEqual(shelf.books.get(), self.book)
|
||||||
|
|
||||||
# it should have posted a status about this
|
|
||||||
status = models.GeneratedNote.objects.order_by('-published_date').first()
|
|
||||||
self.assertEqual(status.content, 'started reading')
|
|
||||||
self.assertEqual(status.user, self.user)
|
|
||||||
self.assertEqual(status.mention_books.count(), 1)
|
|
||||||
self.assertEqual(status.mention_books.first(), self.book)
|
|
||||||
|
|
||||||
# and it should create a read-through
|
|
||||||
readthrough = models.ReadThrough.objects.get()
|
|
||||||
self.assertEqual(readthrough.user, self.user)
|
|
||||||
self.assertEqual(readthrough.book.id, self.book.id)
|
|
||||||
self.assertIsNotNone(readthrough.start_date)
|
|
||||||
self.assertIsNone(readthrough.finish_date)
|
|
||||||
|
|
||||||
|
|
||||||
def test_handle_shelve_read(self):
|
def test_handle_shelve_read(self):
|
||||||
shelf = models.Shelf.objects.get(identifier='read')
|
shelf = models.Shelf.objects.get(identifier='read')
|
||||||
|
@ -78,20 +54,6 @@ class Shelving(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
self.assertEqual(shelf.books.get(), self.book)
|
||||||
|
|
||||||
# it should have posted a status about this
|
|
||||||
status = models.GeneratedNote.objects.order_by('-published_date').first()
|
|
||||||
self.assertEqual(status.content, 'finished reading')
|
|
||||||
self.assertEqual(status.user, self.user)
|
|
||||||
self.assertEqual(status.mention_books.count(), 1)
|
|
||||||
self.assertEqual(status.mention_books.first(), self.book)
|
|
||||||
|
|
||||||
# and it should update the existing read-through
|
|
||||||
readthrough = models.ReadThrough.objects.get()
|
|
||||||
self.assertEqual(readthrough.user, self.user)
|
|
||||||
self.assertEqual(readthrough.book.id, self.book.id)
|
|
||||||
self.assertIsNotNone(readthrough.start_date)
|
|
||||||
self.assertIsNotNone(readthrough.finish_date)
|
|
||||||
|
|
||||||
|
|
||||||
def test_handle_unshelve(self):
|
def test_handle_unshelve(self):
|
||||||
self.shelf.books.add(self.book)
|
self.shelf.books.add(self.book)
|
||||||
|
|
|
@ -15,6 +15,8 @@ class Book(TestCase):
|
||||||
title='Example Edition',
|
title='Example Edition',
|
||||||
parent_work=self.work
|
parent_work=self.work
|
||||||
)
|
)
|
||||||
|
self.work.default_edition = self.edition
|
||||||
|
self.work.save()
|
||||||
|
|
||||||
self.connector = models.Connector.objects.create(
|
self.connector = models.Connector.objects.create(
|
||||||
identifier='test_connector',
|
identifier='test_connector',
|
||||||
|
|
|
@ -39,6 +39,7 @@ class Signature(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def send(self, signature, now, data, digest):
|
def send(self, signature, now, data, digest):
|
||||||
|
''' test request '''
|
||||||
c = Client()
|
c = Client()
|
||||||
return c.post(
|
return c.post(
|
||||||
urlsplit(self.rat.inbox).path,
|
urlsplit(self.rat.inbox).path,
|
||||||
|
@ -73,13 +74,13 @@ class Signature(TestCase):
|
||||||
|
|
||||||
def test_wrong_signature(self):
|
def test_wrong_signature(self):
|
||||||
''' Messages must be signed by the right actor.
|
''' Messages must be signed by the right actor.
|
||||||
(cat cannot sign messages on behalf of mouse)
|
(cat cannot sign messages on behalf of mouse) '''
|
||||||
'''
|
|
||||||
response = self.send_test_request(sender=self.mouse, signer=self.cat)
|
response = self.send_test_request(sender=self.mouse, signer=self.cat)
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_remote_signer(self):
|
def test_remote_signer(self):
|
||||||
|
''' signtures 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
|
||||||
|
@ -138,7 +139,6 @@ class Signature(TestCase):
|
||||||
json=data,
|
json=data,
|
||||||
status=200)
|
status=200)
|
||||||
|
|
||||||
|
|
||||||
# Key correct:
|
# Key correct:
|
||||||
response = self.send_test_request(sender=self.fake_remote)
|
response = self.send_test_request(sender=self.fake_remote)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
|
@ -116,6 +116,7 @@ urlpatterns = [
|
||||||
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
||||||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
||||||
re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost),
|
re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost),
|
||||||
|
re_path(r'^unboost/(?P<status_id>\d+)/?$', actions.unboost),
|
||||||
|
|
||||||
re_path(r'^delete-status/?$', actions.delete_status),
|
re_path(r'^delete-status/?$', actions.delete_status),
|
||||||
|
|
||||||
|
|
|
@ -508,6 +508,7 @@ def unfavorite(request, status_id):
|
||||||
outgoing.handle_unfavorite(request.user, status)
|
outgoing.handle_unfavorite(request.user, status)
|
||||||
return redirect(request.headers.get('Referer', '/'))
|
return redirect(request.headers.get('Referer', '/'))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def boost(request, status_id):
|
def boost(request, status_id):
|
||||||
''' boost a status '''
|
''' boost a status '''
|
||||||
|
@ -516,6 +517,14 @@ def boost(request, status_id):
|
||||||
return redirect(request.headers.get('Referer', '/'))
|
return redirect(request.headers.get('Referer', '/'))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def unboost(request, status_id):
|
||||||
|
''' boost a status '''
|
||||||
|
status = models.Status.objects.get(id=status_id)
|
||||||
|
outgoing.handle_unboost(request.user, status)
|
||||||
|
return redirect(request.headers.get('Referer', '/'))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_status(request):
|
def delete_status(request):
|
||||||
''' delete and tombstone a status '''
|
''' delete and tombstone a status '''
|
||||||
|
|
|
@ -71,7 +71,8 @@ services:
|
||||||
- redis
|
- redis
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
flower:
|
flower:
|
||||||
image: mher/flower
|
build: .
|
||||||
|
command: flower --port=8888
|
||||||
env_file: .env
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
- CELERY_BROKER_URL=${CELERY_BROKER}
|
- CELERY_BROKER_URL=${CELERY_BROKER}
|
||||||
|
|
Loading…
Reference in a new issue