forked from mirrors/bookwyrm
Merge pull request #864 from mouse-reeve/delete-and-redraft
Delete and redraft
This commit is contained in:
commit
ea837a3879
14 changed files with 253 additions and 47 deletions
|
@ -1,18 +1,10 @@
|
|||
""" Handle user activity """
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
|
||||
|
||||
def delete_status(status):
|
||||
""" replace the status with a tombstone """
|
||||
status.deleted = True
|
||||
status.deleted_date = timezone.now()
|
||||
status.save()
|
||||
|
||||
|
||||
def create_generated_note(user, content, mention_books=None, privacy="public"):
|
||||
""" a note created by the app about user activity """
|
||||
# sanitize input html
|
||||
|
|
34
bookwyrm/templates/compose.html
Normal file
34
bookwyrm/templates/compose.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
|
||||
{% block title %}{% trans "Compose status" %}{% endblock %}
|
||||
{% block content %}
|
||||
<header class="block content">
|
||||
<h1>{% trans "Compose status" %}</h1>
|
||||
</header>
|
||||
|
||||
{% with 0|uuid as uuid %}
|
||||
<div class="box columns">
|
||||
{% if book %}
|
||||
<div class="column is-one-third">
|
||||
<div class="block">
|
||||
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book %}</a>
|
||||
</div>
|
||||
<h3 class="title is-6">{% include 'snippets/book_titleby.html' with book=book %}</h3>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="column is-two-thirds">
|
||||
{% if draft.reply_parent %}
|
||||
{% include 'snippets/status/status.html' with status=draft.reply_parent no_interact=True %}
|
||||
{% endif %}
|
||||
|
||||
{% if not draft %}
|
||||
{% include 'snippets/create_status.html' %}
|
||||
{% else %}
|
||||
{% include 'snippets/create_status_form.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endblock %}
|
|
@ -12,6 +12,7 @@
|
|||
{% if not suggested_books %}
|
||||
<p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
|
||||
{% else %}
|
||||
{% with active_book=request.GET.book %}
|
||||
<div class="tab-group">
|
||||
<div class="tabs is-small">
|
||||
<ul role="tablist">
|
||||
|
@ -28,8 +29,14 @@
|
|||
<div class="tabs is-small is-toggle">
|
||||
<ul>
|
||||
{% for book in shelf.books %}
|
||||
<li{% if shelf_counter == 1 and forloop.first %} class="is-active"{% endif %}>
|
||||
<a href="#book-{{ book.id }}" id="tab-book-{{ book.id }}" role="tab" aria-label="{{ book.title }}" aria-selected="{% if shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}" aria-controls="book-{{ book.id }}">
|
||||
<li class="{% if active_book == book.id|stringformat:'d' %}is-active{% elif not active_book and shelf_counter == 1 and forloop.first %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?book={{ book.id }}"
|
||||
id="tab-book-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-label="{{ book.title }}"
|
||||
aria-selected="{% if active_book == book.id|stringformat:'d' %}true{% elif shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}"
|
||||
aria-controls="book-{{ book.id }}">
|
||||
{% include 'snippets/book_cover.html' with book=book size="medium" %}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -45,7 +52,13 @@
|
|||
{% for shelf in suggested_books %}
|
||||
{% with shelf_counter=forloop.counter %}
|
||||
{% for book in shelf.books %}
|
||||
<div class="suggested-tabs card" role="tabpanel" id="book-{{ book.id }}"{% if shelf_counter != 1 or not forloop.first %} hidden{% endif %} aria-labelledby="tab-book-{{ book.id }}">
|
||||
<div
|
||||
class="suggested-tabs card"
|
||||
role="tabpanel"
|
||||
id="book-{{ book.id }}"
|
||||
{% if active_book and active_book == book.id|stringformat:'d' %}{% elif not active_book and shelf_counter == 1 and forloop.first %}{% else %} hidden{% endif %}
|
||||
aria-labelledby="tab-book-{{ book.id }}">
|
||||
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
<div>
|
||||
|
@ -66,6 +79,7 @@
|
|||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% if goal %}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
{% load i18n %}
|
||||
<div class="control{% if not parent_status.content_warning %} hidden{% endif %}" id="spoilers-{{ uuid }}">
|
||||
<div class="control{% if not parent_status.content_warning and not draft.content_warning %} hidden{% endif %}" id="spoilers-{{ uuid }}">
|
||||
<label class="is-sr-only" for="id_content_warning-{{ uuid }}">{% trans "Spoiler alert:" %}</label>
|
||||
<input type="text" name="content_warning" maxlength="255" class="input" id="id_content_warning-{{ uuid }}" placeholder="{% trans 'Spoilers ahead!' %}"{% if parent_status.content_warning %} value="{{ parent_status.content_warning }}"{% endif %}>
|
||||
<input
|
||||
type="text"
|
||||
name="content_warning"
|
||||
maxlength="255"
|
||||
class="input"
|
||||
id="id_content_warning-{{ uuid }}"
|
||||
placeholder="{% trans 'Spoilers ahead!' %}"
|
||||
value="{% firstof draft.content_warning parent_status.content_warning '' %}">
|
||||
</div>
|
||||
|
|
|
@ -2,36 +2,62 @@
|
|||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
|
||||
{% with status_type=request.GET.status_type %}
|
||||
<div class="tab-group">
|
||||
<div class="tabs is-boxed" role="tablist">
|
||||
<ul>
|
||||
<li class="is-active">
|
||||
<a href="#review-{{ book.id }}" id="tab-review-{{ book.id }}" role="tab" aria-selected="true" aria-controls="review-{{ book.id }}" data-category="tab-option-{{ book.id }}">{% trans "Review" %}</a>
|
||||
<li class="{% if status_type == 'review' or not status_type %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?status_type=review&book={{ book.id }}"
|
||||
id="tab-review-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-selected="{% if status_type == 'review' or not status_type %}true{% else %}false{% endif %}"
|
||||
aria-controls="review-{{ book.id }}"
|
||||
data-category="tab-option-{{ book.id }}">
|
||||
{% trans "Review" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#comment-{{ book.id}}" id="tab-comment-{{ book.id }}" role="tab" aria-selected="false" aria-controls="comment-{{ book.id}}" data-category="tab-option-{{ book.id }}">{% trans "Comment" %}</a>
|
||||
<li class="{% if status_type == 'comment' %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?status_type=comment&book={{ book.id}}"
|
||||
id="tab-comment-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-selected="{% if status_type == 'comment' %}true{% else %}false{% endif %}"
|
||||
aria-controls="comment-{{ book.id}}"
|
||||
data-category="tab-option-{{ book.id }}">
|
||||
{% trans "Comment" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#quote-{{ book.id }}" id="tab-quote-{{ book.id }}" role="tab" aria-selected="false" aria-controls="quote-{{ book.id }}" data-category="tab-option-{{ book.id }}">{% trans "Quote" %}</a>
|
||||
<li class="{% if status_type == 'quote' %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?status_type=quote&book={{ book.id }}"
|
||||
id="tab-quote-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-selected="{% if status_type == 'quote' %}true{% else %}false{% endif %}"
|
||||
aria-controls="quote-{{ book.id }}"
|
||||
data-category="tab-option-{{ book.id }}">
|
||||
{% trans "Quote" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tab-option-{{ book.id }}" id="review-{{ book.id }}" role="tabpanel" aria-labelledby="tab-review-{{ book.id }}">
|
||||
<div class="tab-option-{{ book.id }}" id="review-{{ book.id }}" role="tabpanel" aria-labelledby="tab-review-{{ book.id }}" {% if status_type and status_type != "review" %}hidden{% endif %}>
|
||||
{% with 0|uuid as uuid %}
|
||||
{% include 'snippets/create_status_form.html' with type='review' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<div class="tab-option-{{ book.id }}" id="comment-{{ book.id }}" role="tabpanel" aria-labelledby="tab-comment-{{ book.id }}" hidden>
|
||||
<div class="tab-option-{{ book.id }}" id="comment-{{ book.id }}" role="tabpanel" aria-labelledby="tab-comment-{{ book.id }}" {% if status_type != "comment" %}hidden{% endif %}>
|
||||
{% with 0|uuid as uuid %}
|
||||
{% include 'snippets/create_status_form.html' with type="comment" placeholder="Some thoughts on '"|add:book.title|add:"'" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<div class="tab-option-{{ book.id }}" id="quote-{{ book.id }}" role="tabpanel" aria-labelledby="tab-quote-{{ book.id }}" hidden>
|
||||
<div class="tab-option-{{ book.id }}" id="quote-{{ book.id }}" role="tabpanel" aria-labelledby="tab-quote-{{ book.id }}" {% if status_type != "quote" %}hidden{% endif %}>
|
||||
{% with 0|uuid as uuid %}
|
||||
{% include 'snippets/create_status_form.html' with type="quotation" placeholder="An excerpt from '"|add:book.title|add:"'" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||
<input type="hidden" name="reply_parent" value="{{ reply_parent.id }}">
|
||||
<input type="hidden" name="reply_parent" value="{% firstof draft.reply_parent.id reply_parent.id %}">
|
||||
{% if type == 'review' %}
|
||||
<div class="control">
|
||||
<label class="label" for="id_name_{{ book.id }}_{{ type }}">{% trans "Title" %}:</label>
|
||||
<input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}_{{ type }}" placeholder="My {{ type }} of '{{ book.title }}'">
|
||||
<input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}_{{ type }}" placeholder="My {{ type }} of '{{ book.title }}'" value="{% firstof draft.name ''%}">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="control">
|
||||
|
@ -28,22 +28,22 @@
|
|||
<fieldset>
|
||||
<legend class="is-sr-only">{% trans "Rating" %}</legend>
|
||||
|
||||
{% include 'snippets/form_rate_stars.html' with book=book type=type|default:'summary' %}
|
||||
{% include 'snippets/form_rate_stars.html' with book=book type=type|default:'summary' default_rating=draft.rating %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
{% if type == 'quotation' %}
|
||||
<textarea name="quote" class="textarea" id="id_quote_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required></textarea>
|
||||
<textarea name="quote" class="textarea" id="id_quote_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required>{{ draft.quote|default:'' }}</textarea>
|
||||
{% else %}
|
||||
{% include 'snippets/content_warning_field.html' with parent_status=status %}
|
||||
<textarea name="content" class="textarea" id="id_content_{{ type }}-{{ book.id }}{{reply_parent.id}}" placeholder="{{ placeholder }}" {% if type == 'reply' %} aria-label="Reply"{% endif %} required>{% if reply_parent %}{{ reply_parent|mentions:request.user }}{% endif %}{% if mentions %}@{{ mentions|username }} {% endif %}</textarea>
|
||||
<textarea name="content" class="textarea" id="id_content_{{ type }}-{{ book.id }}{{reply_parent.id}}" placeholder="{{ placeholder }}" {% if type == 'reply' %} aria-label="Reply"{% endif %} required>{% if reply_parent %}{{ reply_parent|mentions:request.user }}{% endif %}{% if mentions %}@{{ mentions|username }} {% endif %}{{ draft.content|default:'' }}</textarea>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if type == 'quotation' %}
|
||||
<div class="control">
|
||||
<label class="label" for="id_content_quote-{{ book.id }}">{% trans "Comment" %}:</label>
|
||||
{% include 'snippets/content_warning_field.html' with parent_status=status %}
|
||||
<textarea name="content" class="textarea is-small" id="id_content_quote-{{ book.id }}"></textarea>
|
||||
<textarea name="content" class="textarea is-small" id="id_content_quote-{{ book.id }}">{{ draft.content|default:'' }}</textarea>
|
||||
</div>
|
||||
{% elif type == 'comment' %}
|
||||
<div class="control">
|
||||
|
@ -56,12 +56,12 @@
|
|||
<label class="label" for="progress-{{ uuid }}">{% trans "Progress:" %}</label>
|
||||
<div class="field has-addons mb-0">
|
||||
<div class="control">
|
||||
<input aria-label="{% if readthrough.progress_mode == 'PG' %}Current page{% else %}Percent read{% endif %}" class="input" type="number" min="0" name="progress" size="3" value="{{ readthrough.progress|default:'' }}" id="progress-{{ uuid }}">
|
||||
<input aria-label="{% if draft.progress_mode == 'PG' or readthrough.progress_mode == 'PG' %}Current page{% else %}Percent read{% endif %}" class="input" type="number" min="0" name="progress" size="3" value="{% firstof draft.progress readthrough.progress '' %}" id="progress-{{ uuid }}">
|
||||
</div>
|
||||
<div class="control select">
|
||||
<select name="progress_mode" aria-label="Progress mode">
|
||||
<option value="PG" {% if readthrough.progress_mode == 'PG' %}selected{% endif %}>{% trans "pages" %}</option>
|
||||
<option value="PCT" {% if readthrough.progress_mode == 'PCT' %}selected{% endif %}>{% trans "percent" %}</option>
|
||||
<option value="PG" {% if draft.progress_mode == 'PG' or readthrough.progress_mode == 'PG' %}selected{% endif %}>{% trans "pages" %}</option>
|
||||
<option value="PCT" {% if draft.progress_mode == 'PCT' or readthrough.progress_mode == 'PCT' %}selected{% endif %}>{% trans "percent" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,20 +73,25 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<input type="checkbox" class="hidden" name="sensitive" id="id_show_spoilers-{{ uuid }}" {% if status.content_warning %}checked{% endif %} aria-hidden="true">
|
||||
<input type="checkbox" class="hidden" name="sensitive" id="id_show_spoilers-{{ uuid }}" {% if draft.content_warning or status.content_warning %}checked{% endif %} aria-hidden="true">
|
||||
{# bottom bar #}
|
||||
<div class="columns pt-1">
|
||||
<div class="field has-addons column">
|
||||
<div class="control">
|
||||
{% trans "Include spoiler alert" as button_text %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with text=button_text icon="warning is-size-4" controls_text="spoilers" controls_uid=uuid focus="id_content_warning" checkbox="id_show_spoilers" class="toggle-button" pressed=status.content_warning %}
|
||||
{% firstof draft.content_warning status.content_warning as pressed %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with text=button_text icon="warning is-size-4" controls_text="spoilers" controls_uid=uuid focus="id_content_warning" checkbox="id_show_spoilers" class="toggle-button" pressed=pressed %}
|
||||
</div>
|
||||
<div class="control">
|
||||
{% if type == 'direct' %}
|
||||
<input type="hidden" name="privacy" value="direct">
|
||||
<button type="button" class="button" aria-label="Privacy" disabled>{% trans "Private" %}</button>
|
||||
{% else %}
|
||||
{% if draft %}
|
||||
{% include 'snippets/privacy_select.html' with current=draft.privacy %}
|
||||
{% else %}
|
||||
{% include 'snippets/privacy_select.html' with current=reply_parent.privacy %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
type="radio"
|
||||
name="rating"
|
||||
value="0"
|
||||
{% if book|user_rating:user == 0 %}checked{% endif %}
|
||||
{% if default_rating == 0 or not default_rating %}checked{% endif %}
|
||||
>
|
||||
|
||||
<label class="is-sr-only" for="{{ type|slugify }}-{{ book.id }}-no-rating">
|
||||
|
@ -27,13 +27,13 @@
|
|||
type="radio"
|
||||
name="rating"
|
||||
value="{{ forloop.counter }}"
|
||||
{% if book|user_rating:user == forloop.counter %}checked{% endif %}
|
||||
{% if default_rating == forloop.counter %}checked{% endif %}
|
||||
/>
|
||||
|
||||
<label
|
||||
class="
|
||||
icon
|
||||
{% if forloop.counter <= book|user_rating:user %}
|
||||
{% if forloop.counter <= default_rating %}
|
||||
icon-star-full
|
||||
{% else %}
|
||||
icon-star-empty
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<input type="hidden" name="privacy" value="public">
|
||||
|
||||
{% include 'snippets/form_rate_stars.html' with book=book classes='mb-1 has-text-warning-dark' %}
|
||||
{% include 'snippets/form_rate_stars.html' with book=book classes='mb-1 has-text-warning-dark' default_rating=book|user_rating:request.user %}
|
||||
|
||||
<div class="field has-addons hidden">
|
||||
<div class="control">
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
{% trans "Delete status" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{% elif no_interact %}
|
||||
{# nothing here #}
|
||||
{% elif request.user.is_authenticated %}
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% if status.status_type != 'GeneratedNote' and status.status_type != 'Rating' %}
|
||||
<li role="menuitem">
|
||||
<form class="dropdown-item pt-0 pb-0" name="delete-{{status.id}}" action="{% url 'redraft' status.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="button is-danger is-light is-fullwidth is-small" type="submit">
|
||||
{% trans "Delete & re-draft" %}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{# things you can do to other people's statuses #}
|
||||
<li role="menuitem">
|
||||
|
|
|
@ -40,6 +40,7 @@ class StatusViews(TestCase):
|
|||
remote_id="https://example.com/book/1",
|
||||
parent_work=work,
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_handle_status(self, _):
|
||||
""" create a status """
|
||||
|
@ -166,6 +167,61 @@ class StatusViews(TestCase):
|
|||
self.assertFalse(self.remote_user in reply.mention_users.all())
|
||||
self.assertTrue(self.local_user in reply.mention_users.all())
|
||||
|
||||
def test_delete_and_redraft(self, _):
|
||||
""" delete and re-draft a status """
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
request.user = self.local_user
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
status = models.Comment.objects.create(
|
||||
content="hi", book=self.book, user=self.local_user
|
||||
)
|
||||
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.remove_status") as mock:
|
||||
result = view(request, status.id)
|
||||
self.assertTrue(mock.called)
|
||||
result.render()
|
||||
|
||||
# make sure it was deleted
|
||||
status.refresh_from_db()
|
||||
self.assertTrue(status.deleted)
|
||||
|
||||
def test_delete_and_redraft_invalid_status_type_rating(self, _):
|
||||
""" you can't redraft generated statuses """
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
request.user = self.local_user
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
status = models.ReviewRating.objects.create(
|
||||
book=self.book, rating=2.0, user=self.local_user
|
||||
)
|
||||
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.remove_status") as mock:
|
||||
result = view(request, status.id)
|
||||
self.assertFalse(mock.called)
|
||||
self.assertEqual(result.status_code, 400)
|
||||
|
||||
status.refresh_from_db()
|
||||
self.assertFalse(status.deleted)
|
||||
|
||||
def test_delete_and_redraft_invalid_status_type_generated_note(self, _):
|
||||
""" you can't redraft generated statuses """
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
request.user = self.local_user
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
status = models.GeneratedNote.objects.create(
|
||||
content="hi", user=self.local_user
|
||||
)
|
||||
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.remove_status") as mock:
|
||||
result = view(request, status.id)
|
||||
self.assertFalse(mock.called)
|
||||
self.assertEqual(result.status_code, 400)
|
||||
|
||||
status.refresh_from_db()
|
||||
self.assertFalse(status.deleted)
|
||||
|
||||
def test_find_mentions(self, _):
|
||||
""" detect and look up @ mentions of users """
|
||||
user = models.User.objects.create_user(
|
||||
|
|
|
@ -197,11 +197,31 @@ urlpatterns = [
|
|||
re_path(r"^block/(?P<user_id>\d+)/?$", views.Block.as_view()),
|
||||
re_path(r"^unblock/(?P<user_id>\d+)/?$", views.unblock),
|
||||
# statuses
|
||||
re_path(r"%s(.json)?/?$" % status_path, views.Status.as_view()),
|
||||
re_path(r"%s/activity/?$" % status_path, views.Status.as_view()),
|
||||
re_path(r"%s/replies(.json)?/?$" % status_path, views.Replies.as_view()),
|
||||
re_path(r"^post/(?P<status_type>\w+)/?$", views.CreateStatus.as_view()),
|
||||
re_path(r"^delete-status/(?P<status_id>\d+)/?$", views.DeleteStatus.as_view()),
|
||||
re_path(r"%s(.json)?/?$" % status_path, views.Status.as_view(), name="status"),
|
||||
re_path(r"%s/activity/?$" % status_path, views.Status.as_view(), name="status"),
|
||||
re_path(
|
||||
r"%s/replies(.json)?/?$" % status_path, views.Replies.as_view(), name="replies"
|
||||
),
|
||||
re_path(
|
||||
r"^post/?$",
|
||||
views.CreateStatus.as_view(),
|
||||
name="create-status",
|
||||
),
|
||||
re_path(
|
||||
r"^post/(?P<status_type>\w+)/?$",
|
||||
views.CreateStatus.as_view(),
|
||||
name="create-status",
|
||||
),
|
||||
re_path(
|
||||
r"^delete-status/(?P<status_id>\d+)/?$",
|
||||
views.DeleteStatus.as_view(),
|
||||
name="delete-status",
|
||||
),
|
||||
re_path(
|
||||
r"^redraft-status/(?P<status_id>\d+)/?$",
|
||||
views.DeleteAndRedraft.as_view(),
|
||||
name="redraft",
|
||||
),
|
||||
# interact
|
||||
re_path(r"^favorite/(?P<status_id>\d+)/?$", views.Favorite.as_view()),
|
||||
re_path(r"^unfavorite/(?P<status_id>\d+)/?$", views.Unfavorite.as_view()),
|
||||
|
|
|
@ -31,7 +31,7 @@ from .shelf import Shelf
|
|||
from .shelf import create_shelf, delete_shelf
|
||||
from .shelf import shelve, unshelve
|
||||
from .site import Site
|
||||
from .status import CreateStatus, DeleteStatus
|
||||
from .status import CreateStatus, DeleteStatus, DeleteAndRedraft
|
||||
from .tag import Tag, AddTag, RemoveTag
|
||||
from .updates import get_notification_count, get_unread_status_count
|
||||
from .user import User, EditUser, Followers, Following
|
||||
|
|
|
@ -3,6 +3,7 @@ import re
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from markdown import markdown
|
||||
|
@ -10,7 +11,6 @@ from markdown import markdown
|
|||
from bookwyrm import forms, models
|
||||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from bookwyrm.status import delete_status
|
||||
from bookwyrm.utils import regex
|
||||
from .helpers import handle_remote_webfinger
|
||||
from .reading import edit_readthrough
|
||||
|
@ -21,6 +21,12 @@ from .reading import edit_readthrough
|
|||
class CreateStatus(View):
|
||||
""" the view for *posting* """
|
||||
|
||||
def get(self, request):
|
||||
""" compose view (used for delete-and-redraft """
|
||||
book = get_object_or_404(models.Edition, id=request.GET.get("book"))
|
||||
data = {"book": book}
|
||||
return TemplateResponse(request, "compose.html", data)
|
||||
|
||||
def post(self, request, status_type):
|
||||
""" create status of whatever type """
|
||||
status_type = status_type[0].upper() + status_type[1:]
|
||||
|
@ -69,9 +75,10 @@ class CreateStatus(View):
|
|||
# update a readthorugh, if needed
|
||||
edit_readthrough(request)
|
||||
|
||||
return redirect(request.headers.get("Referer", "/"))
|
||||
return redirect("/")
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class DeleteStatus(View):
|
||||
""" tombstone that bad boy """
|
||||
|
||||
|
@ -84,10 +91,44 @@ class DeleteStatus(View):
|
|||
return HttpResponseBadRequest()
|
||||
|
||||
# perform deletion
|
||||
delete_status(status)
|
||||
status.delete()
|
||||
return redirect(request.headers.get("Referer", "/"))
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class DeleteAndRedraft(View):
|
||||
""" delete a status but let the user re-create it """
|
||||
|
||||
def post(self, request, status_id):
|
||||
""" delete and tombstone a status """
|
||||
status = get_object_or_404(
|
||||
models.Status.objects.select_subclasses(), id=status_id
|
||||
)
|
||||
if isinstance(status, (models.GeneratedNote, models.ReviewRating)):
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
# don't let people redraft other people's statuses
|
||||
if status.user != request.user:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
status_type = status.status_type.lower()
|
||||
if status.reply_parent:
|
||||
status_type = "reply"
|
||||
|
||||
data = {
|
||||
"draft": status,
|
||||
"type": status_type,
|
||||
}
|
||||
if hasattr(status, "book"):
|
||||
data["book"] = status.book
|
||||
elif status.mention_books:
|
||||
data["book"] = status.mention_books.first()
|
||||
|
||||
# perform deletion
|
||||
status.delete()
|
||||
return TemplateResponse(request, "compose.html", data)
|
||||
|
||||
|
||||
def find_mentions(content):
|
||||
""" detect @mentions in raw status content """
|
||||
if not content:
|
||||
|
|
Loading…
Reference in a new issue