From f01492ae4899aa6219f29a127da2e749ebf64c30 Mon Sep 17 00:00:00 2001 From: Blackle Morisanchetto Date: Wed, 31 Aug 2022 11:40:11 -0400 Subject: [PATCH] [bugfix] Use custom blackfriday renderer to only add mention/hashtag links in normal text (#787) * Use custom blackfriday renderer to only add mention/hashtag links in normal text * Add additional markdown tests --- internal/text/markdown.go | 49 +++++++++++++++++++++++++++++----- internal/text/markdown_test.go | 18 +++++++++++++ 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/internal/text/markdown.go b/internal/text/markdown.go index 13fb93378..1382cbf61 100644 --- a/internal/text/markdown.go +++ b/internal/text/markdown.go @@ -19,7 +19,9 @@ package text import ( + "bytes" "context" + "io" "github.com/russross/blackfriday/v2" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -33,18 +35,51 @@ var ( m *minify.M ) +type renderer struct { + f *formatter + ctx context.Context + mentions []*gtsmodel.Mention + tags []*gtsmodel.Tag + blackfriday.HTMLRenderer +} + +func (r *renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if node.Type == blackfriday.Text { + // call RenderNode to do the html escaping + var buff bytes.Buffer + status := r.HTMLRenderer.RenderNode(&buff, node, entering) + + html := buff.String() + html = r.f.ReplaceTags(r.ctx, html, r.tags) + html = r.f.ReplaceMentions(r.ctx, html, r.mentions) + + // we don't have much recourse if this fails + _, err := io.WriteString(w, html) + if err != nil { + log.Errorf("error outputting markdown text: %s", err) + } + return status + } + return r.HTMLRenderer.RenderNode(w, node, entering) +} + func (f *formatter) FromMarkdown(ctx context.Context, md string, mentions []*gtsmodel.Mention, tags []*gtsmodel.Tag) string { - // format tags nicely - content := f.ReplaceTags(ctx, md, tags) - // format mentions nicely - content = f.ReplaceMentions(ctx, content, mentions) + renderer := &renderer{ + f: f, + ctx: ctx, + mentions: mentions, + tags: tags, + HTMLRenderer: *blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ + Flags: blackfriday.CommonHTMLFlags, + }), + } - // parse markdown - contentBytes := blackfriday.Run([]byte(content), blackfriday.WithExtensions(bfExtensions)) + // parse markdown, use custom renderer to add hashtag/mention links + contentBytes := blackfriday.Run([]byte(md), blackfriday.WithExtensions(bfExtensions), blackfriday.WithRenderer(renderer)) // clean anything dangerous out of it - content = SanitizeHTML(string(contentBytes)) + content := SanitizeHTML(string(contentBytes)) if m == nil { m = minify.New() diff --git a/internal/text/markdown_test.go b/internal/text/markdown_test.go index 3e156f43e..4c9483c7e 100644 --- a/internal/text/markdown_test.go +++ b/internal/text/markdown_test.go @@ -65,6 +65,10 @@ const ( mdWithFootnoteExpected = "

fox mulder,fbi.1


  1. federated bureau of investigation
" mdWithBlockQuote = "get ready, there's a block quote coming:\n\n>line1\n>line2\n>\n>line3\n\n" mdWithBlockQuoteExpected = "

get ready, there’s a block quote coming:

line1
line2

line3

" + mdHashtagAndCodeBlock = "#Hashtag\n\n```\n#Hashtag\n```" + mdHashtagAndCodeBlockExpected = "

#Hashtag

#Hashtag\n
" + mdMentionAndCodeBlock = "@the_mighty_zork\n\n```\n@the_mighty_zork\n```" + mdMentionAndCodeBlockExpected = "

@the_mighty_zork

@the_mighty_zork\n
" ) type MarkdownTestSuite struct { @@ -133,6 +137,20 @@ func (suite *MarkdownTestSuite) TestParseWithBlockquote() { suite.Equal(mdWithBlockQuoteExpected, s) } +func (suite *MarkdownTestSuite) TestParseHashtagWithCodeBlock() { + s := suite.formatter.FromMarkdown(context.Background(), mdHashtagAndCodeBlock, nil, []*gtsmodel.Tag{ + suite.testTags["Hashtag"], + }) + suite.Equal(mdHashtagAndCodeBlockExpected, s) +} + +func (suite *MarkdownTestSuite) TestParseMentionWithCodeBlock() { + s := suite.formatter.FromMarkdown(context.Background(), mdMentionAndCodeBlock, []*gtsmodel.Mention{ + suite.testMentions["local_user_2_mention_zork"], + }, nil) + suite.Equal(mdMentionAndCodeBlockExpected, s) +} + func TestMarkdownTestSuite(t *testing.T) { suite.Run(t, new(MarkdownTestSuite)) }