forked from mirrors/bookwyrm
Adds quotes
This commit is contained in:
parent
5b7f29c45b
commit
a88cf2b6dd
16 changed files with 190 additions and 7 deletions
|
@ -9,6 +9,7 @@ from .shelve import get_add, get_remove
|
|||
from .status import get_review, get_review_article
|
||||
from .status import get_rating, get_rating_note
|
||||
from .status import get_comment, get_comment_article
|
||||
from .status import get_quotation, get_quotation_article
|
||||
from .status import get_status, get_replies, get_replies_page
|
||||
from .status import get_favorite, get_unfavorite
|
||||
from .status import get_boost
|
||||
|
|
|
@ -14,6 +14,29 @@ def get_rating(review):
|
|||
review.rating, review.book.title)
|
||||
return status
|
||||
|
||||
|
||||
def get_quotation(quotation):
|
||||
''' fedireads json for quotations '''
|
||||
status = get_status(quotation)
|
||||
status['inReplyToBook'] = quotation.book.absolute_id
|
||||
status['fedireadsType'] = quotation.status_type
|
||||
status['quote'] = quotation.quote
|
||||
return status
|
||||
|
||||
|
||||
def get_quotation_article(quotation):
|
||||
''' a book quotation formatted for a non-fedireads isntance (mastodon) '''
|
||||
status = get_status(quotation)
|
||||
content = '"%s"<br>-- <a href="%s">"%s"</a>)<br><br>%s' % (
|
||||
quotation.quote,
|
||||
quotation.book.absolute_id,
|
||||
quotation.book.title,
|
||||
quotation.content,
|
||||
)
|
||||
status['content'] = content
|
||||
return status
|
||||
|
||||
|
||||
def get_review(review):
|
||||
''' fedireads json for book reviews '''
|
||||
status = get_status(review)
|
||||
|
|
|
@ -53,6 +53,17 @@ class CommentForm(ModelForm):
|
|||
}
|
||||
|
||||
|
||||
class QuotationForm(ModelForm):
|
||||
class Meta:
|
||||
model = models.Quotation
|
||||
fields = ['quote', 'content']
|
||||
help_texts = {f: None for f in fields}
|
||||
labels = {
|
||||
'quote': 'Quote',
|
||||
'content': 'Comment',
|
||||
}
|
||||
|
||||
|
||||
class ReplyForm(ModelForm):
|
||||
class Meta:
|
||||
model = models.Status
|
||||
|
|
26
fedireads/migrations/0030_quotation.py
Normal file
26
fedireads/migrations/0030_quotation.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 3.0.3 on 2020-04-07 00:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fedireads', '0029_auto_20200403_1835'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Quotation',
|
||||
fields=[
|
||||
('status_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='fedireads.Status')),
|
||||
('quote', models.TextField()),
|
||||
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Edition')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('fedireads.status',),
|
||||
),
|
||||
]
|
|
@ -1,6 +1,7 @@
|
|||
''' bring all the models into the app namespace '''
|
||||
from .book import Connector, Book, Work, Edition, Author
|
||||
from .shelf import Shelf, ShelfBook
|
||||
from .status import Status, Review, Comment, Favorite, Boost, Tag, Notification
|
||||
from .status import Status, Review, Comment, Quotation
|
||||
from .status import Favorite, Boost, Tag, Notification
|
||||
from .user import User, UserFollows, UserFollowRequest, UserBlocks
|
||||
from .user import FederatedServer
|
||||
|
|
|
@ -56,6 +56,17 @@ class Comment(Status):
|
|||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class Quotation(Status):
|
||||
''' like a review but without a rating and transient '''
|
||||
book = models.ForeignKey('Edition', on_delete=models.PROTECT)
|
||||
quote = models.TextField()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.status_type = 'Quotation'
|
||||
self.activity_type = 'Note'
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class Review(Status):
|
||||
''' a book review '''
|
||||
name = models.CharField(max_length=255, null=True)
|
||||
|
|
|
@ -9,7 +9,8 @@ import requests
|
|||
from fedireads import activitypub
|
||||
from fedireads import models
|
||||
from fedireads.broadcast import get_recipients, broadcast
|
||||
from fedireads.status import create_review, create_status, create_comment
|
||||
from fedireads.status import create_review, create_status
|
||||
from fedireads.status import create_quotation, create_comment
|
||||
from fedireads.status import create_tag, create_notification, create_rating
|
||||
from fedireads.remote_user import get_or_create_remote_user
|
||||
|
||||
|
@ -222,6 +223,24 @@ def handle_review(user, book, name, content, rating):
|
|||
broadcast(user, article_create_activity, other_recipients)
|
||||
|
||||
|
||||
def handle_quotation(user, book, content, quote):
|
||||
''' post a review '''
|
||||
# validated and saves the review in the database so it has an id
|
||||
quotation = create_quotation(user, book, content, quote)
|
||||
|
||||
quotation_activity = activitypub.get_quotation(quotation)
|
||||
quotation_create_activity = activitypub.get_create(user, quotation_activity)
|
||||
fr_recipients = get_recipients(user, 'public', limit='fedireads')
|
||||
broadcast(user, quotation_create_activity, fr_recipients)
|
||||
|
||||
# re-format the activity for non-fedireads servers
|
||||
article_activity = activitypub.get_quotation_article(quotation)
|
||||
article_create_activity = activitypub.get_create(user, article_activity)
|
||||
|
||||
other_recipients = get_recipients(user, 'public', limit='other')
|
||||
broadcast(user, article_create_activity, other_recipients)
|
||||
|
||||
|
||||
def handle_comment(user, book, content):
|
||||
''' post a review '''
|
||||
# validated and saves the review in the database so it has an id
|
||||
|
|
|
@ -222,6 +222,9 @@ body {
|
|||
width: 30rem;
|
||||
height: 10rem;
|
||||
}
|
||||
.review-form.quote-form textarea#id_content {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -578,12 +581,33 @@ input:checked ~ .compose-suggestion {
|
|||
blockquote {
|
||||
white-space: pre-line;
|
||||
}
|
||||
blockquote .icon-quote-open {
|
||||
float: left;
|
||||
blockquote .icon-quote-open, blockquote .icon-quote-close, .quote .icon-quote-open, .quote .icon-quote-close {
|
||||
font-size: 2rem;
|
||||
margin-right: 0.5rem;
|
||||
color: #888;
|
||||
}
|
||||
blockquote .icon-quote-open, .quote .icon-quote-close {
|
||||
float: left;
|
||||
}
|
||||
.quote {
|
||||
margin-bottom: 2em;
|
||||
position: relative;
|
||||
}
|
||||
.quote .icon-quote-open, .quote .icon-quote-close {
|
||||
position: absolute;
|
||||
}
|
||||
.quote .icon-quote-open {
|
||||
top: -0.5rem;
|
||||
}
|
||||
.quote .icon-quote-close {
|
||||
right: 0;
|
||||
bottom: 1.5rem;
|
||||
}
|
||||
.quote blockquote {
|
||||
background-color: white;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.interaction {
|
||||
background-color: #B2DBBF;
|
||||
|
|
|
@ -56,6 +56,38 @@ def create_review(user, book, name, content, rating):
|
|||
)
|
||||
|
||||
|
||||
def create_quotation_from_activity(author, activity):
|
||||
''' parse an activity json blob into a status '''
|
||||
book = activity['inReplyToBook']
|
||||
book = book.split('/')[-1]
|
||||
quote = activity.get('quote')
|
||||
content = activity.get('content')
|
||||
published = activity.get('published')
|
||||
remote_id = activity['id']
|
||||
|
||||
quotation = create_quotation(author, book, content, quote)
|
||||
quotation.published_date = published
|
||||
quotation.remote_id = remote_id
|
||||
quotation.save()
|
||||
return quotation
|
||||
|
||||
|
||||
def create_quotation(user, possible_book, content, quote):
|
||||
''' a quotation has been added '''
|
||||
# throws a value error if the book is not found
|
||||
book = get_or_create_book(possible_book)
|
||||
content = sanitize(content)
|
||||
quote = sanitize(quote)
|
||||
|
||||
return models.Quotation.objects.create(
|
||||
user=user,
|
||||
book=book,
|
||||
content=content,
|
||||
quote=quote,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def create_comment_from_activity(author, activity):
|
||||
''' parse an activity json blob into a status '''
|
||||
book = activity['inReplyToBook']
|
||||
|
|
|
@ -15,8 +15,8 @@ a <a href="/book/{{ book.fedireads_key }}">{{ book.title }}</a>
|
|||
<div class="tab" data-id="tab-comment-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||
<a href="{{ book.absolute_id }}/comment" onclick="tabChange(event)">Comment</a>
|
||||
</div>
|
||||
<div class="tab" data-id="tab-quote-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||
Quote
|
||||
<div class="tab" data-id="tab-quotation-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||
<a href="{{ book.absolute_id }}/quotation" onclick="tabChange(event)">Quote</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -38,4 +38,11 @@ a <a href="/book/{{ book.fedireads_key }}">{{ book.title }}</a>
|
|||
{{ comment_form.as_p }}
|
||||
<button type="submit">post comment</button>
|
||||
</form>
|
||||
|
||||
<form class="hidden tab-option-{{ book.id }} review-form quote-form" name="quotation" action="/quotate/" method="post" id="tab-quotation-{{ book.id }}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.fedireads_key }}"></input>
|
||||
{{ quotation_form.as_p }}
|
||||
<button typr="submit">post quote</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -40,6 +40,16 @@
|
|||
</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if status.quote %}
|
||||
<div class="quote">
|
||||
<span class="icon icon-quote-open"></span>
|
||||
<blockquote>{{ status.quote }}</blockquote>
|
||||
<span class="icon icon-quote-close"></span>
|
||||
|
||||
<p> — {% include 'snippets/book_titleby.html' with book=status.book %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if status.content and status.status_type != 'Update' and status.status_type != 'Boost' %}
|
||||
<blockquote>{{ status.content | safe }}</blockquote>
|
||||
{% endif %}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
reviewed <a href="{{ status.book.absolute_id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Comment' %}
|
||||
commented on <a href="{{ status.book.absolute_id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Quotation' %}
|
||||
quoted <a href="{{ status.book.absolute_id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Boost' %}
|
||||
boosted
|
||||
{% elif status.reply_parent %}
|
||||
|
|
|
@ -80,6 +80,7 @@ urlpatterns = [
|
|||
|
||||
re_path(r'^rate/?$', actions.rate),
|
||||
re_path(r'^review/?$', actions.review),
|
||||
re_path(r'^quotate/?$', actions.quotate),
|
||||
re_path(r'^comment/?$', actions.comment),
|
||||
re_path(r'^tag/?$', actions.tag),
|
||||
re_path(r'^untag/?$', actions.untag),
|
||||
|
|
|
@ -228,6 +228,21 @@ def review(request):
|
|||
return redirect('/book/%s' % book_identifier)
|
||||
|
||||
|
||||
@login_required
|
||||
def quotate(request):
|
||||
''' create a book quotation '''
|
||||
form = forms.QuotationForm(request.POST)
|
||||
book_identifier = request.POST.get('book')
|
||||
if not form.is_valid():
|
||||
return redirect('/book/%s' % book_identifier)
|
||||
|
||||
quote = form.cleaned_data.get('quote')
|
||||
content = form.cleaned_data.get('content')
|
||||
|
||||
outgoing.handle_quotation(request.user, book_identifier, content, quote)
|
||||
return redirect('/book/%s' % book_identifier)
|
||||
|
||||
|
||||
@login_required
|
||||
def comment(request):
|
||||
''' create a book comment '''
|
||||
|
|
|
@ -92,6 +92,7 @@ def home_tab(request, tab):
|
|||
],
|
||||
'active_tab': tab,
|
||||
'review_form': forms.ReviewForm(),
|
||||
'quotation_form': forms.QuotationForm(),
|
||||
'comment_form': forms.CommentForm(),
|
||||
'next': next_page if activity_count > (page_size * page) else None,
|
||||
'prev': prev_page if page > 1 else None,
|
||||
|
|
1
fr-dev
1
fr-dev
|
@ -36,6 +36,5 @@ case "$1" in
|
|||
;;
|
||||
*)
|
||||
echo "Unrecognised command. Try: up, initdb, resetdb,makemigrations, migrate, shell, dbshell "
|
||||
docker-compose build
|
||||
;;
|
||||
esac
|
||||
|
|
Loading…
Reference in a new issue