mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-04 15:26:48 +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
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
FROM python:3.9
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN mkdir /app
|
||||
RUN mkdir /app/static
|
||||
RUN mkdir /app/images
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app/
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY ./bookwyrm /app
|
||||
COPY ./celerywyrm /app
|
||||
|
|
|
@ -76,11 +76,12 @@ class ActivityObject:
|
|||
if not isinstance(self, model.activity_serializer):
|
||||
raise TypeError('Wrong activity type for model')
|
||||
|
||||
# check for an existing instance
|
||||
try:
|
||||
return model.objects.get(remote_id=self.id)
|
||||
except model.DoesNotExist:
|
||||
pass
|
||||
# check for an existing instance, if we're not updating a known obj
|
||||
if not instance:
|
||||
try:
|
||||
return model.objects.get(remote_id=self.id)
|
||||
except model.DoesNotExist:
|
||||
pass
|
||||
|
||||
model_fields = [m.name for m in model._meta.get_fields()]
|
||||
mapped_fields = {}
|
||||
|
|
|
@ -269,7 +269,12 @@ def handle_favorite(activity):
|
|||
@app.task
|
||||
def handle_unfavorite(activity):
|
||||
''' 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()
|
||||
|
||||
|
||||
|
@ -294,7 +299,7 @@ def handle_unboost(activity):
|
|||
remote_id=activity['object']['id']
|
||||
).first()
|
||||
if boost:
|
||||
status_builder.delete_status(boost)
|
||||
boost.delete()
|
||||
|
||||
|
||||
@app.task
|
||||
|
|
|
@ -299,7 +299,8 @@ def handle_unfavorite(user, status):
|
|||
# can't find that status, idk
|
||||
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])
|
||||
|
||||
|
||||
|
@ -319,6 +320,17 @@ def handle_boost(user, status):
|
|||
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):
|
||||
''' broadcast the news about our book '''
|
||||
broadcast(user, book.to_update_activity(user))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% load fr_display %}
|
||||
{% with activity.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 }}">
|
||||
{% with status.id|uuid as 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 %}
|
||||
<button class="button is-small" type="submit">
|
||||
<span class="icon icon-boost">
|
||||
|
@ -8,7 +8,7 @@
|
|||
</span>
|
||||
</button>
|
||||
</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 %}
|
||||
<button class="button is-small is-success" type="submit">
|
||||
<span class="icon icon-boost">
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% 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>
|
||||
{% if type == 'quote' %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% load fr_display %}
|
||||
{% with activity.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 }}">
|
||||
{% with status.id|uuid as 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 %}
|
||||
<button class="button is-small" type="submit">
|
||||
<span class="icon icon-heart">
|
||||
|
@ -8,7 +8,7 @@
|
|||
</span>
|
||||
</button>
|
||||
</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 %}
|
||||
<button class="button is-success is-small" type="submit">
|
||||
<span class="icon icon-heart">
|
||||
|
|
|
@ -1,142 +1,11 @@
|
|||
{% load humanize %}
|
||||
{% load fr_display %}
|
||||
|
||||
{% if not status.deleted %}
|
||||
<div class="card">
|
||||
<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>
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/avatar.html' with user=status.user %}
|
||||
{% include 'snippets/username.html' with user=status.user %}
|
||||
deleted this status
|
||||
</p>
|
||||
</header>
|
||||
</div>
|
||||
boosted
|
||||
{% include 'snippets/status_body.html' with status=status|boosted_status %}
|
||||
{% else %}
|
||||
{% include 'snippets/status_body.html' with status=status %}
|
||||
{% 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):
|
||||
self.edition.default = True
|
||||
self.edition.save()
|
||||
''' it should get rid of duplicate editions for the same work '''
|
||||
self.work.default_edition = self.edition
|
||||
self.work.save()
|
||||
|
||||
results = self.connector.search('Anonymous')
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].title, 'Edition of Example Work')
|
||||
|
|
|
@ -17,6 +17,7 @@ class Favorite(TestCase):
|
|||
self.local_user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.com', 'mouseword',
|
||||
remote_id='http://local.com/user/mouse')
|
||||
|
||||
self.status = models.Status.objects.create(
|
||||
user=self.local_user,
|
||||
content='Test status',
|
||||
|
@ -33,24 +34,13 @@ class Favorite(TestCase):
|
|||
def test_handle_favorite(self):
|
||||
activity = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': 'http://example.com/activity/1',
|
||||
|
||||
'type': 'Create',
|
||||
'id': 'http://example.com/fav/1',
|
||||
'actor': 'https://example.com/users/rat',
|
||||
'published': 'Mon, 25 May 2020 19:31:20 GMT',
|
||||
'to': ['https://example.com/user/rat/followers'],
|
||||
'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': {}
|
||||
'object': 'http://local.com/status/1',
|
||||
}
|
||||
|
||||
result = incoming.handle_favorite(activity)
|
||||
incoming.handle_favorite(activity)
|
||||
|
||||
fav = models.Favorite.objects.get(remote_id='http://example.com/fav/1')
|
||||
self.assertEqual(fav.status, self.status)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
''' when a remote user changes their profile '''
|
||||
import json
|
||||
import pathlib
|
||||
from django.test import TestCase
|
||||
|
|
|
@ -39,17 +39,8 @@ class Book(TestCase):
|
|||
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):
|
||||
''' checksums and so on '''
|
||||
isbn_10 = '178816167X'
|
||||
isbn_13 = isbn_10_to_13(isbn_10)
|
||||
self.assertEqual(isbn_13, '9781788161671')
|
||||
|
@ -59,8 +50,8 @@ class Book(TestCase):
|
|||
self.assertEqual(isbn_13, '9781788161671')
|
||||
|
||||
|
||||
|
||||
def test_isbn_13_to_10(self):
|
||||
''' checksums and so on '''
|
||||
isbn_13 = '9781788161671'
|
||||
isbn_10 = isbn_13_to_10(isbn_13)
|
||||
self.assertEqual(isbn_10, '178816167X')
|
||||
|
|
|
@ -38,16 +38,6 @@ class Shelving(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
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):
|
||||
shelf = models.Shelf.objects.get(identifier='reading')
|
||||
|
@ -56,20 +46,6 @@ class Shelving(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
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):
|
||||
shelf = models.Shelf.objects.get(identifier='read')
|
||||
|
@ -78,20 +54,6 @@ class Shelving(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
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):
|
||||
self.shelf.books.add(self.book)
|
||||
|
|
|
@ -15,6 +15,8 @@ class Book(TestCase):
|
|||
title='Example Edition',
|
||||
parent_work=self.work
|
||||
)
|
||||
self.work.default_edition = self.edition
|
||||
self.work.save()
|
||||
|
||||
self.connector = models.Connector.objects.create(
|
||||
identifier='test_connector',
|
||||
|
|
|
@ -39,6 +39,7 @@ class Signature(TestCase):
|
|||
)
|
||||
|
||||
def send(self, signature, now, data, digest):
|
||||
''' test request '''
|
||||
c = Client()
|
||||
return c.post(
|
||||
urlsplit(self.rat.inbox).path,
|
||||
|
@ -73,13 +74,13 @@ class Signature(TestCase):
|
|||
|
||||
def test_wrong_signature(self):
|
||||
''' 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)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
@responses.activate
|
||||
def test_remote_signer(self):
|
||||
''' signtures for remote users '''
|
||||
datafile = pathlib.Path(__file__).parent.joinpath('data/ap_user.json')
|
||||
data = json.loads(datafile.read_bytes())
|
||||
data['id'] = self.fake_remote.remote_id
|
||||
|
@ -138,7 +139,6 @@ class Signature(TestCase):
|
|||
json=data,
|
||||
status=200)
|
||||
|
||||
|
||||
# Key correct:
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
@ -116,6 +116,7 @@ urlpatterns = [
|
|||
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
||||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
||||
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),
|
||||
|
||||
|
|
|
@ -508,6 +508,7 @@ def unfavorite(request, status_id):
|
|||
outgoing.handle_unfavorite(request.user, status)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
|
||||
@login_required
|
||||
def boost(request, status_id):
|
||||
''' boost a status '''
|
||||
|
@ -516,6 +517,14 @@ def boost(request, status_id):
|
|||
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
|
||||
def delete_status(request):
|
||||
''' delete and tombstone a status '''
|
||||
|
|
|
@ -71,7 +71,8 @@ services:
|
|||
- redis
|
||||
restart: on-failure
|
||||
flower:
|
||||
image: mher/flower
|
||||
build: .
|
||||
command: flower --port=8888
|
||||
env_file: .env
|
||||
environment:
|
||||
- CELERY_BROKER_URL=${CELERY_BROKER}
|
||||
|
|
Loading…
Reference in a new issue