mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 14:02:25 -05:00 
			
		
		
		
	[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
This commit is contained in:
		
					parent
					
						
							
								0245c606d7
							
						
					
				
			
			
				commit
				
					
						f01492ae48
					
				
			
		
					 2 changed files with 60 additions and 7 deletions
				
			
		|  | @ -19,7 +19,9 @@ | ||||||
| package text | package text | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"io" | ||||||
| 
 | 
 | ||||||
| 	"github.com/russross/blackfriday/v2" | 	"github.com/russross/blackfriday/v2" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
|  | @ -33,18 +35,51 @@ var ( | ||||||
| 	m            *minify.M | 	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 { | 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 | 	renderer := &renderer{ | ||||||
| 	content = f.ReplaceMentions(ctx, content, mentions) | 		f:        f, | ||||||
|  | 		ctx:      ctx, | ||||||
|  | 		mentions: mentions, | ||||||
|  | 		tags:     tags, | ||||||
|  | 		HTMLRenderer: *blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ | ||||||
|  | 			Flags: blackfriday.CommonHTMLFlags, | ||||||
|  | 		}), | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// parse markdown | 	// parse markdown, use custom renderer to add hashtag/mention links | ||||||
| 	contentBytes := blackfriday.Run([]byte(content), blackfriday.WithExtensions(bfExtensions)) | 	contentBytes := blackfriday.Run([]byte(md), blackfriday.WithExtensions(bfExtensions), blackfriday.WithRenderer(renderer)) | ||||||
| 
 | 
 | ||||||
| 	// clean anything dangerous out of it | 	// clean anything dangerous out of it | ||||||
| 	content = SanitizeHTML(string(contentBytes)) | 	content := SanitizeHTML(string(contentBytes)) | ||||||
| 
 | 
 | ||||||
| 	if m == nil { | 	if m == nil { | ||||||
| 		m = minify.New() | 		m = minify.New() | ||||||
|  |  | ||||||
|  | @ -65,6 +65,10 @@ const ( | ||||||
| 	mdWithFootnoteExpected          = "<p>fox mulder,fbi.<sup id=\"fnref:1\"><a href=\"#fn:1\" rel=\"nofollow noreferrer\">1</a></sup></p><div><hr><ol><li id=\"fn:1\">federated bureau of investigation<br></li></ol></div>" | 	mdWithFootnoteExpected          = "<p>fox mulder,fbi.<sup id=\"fnref:1\"><a href=\"#fn:1\" rel=\"nofollow noreferrer\">1</a></sup></p><div><hr><ol><li id=\"fn:1\">federated bureau of investigation<br></li></ol></div>" | ||||||
| 	mdWithBlockQuote                = "get ready, there's a block quote coming:\n\n>line1\n>line2\n>\n>line3\n\n" | 	mdWithBlockQuote                = "get ready, there's a block quote coming:\n\n>line1\n>line2\n>\n>line3\n\n" | ||||||
| 	mdWithBlockQuoteExpected        = "<p>get ready, there’s a block quote coming:</p><blockquote><p>line1<br>line2</p><p>line3</p></blockquote>" | 	mdWithBlockQuoteExpected        = "<p>get ready, there’s a block quote coming:</p><blockquote><p>line1<br>line2</p><p>line3</p></blockquote>" | ||||||
|  | 	mdHashtagAndCodeBlock           = "#Hashtag\n\n```\n#Hashtag\n```" | ||||||
|  | 	mdHashtagAndCodeBlockExpected   = "<p><a href=\"http://localhost:8080/tags/Hashtag\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>Hashtag</span></a></p><pre><code>#Hashtag\n</code></pre>" | ||||||
|  | 	mdMentionAndCodeBlock           = "@the_mighty_zork\n\n```\n@the_mighty_zork\n```" | ||||||
|  | 	mdMentionAndCodeBlockExpected   = "<p><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span></p><pre><code>@the_mighty_zork\n</code></pre>" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type MarkdownTestSuite struct { | type MarkdownTestSuite struct { | ||||||
|  | @ -133,6 +137,20 @@ func (suite *MarkdownTestSuite) TestParseWithBlockquote() { | ||||||
| 	suite.Equal(mdWithBlockQuoteExpected, s) | 	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) { | func TestMarkdownTestSuite(t *testing.T) { | ||||||
| 	suite.Run(t, new(MarkdownTestSuite)) | 	suite.Run(t, new(MarkdownTestSuite)) | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue