Repository: zulip/zulip

Issue:
Make our `text_output` markdown integration do something reasonable with block quotes
Right now, if a user sends a block-quoted message to another user and it ends up in a push notification, one can end up with what would read in the webapp as
```
> something
your reply
```

looking like "something\n your reply" in the `text_output`, which is super confusing, since it makes it look like you said "something", when actually you're replying to it.

What we should do is translate the HTML `<blockquote>something</blockquote>` tag that we generate in Zulip into a `> something\n`, instead of "something".  Or if we can't get that working in a way that feels good because of issues around newlines, just hide the `<blockquote>` content entirely.

(This is the correct way to fix https://github.com/zulip/zulip-mobile/issues/2419).

@HarshitOnGitHub this seems like a good quick issue for you, given your expertise with that feature :).


Discussion:
@zulipbot claim

Resolving patch:
diff --git a/zerver/lib/push_notifications.py b/zerver/lib/push_notifications.py
--- a/zerver/lib/push_notifications.py
+++ b/zerver/lib/push_notifications.py
@@ -445,13 +445,24 @@ def get_text(elem: LH.HtmlElement) -> Text:
         # Handles realm emojis, avatars etc.
         if elem.tag == "img":
             return elem.get("alt", "")
+        if elem.tag == 'blockquote':
+            return ''  # To avoid empty line before quote text
+        return elem.text or ''
 
-        return elem.text or ""
+    def format_as_quote(quote_text: Text) -> Text:
+        quote_text_list = filter(None, quote_text.split('\n'))  # Remove empty lines
+        quote_text = '\n'.join(map(lambda x: "> "+x, quote_text_list))
+        quote_text += '\n'
+        return quote_text
 
     def process(elem: LH.HtmlElement) -> Text:
         plain_text = get_text(elem)
+        sub_text = ''
         for child in elem:
-            plain_text += process(child)
+            sub_text += process(child)
+        if elem.tag == 'blockquote':
+            sub_text = format_as_quote(sub_text)
+        plain_text += sub_text
         plain_text += elem.tail or ""
         return plain_text
 


Test patch:
diff --git a/zerver/tests/fixtures/markdown_test_cases.json b/zerver/tests/fixtures/markdown_test_cases.json
--- a/zerver/tests/fixtures/markdown_test_cases.json
+++ b/zerver/tests/fixtures/markdown_test_cases.json
@@ -52,20 +52,20 @@
       "name": "fenced_quote",
       "input": "Hamlet said:\n~~~ quote\nTo be or **not** to be.\n\nThat is the question\n~~~",
       "expected_output": "<p>Hamlet said:</p>\n<blockquote>\n<p>To be or <strong>not</strong> to be.</p>\n<p>That is the question</p>\n</blockquote>",
-      "text_content": "Hamlet said:\n\nTo be or not to be.\nThat is the question\n"
+      "text_content": "Hamlet said:\n> To be or not to be.\n> That is the question\n"
     },
     {
       "name": "fenced_nested_quote",
       "input": "Hamlet said:\n~~~ quote\nPolonius said:\n> This above all: to thine ownself be true,\nAnd it must follow, as the night the day,\nThou canst not then be false to any man.\n\nWhat good advice!\n~~~",
       "expected_output": "<p>Hamlet said:</p>\n<blockquote>\n<p>Polonius said:</p>\n<blockquote>\n<p>This above all: to thine ownself be true,<br>\nAnd it must follow, as the night the day,<br>\nThou canst not then be false to any man.</p>\n</blockquote>\n<p>What good advice!</p>\n</blockquote>",
-      "text_content": "Hamlet said:\n\nPolonius said:\n\nThis above all: to thine ownself be true,\nAnd it must follow, as the night the day,\nThou canst not then be false to any man.\n\nWhat good advice!\n"
+      "text_content": "Hamlet said:\n> Polonius said:\n> > This above all: to thine ownself be true,\n> > And it must follow, as the night the day,\n> > Thou canst not then be false to any man.\n> What good advice!\n"
     },
     {
       "name": "complexly_nested_quote",
       "input": "I heard about this second hand...\n~~~ quote\n\nHe said:\n~~~ quote\nThe customer is complaining.\n\nThey looked at this code:\n``` \ndef hello(): print 'hello\n```\nThey would prefer:\n~~~\ndef hello()\n  puts 'hello'\nend\n~~~\n\nPlease advise.\n~~~\n\nShe said:\n~~~ quote\nJust send them this:\n```\necho \"hello\n\"\n```\n~~~",
       "expected_output": "<p>I heard about this second hand...</p>\n<blockquote>\n<p>He said:</p>\n<blockquote>\n<p>The customer is complaining.</p>\n<p>They looked at this code:</p>\n<div class=\"codehilite\"><pre><span></span>def hello(): print &#39;hello\n</pre></div>\n\n\n<p>They would prefer:</p>\n</blockquote>\n<p>def hello()<br>\n  puts 'hello'<br>\nend</p>\n</blockquote>\n<p>Please advise.</p>\n<div class=\"codehilite\"><pre><span></span>She said:\n~~~ quote\nJust send them this:\n```\necho &quot;hello\n&quot;\n```\n</pre></div>",
       "marked_expected_output": "<p>I heard about this second hand...</p>\n<blockquote>\n<p>He said:</p>\n<blockquote>\n<p>The customer is complaining.</p>\n<p>They looked at this code:</p>\n<div class=\"codehilite\"><pre><span></span>def hello(): print &#39;hello\n</pre></div>\n\n\n<p>They would prefer:</p>\n</blockquote>\n<p>def hello()<br>\n  puts &#39;hello&#39;<br>\nend</p>\n</blockquote>\n<p>Please advise.</p>\n<div class=\"codehilite\"><pre><span></span>\nShe said:\n~~~ quote\nJust send them this:\n```\necho &quot;hello\n&quot;\n```\n</pre></div>",
-      "text_content": "I heard about this second hand...\n\nHe said:\n\nThe customer is complaining.\nThey looked at this code:\ndef hello(): print 'hello\n\n\n\nThey would prefer:\n\ndef hello()\n  puts 'hello'\nend\n\nPlease advise.\nShe said:\n~~~ quote\nJust send them this:\n```\necho \"hello\n\"\n```\n"
+      "text_content": "I heard about this second hand...\n> He said:\n> > The customer is complaining.\n> > They looked at this code:\n> > def hello(): print 'hello\n> > They would prefer:\n> def hello()\n>   puts 'hello'\n> end\n\nPlease advise.\nShe said:\n~~~ quote\nJust send them this:\n```\necho \"hello\n\"\n```\n"
     },
     {
       "name": "fenced_quotes_inside_mathblock",
@@ -92,7 +92,7 @@
       "name": "fenced_quote_with_hashtag",
       "input": "```quote\n# line 1\n# line 2\n```",
       "expected_output": "<blockquote>\n<p># line 1<br>\n# line 2</p>\n</blockquote>",
-      "text_content": "\n# line 1\n# line 2\n"
+      "text_content": "> # line 1\n> # line 2\n"
     },
     {
       "name": "dangerous_block",
@@ -285,7 +285,7 @@
       "input": ">Google logo today:\n>https://www.google.com/images/srpr/logo4w.png\n>Kinda boring",
       "expected_output": "<blockquote>\n<p>Google logo today:<br>\n<a href=\"https://www.google.com/images/srpr/logo4w.png\" target=\"_blank\" title=\"https://www.google.com/images/srpr/logo4w.png\">https://www.google.com/images/srpr/logo4w.png</a><br>\nKinda boring</p>\n<div class=\"message_inline_image\"><a href=\"https://www.google.com/images/srpr/logo4w.png\" target=\"_blank\" title=\"https://www.google.com/images/srpr/logo4w.png\"><img src=\"https://www.google.com/images/srpr/logo4w.png\"></a></div></blockquote>",
       "backend_only_rendering": true,
-      "text_content": "\nGoogle logo today:\nhttps:\/\/www.google.com\/images\/srpr\/logo4w.png\nKinda boring\n"
+      "text_content": "> Google logo today:\n> https:\/\/www.google.com\/images\/srpr\/logo4w.png\n> Kinda boring\n"
     },
     {
       "name": "two_inline_images",