Repository: bookwyrm-social/bookwyrm

Issue:
When Redrafting a Quote Post, HTML Appears in the Text Inputs
**Describe the bug**
When deleting and re-drafting a Quote post (maybe the other types. didn't test), HTML markup appears in the input.
The text will be surrounded by `<p>` tags, @s will be replaces with `<a>` tags, underscored text with `<em>`.

**To Reproduce**
Steps to reproduce the behavior:
1. Create a Quote post, optionally @-ing another user or with underscores for emphasis
2. Click on `More Options`
3. Click on `Delete & re-draft`
4. See HTML markup in the input fields

**Expected behavior**
The text in the quote and comment inputs matches the text I entered originally.

**Desktop (please complete the following information):**
    - OS: macOS Big Sur
    - Browser Firefox
    - Version 89.0b15



Discussion:
Right now, markdown is converted to html pre-save. If it was converted to html pre-render, this problem would be fixed

Resolving patch:
diff --git a/bookwyrm/migrations/0110_auto_20211015_1734.py b/bookwyrm/migrations/0110_auto_20211015_1734.py
new file mode 100644
--- /dev/null
+++ b/bookwyrm/migrations/0110_auto_20211015_1734.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.5 on 2021-10-15 17:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("bookwyrm", "0109_status_edited_date"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="quotation",
+            name="raw_quote",
+            field=models.TextField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name="status",
+            name="raw_content",
+            field=models.TextField(blank=True, null=True),
+        ),
+    ]
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -31,6 +31,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
         "User", on_delete=models.PROTECT, activitypub_field="attributedTo"
     )
     content = fields.HtmlField(blank=True, null=True)
+    raw_content = models.TextField(blank=True, null=True)
     mention_users = fields.TagField("User", related_name="mention_user")
     mention_books = fields.TagField("Edition", related_name="mention_book")
     local = models.BooleanField(default=True)
@@ -295,6 +296,7 @@ class Quotation(BookStatus):
     """like a review but without a rating and transient"""
 
     quote = fields.HtmlField()
+    raw_quote = models.TextField(blank=True, null=True)
     position = models.IntegerField(
         validators=[MinValueValidator(0)], null=True, blank=True
     )
diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py
--- a/bookwyrm/views/status.py
+++ b/bookwyrm/views/status.py
@@ -79,6 +79,11 @@ def post(self, request, status_type, existing_status_id=None):
             return redirect(request.headers.get("Referer", "/"))
 
         status = form.save(commit=False)
+        # save the plain, unformatted version of the status for future editing
+        status.raw_content = status.content
+        if hasattr(status, "quote"):
+            status.raw_quote = status.quote
+
         if not status.sensitive and status.content_warning:
             # the cw text field remains populated when you click "remove"
             status.content_warning = None


Test patch:
diff --git a/bookwyrm/tests/views/test_status.py b/bookwyrm/tests/views/test_status.py
--- a/bookwyrm/tests/views/test_status.py
+++ b/bookwyrm/tests/views/test_status.py
@@ -51,7 +51,7 @@ def setUp(self):
         )
         models.SiteSettings.objects.create()
 
-    def test_handle_status(self, *_):
+    def test_create_status_comment(self, *_):
         """create a status"""
         view = views.CreateStatus.as_view()
         form = forms.CommentForm(
@@ -68,12 +68,13 @@ def test_handle_status(self, *_):
         view(request, "comment")
 
         status = models.Comment.objects.get()
+        self.assertEqual(status.raw_content, "hi")
         self.assertEqual(status.content, "<p>hi</p>")
         self.assertEqual(status.user, self.local_user)
         self.assertEqual(status.book, self.book)
         self.assertIsNone(status.edited_date)
 
-    def test_handle_status_reply(self, *_):
+    def test_create_status_reply(self, *_):
         """create a status in reply to an existing status"""
         view = views.CreateStatus.as_view()
         user = models.User.objects.create_user(
@@ -100,7 +101,7 @@ def test_handle_status_reply(self, *_):
         self.assertEqual(status.user, user)
         self.assertEqual(models.Notification.objects.get().user, self.local_user)
 
-    def test_handle_status_mentions(self, *_):
+    def test_create_status_mentions(self, *_):
         """@mention a user in a post"""
         view = views.CreateStatus.as_view()
         user = models.User.objects.create_user(
@@ -130,7 +131,7 @@ def test_handle_status_mentions(self, *_):
             status.content, f'<p>hi <a href="{user.remote_id}">@rat</a></p>'
         )
 
-    def test_handle_status_reply_with_mentions(self, *_):
+    def test_create_status_reply_with_mentions(self, *_):
         """reply to a post with an @mention'ed user"""
         view = views.CreateStatus.as_view()
         user = models.User.objects.create_user(
@@ -297,7 +298,7 @@ def test_to_markdown_link(self, *_):
         result = views.status.to_markdown(text)
         self.assertEqual(result, '<p><a href="http://fish.com">hi</a> ' "is rad</p>")
 
-    def test_handle_delete_status(self, mock, *_):
+    def test_delete_status(self, mock, *_):
         """marks a status as deleted"""
         view = views.DeleteStatus.as_view()
         with patch("bookwyrm.activitystreams.add_status_task.delay"):
@@ -315,7 +316,7 @@ def test_handle_delete_status(self, mock, *_):
         status.refresh_from_db()
         self.assertTrue(status.deleted)
 
-    def test_handle_delete_status_permission_denied(self, *_):
+    def test_delete_status_permission_denied(self, *_):
         """marks a status as deleted"""
         view = views.DeleteStatus.as_view()
         with patch("bookwyrm.activitystreams.add_status_task.delay"):
@@ -330,7 +331,7 @@ def test_handle_delete_status_permission_denied(self, *_):
         status.refresh_from_db()
         self.assertFalse(status.deleted)
 
-    def test_handle_delete_status_moderator(self, mock, *_):
+    def test_delete_status_moderator(self, mock, *_):
         """marks a status as deleted"""
         view = views.DeleteStatus.as_view()
         with patch("bookwyrm.activitystreams.add_status_task.delay"):
@@ -378,7 +379,7 @@ def test_edit_status_get_reply(self, *_):
         validate_html(result.render())
         self.assertEqual(result.status_code, 200)
 
-    def test_create_status_edit_success(self, mock, *_):
+    def test_edit_status_success(self, mock, *_):
         """update an existing status"""
         status = models.Status.objects.create(content="status", user=self.local_user)
         self.assertIsNone(status.edited_date)
@@ -403,7 +404,7 @@ def test_create_status_edit_success(self, mock, *_):
         self.assertEqual(status.content, "<p>hi</p>")
         self.assertIsNotNone(status.edited_date)
 
-    def test_create_status_edit_permission_denied(self, *_):
+    def test_edit_status_permission_denied(self, *_):
         """update an existing status"""
         status = models.Status.objects.create(content="status", user=self.local_user)
         view = views.CreateStatus.as_view()