mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2025-01-24 07:58:08 +00:00
parent
3f96c8cd9d
commit
7862af9729
16 changed files with 222 additions and 31 deletions
|
@ -1,8 +1,12 @@
|
||||||
''' bring activitypub functions into the namespace '''
|
''' bring activitypub functions into the namespace '''
|
||||||
from .actor import get_actor
|
from .actor import get_actor
|
||||||
from .collection import get_outbox, get_outbox_page, get_add, get_remove, \
|
from .collection import get_outbox, get_outbox_page
|
||||||
get_following, get_followers
|
from .collection import get_add, get_remove
|
||||||
|
from .collection import get_following, get_followers
|
||||||
from .create import get_create
|
from .create import get_create
|
||||||
from .follow import get_follow_request, get_unfollow, get_accept, get_reject
|
from .follow import get_follow_request, get_unfollow, get_accept, get_reject
|
||||||
from .status import get_review, get_review_article, get_status, get_replies, \
|
from .status import get_review, get_review_article
|
||||||
get_favorite, get_unfavorite, get_add_tag, get_remove_tag, get_replies_page
|
from .status import get_comment, get_comment_article
|
||||||
|
from .status import get_status, get_replies, get_replies_page
|
||||||
|
from .status import get_favorite, get_unfavorite
|
||||||
|
from .status import get_add_tag, get_remove_tag
|
||||||
|
|
|
@ -12,6 +12,15 @@ def get_review(review):
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def get_comment(comment):
|
||||||
|
''' fedireads json for book reviews '''
|
||||||
|
status = get_status(comment)
|
||||||
|
status['inReplyToBook'] = comment.book.absolute_id
|
||||||
|
status['fedireadsType'] = comment.status_type
|
||||||
|
status['name'] = comment.name
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
def get_review_article(review):
|
def get_review_article(review):
|
||||||
''' a book review formatted for a non-fedireads isntance (mastodon) '''
|
''' a book review formatted for a non-fedireads isntance (mastodon) '''
|
||||||
status = get_status(review)
|
status = get_status(review)
|
||||||
|
@ -24,6 +33,17 @@ def get_review_article(review):
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def get_comment_article(comment):
|
||||||
|
''' a book comment formatted for a non-fedireads isntance (mastodon) '''
|
||||||
|
status = get_status(comment)
|
||||||
|
name = '%s (comment on "%s")' % (
|
||||||
|
comment.name,
|
||||||
|
comment.book.title
|
||||||
|
)
|
||||||
|
status['name'] = name
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
def get_status(status):
|
def get_status(status):
|
||||||
''' create activitypub json for a status '''
|
''' create activitypub json for a status '''
|
||||||
user = status.user
|
user = status.user
|
||||||
|
|
|
@ -41,6 +41,17 @@ class ReviewForm(ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(ModelForm):
|
class CommentForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Comment
|
||||||
|
fields = ['name', 'content']
|
||||||
|
help_texts = {f: None for f in fields}
|
||||||
|
labels = {
|
||||||
|
'name': 'Title',
|
||||||
|
'content': 'Comment',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ReplyForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Status
|
model = models.Status
|
||||||
fields = ['content']
|
fields = ['content']
|
||||||
|
|
|
@ -236,6 +236,19 @@ def handle_incoming_create(activity):
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
elif activity['object'].get('fedireadsType') == 'Comment' and \
|
||||||
|
'inReplyToBook' in activity['object']:
|
||||||
|
if user.local:
|
||||||
|
comment_id = activity['object']['id'].split('/')[-1]
|
||||||
|
models.Comment.objects.get(id=comment_id)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
status_builder.create_comment_from_activity(
|
||||||
|
user,
|
||||||
|
activity['object']
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
elif not user.local:
|
elif not user.local:
|
||||||
try:
|
try:
|
||||||
status = status_builder.create_status_from_activity(
|
status = status_builder.create_status_from_activity(
|
||||||
|
|
26
fedireads/migrations/0019_comment.py
Normal file
26
fedireads/migrations/0019_comment.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 3.0.3 on 2020-03-21 22:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fedireads', '0018_favorite_remote_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Comment',
|
||||||
|
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')),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=('fedireads.status',),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,6 @@
|
||||||
''' bring all the models into the app namespace '''
|
''' bring all the models into the app namespace '''
|
||||||
from .book import Book, Work, Edition, Author
|
from .book import Book, Work, Edition, Author
|
||||||
from .shelf import Shelf, ShelfBook
|
from .shelf import Shelf, ShelfBook
|
||||||
from .status import Status, Review, Favorite, Tag, Notification
|
from .status import Status, Review, Comment, Favorite, Tag, Notification
|
||||||
from .user import User, FederatedServer, UserFollows, UserFollowRequest, UserBlocks
|
from .user import User, FederatedServer, UserFollows, UserFollowRequest, \
|
||||||
|
UserBlocks
|
||||||
|
|
|
@ -46,6 +46,17 @@ class Status(FedireadsModel):
|
||||||
return '%s/%s/%d' % (base_path, model_name, self.id)
|
return '%s/%s/%d' % (base_path, model_name, self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(Status):
|
||||||
|
''' like a review but without a rating and transient '''
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
book = models.ForeignKey('Book', on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.status_type = 'Comment'
|
||||||
|
self.activity_type = 'Article'
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Review(Status):
|
class Review(Status):
|
||||||
''' a book review '''
|
''' a book review '''
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from urllib.parse import urlencode
|
||||||
from fedireads import activitypub
|
from fedireads import activitypub
|
||||||
from fedireads import models
|
from fedireads import models
|
||||||
from fedireads.status import create_review, create_status, create_tag, \
|
from fedireads.status import create_review, create_status, create_tag, \
|
||||||
create_notification
|
create_notification, create_comment
|
||||||
from fedireads.remote_user import get_or_create_remote_user
|
from fedireads.remote_user import get_or_create_remote_user
|
||||||
from fedireads.broadcast import get_recipients, broadcast
|
from fedireads.broadcast import get_recipients, broadcast
|
||||||
|
|
||||||
|
@ -175,6 +175,24 @@ def handle_review(user, book, name, content, rating):
|
||||||
broadcast(user, article_create_activity, other_recipients)
|
broadcast(user, article_create_activity, other_recipients)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_comment(user, book, name, content):
|
||||||
|
''' post a review '''
|
||||||
|
# validated and saves the review in the database so it has an id
|
||||||
|
comment = create_comment(user, book, name, content)
|
||||||
|
|
||||||
|
comment_activity = activitypub.get_comment(comment)
|
||||||
|
comment_create_activity = activitypub.get_create(user, comment_activity)
|
||||||
|
fr_recipients = get_recipients(user, 'public', limit='fedireads')
|
||||||
|
broadcast(user, comment_create_activity, fr_recipients)
|
||||||
|
|
||||||
|
# re-format the activity for non-fedireads servers
|
||||||
|
article_activity = activitypub.get_comment_article(comment)
|
||||||
|
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_tag(user, book, name):
|
def handle_tag(user, book, name):
|
||||||
''' tag a book '''
|
''' tag a book '''
|
||||||
tag = create_tag(user, book, name)
|
tag = create_tag(user, book, name)
|
||||||
|
@ -195,19 +213,19 @@ def handle_untag(user, book, name):
|
||||||
broadcast(user, tag_activity, recipients)
|
broadcast(user, tag_activity, recipients)
|
||||||
|
|
||||||
|
|
||||||
def handle_comment(user, review, content):
|
def handle_reply(user, review, content):
|
||||||
''' respond to a review or status '''
|
''' respond to a review or status '''
|
||||||
# validated and saves the comment in the database so it has an id
|
# validated and saves the comment in the database so it has an id
|
||||||
comment = create_status(user, content, reply_parent=review)
|
reply = create_status(user, content, reply_parent=review)
|
||||||
if comment.reply_parent:
|
if reply.reply_parent:
|
||||||
create_notification(
|
create_notification(
|
||||||
comment.reply_parent.user,
|
reply.reply_parent.user,
|
||||||
'REPLY',
|
'REPLY',
|
||||||
related_user=user,
|
related_user=user,
|
||||||
related_status=comment,
|
related_status=reply,
|
||||||
)
|
)
|
||||||
comment_activity = activitypub.get_status(comment)
|
reply_activity = activitypub.get_status(reply)
|
||||||
create_activity = activitypub.get_create(user, comment_activity)
|
create_activity = activitypub.get_create(user, reply_activity)
|
||||||
|
|
||||||
recipients = get_recipients(user, 'public')
|
recipients = get_recipients(user, 'public')
|
||||||
broadcast(user, create_activity, recipients)
|
broadcast(user, create_activity, recipients)
|
||||||
|
|
|
@ -18,13 +18,39 @@ function interact(e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function comment(e) {
|
function reply(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
ajaxPost(e.target);
|
ajaxPost(e.target);
|
||||||
// TODO: display comment
|
// TODO: display comment
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tabChange(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var target = e.target.parentElement;
|
||||||
|
var identifier = target.getAttribute('data-id');
|
||||||
|
|
||||||
|
var options_class = target.getAttribute('data-category');
|
||||||
|
var options = document.getElementsByClassName(options_class);
|
||||||
|
for (var i = 0; i < options.length; i++) {
|
||||||
|
if (!options[i].className.includes('hidden')) {
|
||||||
|
options[i].className += ' hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabs = target.parentElement.children;
|
||||||
|
for (i = 0; i < tabs.length; i++) {
|
||||||
|
if (tabs[i].getAttribute('data-id') == identifier) {
|
||||||
|
tabs[i].className += ' active';
|
||||||
|
} else {
|
||||||
|
tabs[i].className = tabs[i].className.replace('active', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var el = document.getElementById(identifier);
|
||||||
|
el.className = el.className.replace('hidden', '');
|
||||||
|
}
|
||||||
|
|
||||||
function ajaxPost(form) {
|
function ajaxPost(form) {
|
||||||
fetch(form.action, {
|
fetch(form.action, {
|
||||||
method : "POST",
|
method : "POST",
|
||||||
|
|
|
@ -41,6 +41,36 @@ def create_review(user, possible_book, name, content, rating):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_comment_from_activity(author, activity):
|
||||||
|
''' parse an activity json blob into a status '''
|
||||||
|
book = activity['inReplyToBook']
|
||||||
|
book = book.split('/')[-1]
|
||||||
|
name = activity.get('name')
|
||||||
|
content = activity.get('content')
|
||||||
|
published = activity.get('published')
|
||||||
|
remote_id = activity['id']
|
||||||
|
|
||||||
|
comment = create_comment(author, book, name, content, rating)
|
||||||
|
comment.published_date = published
|
||||||
|
comment.remote_id = remote_id
|
||||||
|
comment.save()
|
||||||
|
return comment
|
||||||
|
|
||||||
|
|
||||||
|
def create_comment(user, possible_book, name, content):
|
||||||
|
''' a book comment has been added '''
|
||||||
|
# throws a value error if the book is not found
|
||||||
|
book = get_or_create_book(possible_book)
|
||||||
|
content = sanitize(content)
|
||||||
|
|
||||||
|
return models.Comment.objects.create(
|
||||||
|
user=user,
|
||||||
|
book=book,
|
||||||
|
name=name,
|
||||||
|
content=content,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_status_from_activity(author, activity):
|
def create_status_from_activity(author, activity):
|
||||||
''' parse a status object out of an activity json blob '''
|
''' parse a status object out of an activity json blob '''
|
||||||
content = activity.get('content')
|
content = activity.get('content')
|
||||||
|
@ -58,6 +88,7 @@ def create_status_from_activity(author, activity):
|
||||||
|
|
||||||
|
|
||||||
def create_favorite_from_activity(user, activity):
|
def create_favorite_from_activity(user, activity):
|
||||||
|
''' create a new favorite entry '''
|
||||||
status = get_status(activity['object'])
|
status = get_status(activity['object'])
|
||||||
remote_id = activity['id']
|
remote_id = activity['id']
|
||||||
try:
|
try:
|
||||||
|
@ -81,6 +112,7 @@ def get_favorite(absolute_id):
|
||||||
|
|
||||||
|
|
||||||
def get_by_absolute_id(absolute_id, model):
|
def get_by_absolute_id(absolute_id, model):
|
||||||
|
''' generalized function to get from a model with a remote_id field '''
|
||||||
# check if it's a remote status
|
# check if it's a remote status
|
||||||
try:
|
try:
|
||||||
return model.objects.get(remote_id=absolute_id)
|
return model.objects.get(remote_id=absolute_id)
|
||||||
|
|
|
@ -9,24 +9,32 @@
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="tabs secondary">
|
<div class="tabs secondary">
|
||||||
<div class="tab active">
|
<div class="tab active" data-id="tab-review-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
Review
|
<a href="{{ book.absolute_id }}/review" onclick="tabChange(event)">Review</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
<div class="tab" data-id="tab-comment-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
Comment
|
<a href="{{ book.absolute_id }}/comment" onclick="tabChange(event)">Comment</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
<div class="tab" data-id="tab-quote-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
Quote
|
Quote
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="book-preview">
|
<div class="book-preview">
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
{% include 'snippets/book_cover.html' with book=book %}
|
||||||
<form class="review-form" name="review" action="/review/" method="post">
|
<form class="tab-option-{{ book.id }} review-form" name="review" action="/review/" method="post" id="tab-review-{{ book.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{# TODO: this shouldn't use the openlibrary key #}
|
{# todo: this shouldn't use the openlibrary key #}
|
||||||
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
||||||
{{ review_form.as_p }}
|
{{ review_form.as_p }}
|
||||||
<button type="submit">Post review</button>
|
<button type="submit">post review</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="hidden tab-option-{{ book.id }} review-form" name="comment" action="/comment/" method="post" id="tab-comment-{{ book.id }}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{# todo: this shouldn't use the openlibrary key #}
|
||||||
|
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
||||||
|
{{ comment_form.as_p }}
|
||||||
|
<button type="submit">post comment</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
<div class="interaction">
|
<div class="interaction">
|
||||||
|
|
||||||
<form name="comment" action="/comment" method="post" onsubmit="return comment(e_">
|
<form name="reply" action="/reply" method="post" onsubmit="return reply(event)">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="parent" value="{{ activity.id }}"></input>
|
<input type="hidden" name="parent" value="{{ activity.id }}"></input>
|
||||||
<textarea name="content" placeholder="Leave a comment..." id="id_content" required="true"></textarea>
|
<textarea name="content" placeholder="Leave a comment..." id="id_content" required="true"></textarea>
|
||||||
<button type="submit" class="comment">
|
<button type="submit">
|
||||||
<span class="icon icon-comment">
|
<span class="icon icon-comment">
|
||||||
<span class="hidden-text">Comment</span>
|
<span class="hidden-text">Comment</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
{{ status.content | safe }}
|
{{ status.content | safe }}
|
||||||
{% elif status.status_type == 'Review' %}
|
{% elif status.status_type == 'Review' %}
|
||||||
reviewed {{ status.book.title }}
|
reviewed {{ status.book.title }}
|
||||||
|
{% elif status.status_type == 'Comment' %}
|
||||||
|
commented on {{ status.book.title }}
|
||||||
{% elif status.reply_parent %}
|
{% elif status.reply_parent %}
|
||||||
{% with parent_status=status|parent %}
|
{% with parent_status=status|parent %}
|
||||||
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.absolute_id }}">{{ parent_status.status_type|lower }}</a>
|
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.absolute_id }}">{{ parent_status.status_type|lower }}</a>
|
||||||
|
|
|
@ -10,7 +10,7 @@ username_regex = r'(?P<username>[\w@\-_\.]+)'
|
||||||
localname_regex = r'(?P<username>[\w\-_]+)'
|
localname_regex = r'(?P<username>[\w\-_]+)'
|
||||||
user_path = r'^user/%s' % username_regex
|
user_path = r'^user/%s' % username_regex
|
||||||
local_user_path = r'^user/%s' % localname_regex
|
local_user_path = r'^user/%s' % localname_regex
|
||||||
status_path = r'%s/(status|review)/(?P<status_id>\d+)' % local_user_path
|
status_path = r'%s/(status|review|comment)/(?P<status_id>\d+)' % local_user_path
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
@ -65,9 +65,10 @@ urlpatterns = [
|
||||||
re_path(r'^user-login/?$', actions.user_login),
|
re_path(r'^user-login/?$', actions.user_login),
|
||||||
re_path(r'^register/?$', actions.register),
|
re_path(r'^register/?$', actions.register),
|
||||||
re_path(r'^review/?$', actions.review),
|
re_path(r'^review/?$', actions.review),
|
||||||
|
re_path(r'^comment/?$', actions.comment),
|
||||||
re_path(r'^tag/?$', actions.tag),
|
re_path(r'^tag/?$', actions.tag),
|
||||||
re_path(r'^untag/?$', actions.untag),
|
re_path(r'^untag/?$', actions.untag),
|
||||||
re_path(r'^comment/?$', actions.comment),
|
re_path(r'^reply/?$', actions.reply),
|
||||||
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'^shelve/?$', actions.shelve),
|
re_path(r'^shelve/?$', actions.shelve),
|
||||||
|
|
|
@ -100,7 +100,7 @@ def shelve(request):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def review(request):
|
def review(request):
|
||||||
''' create a book review note '''
|
''' create a book review '''
|
||||||
form = forms.ReviewForm(request.POST)
|
form = forms.ReviewForm(request.POST)
|
||||||
book_identifier = request.POST.get('book')
|
book_identifier = request.POST.get('book')
|
||||||
# TODO: better failure behavior
|
# TODO: better failure behavior
|
||||||
|
@ -116,6 +116,23 @@ def review(request):
|
||||||
return redirect('/book/%s' % book_identifier)
|
return redirect('/book/%s' % book_identifier)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def comment(request):
|
||||||
|
''' create a book comment '''
|
||||||
|
form = forms.CommentForm(request.POST)
|
||||||
|
book_identifier = request.POST.get('book')
|
||||||
|
# TODO: better failure behavior
|
||||||
|
if not form.is_valid():
|
||||||
|
return redirect('/book/%s' % book_identifier)
|
||||||
|
|
||||||
|
# TODO: validation, htmlification
|
||||||
|
name = form.data.get('name')
|
||||||
|
content = form.data.get('content')
|
||||||
|
|
||||||
|
outgoing.handle_comment(request.user, book_identifier, name, content)
|
||||||
|
return redirect('/book/%s' % book_identifier)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def tag(request):
|
def tag(request):
|
||||||
''' tag a book '''
|
''' tag a book '''
|
||||||
|
@ -139,15 +156,15 @@ def untag(request):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def comment(request):
|
def reply(request):
|
||||||
''' respond to a book review '''
|
''' respond to a book review '''
|
||||||
form = forms.CommentForm(request.POST)
|
form = forms.ReplyForm(request.POST)
|
||||||
# this is a bit of a formality, the form is just one text field
|
# this is a bit of a formality, the form is just one text field
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
parent_id = request.POST['parent']
|
parent_id = request.POST['parent']
|
||||||
parent = models.Status.objects.get(id=parent_id)
|
parent = models.Status.objects.get(id=parent_id)
|
||||||
outgoing.handle_comment(request.user, parent, form.data['content'])
|
outgoing.handle_reply(request.user, parent, form.data['content'])
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ def home_tab(request, tab):
|
||||||
],
|
],
|
||||||
'active_tab': tab,
|
'active_tab': tab,
|
||||||
'review_form': forms.ReviewForm(),
|
'review_form': forms.ReviewForm(),
|
||||||
|
'comment_form': forms.CommentForm(),
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, 'feed.html', data)
|
return TemplateResponse(request, 'feed.html', data)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue