Fix hashes in code blocks rendered as hashtags
Previously, our hashtag parser would indiscriminately replace hashtag-like text with hashtag HTML -- including in places it shouldn't have, like inside code blocks. Along with the v1.7.0 changes to writeas/saturday, this fixes that and closes #6. As a bonus, strings of #spaceless#hashtags#in#a#row are now rendered correctly.
This commit is contained in:
parent
c2436a43c5
commit
32e99d0041
2
app.go
2
app.go
|
@ -142,7 +142,7 @@ func handleTemplatedPage(app *app, w http.ResponseWriter, r *http.Request, t *te
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Content = template.HTML(applyMarkdown([]byte(c)))
|
p.Content = template.HTML(applyMarkdown([]byte(c), ""))
|
||||||
p.PlainContent = shortPostDescription(stripmd.Strip(c))
|
p.PlainContent = shortPostDescription(stripmd.Strip(c))
|
||||||
if updated != nil {
|
if updated != nil {
|
||||||
p.Updated = updated.Format("January 2, 2006")
|
p.Updated = updated.Format("January 2, 2006")
|
||||||
|
|
2
feed.go
2
feed.go
|
@ -93,7 +93,7 @@ func ViewFeed(app *app, w http.ResponseWriter, req *http.Request) error {
|
||||||
Title: title,
|
Title: title,
|
||||||
Link: &Link{Href: permalink},
|
Link: &Link{Href: permalink},
|
||||||
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
|
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
|
||||||
Content: applyMarkdown([]byte(p.Content)),
|
Content: applyMarkdown([]byte(p.Content), ""),
|
||||||
Author: &Author{author, ""},
|
Author: &Author{author, ""},
|
||||||
Created: p.Created,
|
Created: p.Created,
|
||||||
Updated: p.Updated,
|
Updated: p.Updated,
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
package writefreely
|
package writefreely
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
stripmd "github.com/writeas/go-strip-markdown"
|
stripmd "github.com/writeas/go-strip-markdown"
|
||||||
|
@ -31,7 +30,7 @@ var (
|
||||||
endBlockReg = regexp.MustCompile("</([a-z]+)>\n</(ul|ol|blockquote)>")
|
endBlockReg = regexp.MustCompile("</([a-z]+)>\n</(ul|ol|blockquote)>")
|
||||||
youtubeReg = regexp.MustCompile("(https?://www.youtube.com/embed/[a-zA-Z0-9\\-_]+)(\\?[^\t\n\f\r \"']+)?")
|
youtubeReg = regexp.MustCompile("(https?://www.youtube.com/embed/[a-zA-Z0-9\\-_]+)(\\?[^\t\n\f\r \"']+)?")
|
||||||
titleElementReg = regexp.MustCompile("</?h[1-6]>")
|
titleElementReg = regexp.MustCompile("</?h[1-6]>")
|
||||||
hashtagReg = regexp.MustCompile(`(?m)(^| )#([\p{L}\p{M}\d]+)`)
|
hashtagReg = regexp.MustCompile(`{{\[\[\|\|([^|]+)\|\|\]\]}}`)
|
||||||
markeddownReg = regexp.MustCompile("<p>(.+)</p>")
|
markeddownReg = regexp.MustCompile("<p>(.+)</p>")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,22 +39,10 @@ func (p *Post) formatContent(c *Collection, isOwner bool) {
|
||||||
if !isSingleUser {
|
if !isSingleUser {
|
||||||
baseURL = "/" + c.Alias + "/"
|
baseURL = "/" + c.Alias + "/"
|
||||||
}
|
}
|
||||||
newCon := hashtagReg.ReplaceAllFunc([]byte(p.Content), func(b []byte) []byte {
|
|
||||||
// Ensure we only replace "hashtags" that have already been extracted.
|
|
||||||
// `hashtagReg` catches everything, including any hash on the end of a
|
|
||||||
// URL, so we rely on p.Tags as the final word on whether or not to link
|
|
||||||
// a tag.
|
|
||||||
for _, t := range p.Tags {
|
|
||||||
if strings.TrimSpace(string(b)) == "#"+t {
|
|
||||||
return bytes.Replace(b, []byte("#"+t), []byte("<a href=\""+baseURL+"tag:"+t+"\" class=\"hashtag\"><span>#</span><span class=\"p-category\">"+t+"</span></a>"), -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
})
|
|
||||||
p.HTMLTitle = template.HTML(applyBasicMarkdown([]byte(p.Title.String)))
|
p.HTMLTitle = template.HTML(applyBasicMarkdown([]byte(p.Title.String)))
|
||||||
p.HTMLContent = template.HTML(applyMarkdown([]byte(newCon)))
|
p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), baseURL))
|
||||||
if exc := strings.Index(string(newCon), "<!--more-->"); exc > -1 {
|
if exc := strings.Index(string(p.Content), "<!--more-->"); exc > -1 {
|
||||||
p.HTMLExcerpt = template.HTML(applyMarkdown([]byte(newCon[:exc])))
|
p.HTMLExcerpt = template.HTML(applyMarkdown([]byte(p.Content[:exc]), baseURL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,11 +50,11 @@ func (p *PublicPost) formatContent(isOwner bool) {
|
||||||
p.Post.formatContent(&p.Collection.Collection, isOwner)
|
p.Post.formatContent(&p.Collection.Collection, isOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyMarkdown(data []byte) string {
|
func applyMarkdown(data []byte, baseURL string) string {
|
||||||
return applyMarkdownSpecial(data, false)
|
return applyMarkdownSpecial(data, false, baseURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyMarkdownSpecial(data []byte, skipNoFollow bool) string {
|
func applyMarkdownSpecial(data []byte, skipNoFollow bool, baseURL string) string {
|
||||||
mdExtensions := 0 |
|
mdExtensions := 0 |
|
||||||
blackfriday.EXTENSION_TABLES |
|
blackfriday.EXTENSION_TABLES |
|
||||||
blackfriday.EXTENSION_FENCED_CODE |
|
blackfriday.EXTENSION_FENCED_CODE |
|
||||||
|
@ -79,8 +66,16 @@ func applyMarkdownSpecial(data []byte, skipNoFollow bool) string {
|
||||||
blackfriday.HTML_USE_SMARTYPANTS |
|
blackfriday.HTML_USE_SMARTYPANTS |
|
||||||
blackfriday.HTML_SMARTYPANTS_DASHES
|
blackfriday.HTML_SMARTYPANTS_DASHES
|
||||||
|
|
||||||
|
if baseURL != "" {
|
||||||
|
htmlFlags |= blackfriday.HTML_HASHTAGS
|
||||||
|
}
|
||||||
|
|
||||||
// Generate Markdown
|
// Generate Markdown
|
||||||
md := blackfriday.Markdown([]byte(data), blackfriday.HtmlRenderer(htmlFlags, "", ""), mdExtensions)
|
md := blackfriday.Markdown([]byte(data), blackfriday.HtmlRenderer(htmlFlags, "", ""), mdExtensions)
|
||||||
|
if baseURL != "" {
|
||||||
|
// Replace special text generated by Markdown parser
|
||||||
|
md = []byte(hashtagReg.ReplaceAll(md, []byte("<a href=\""+baseURL+"tag:$1\" class=\"hashtag\"><span>#</span><span class=\"p-category\">$1</span></a>")))
|
||||||
|
}
|
||||||
// Strip out bad HTML
|
// Strip out bad HTML
|
||||||
policy := getSanitizationPolicy()
|
policy := getSanitizationPolicy()
|
||||||
policy.RequireNoFollowOnLinks(!skipNoFollow)
|
policy.RequireNoFollowOnLinks(!skipNoFollow)
|
||||||
|
|
2
posts.go
2
posts.go
|
@ -368,7 +368,7 @@ func handleViewPost(app *app, w http.ResponseWriter, r *http.Request) error {
|
||||||
Direction: d,
|
Direction: d,
|
||||||
}
|
}
|
||||||
if !isRaw {
|
if !isRaw {
|
||||||
post.HTMLContent = template.HTML(applyMarkdown([]byte(content)))
|
post.HTMLContent = template.HTML(applyMarkdown([]byte(content), ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
read.go
4
read.go
|
@ -95,7 +95,7 @@ func (db *datastore) FetchPublicPosts() (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p.extractData()
|
p.extractData()
|
||||||
p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content)))
|
p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), ""))
|
||||||
fp := p.processPost()
|
fp := p.processPost()
|
||||||
if isCollectionPost {
|
if isCollectionPost {
|
||||||
fp.Collection = &CollectionObj{Collection: *c}
|
fp.Collection = &CollectionObj{Collection: *c}
|
||||||
|
@ -283,7 +283,7 @@ func viewLocalTimelineFeed(app *app, w http.ResponseWriter, req *http.Request) e
|
||||||
Title: title,
|
Title: title,
|
||||||
Link: &Link{Href: permalink},
|
Link: &Link{Href: permalink},
|
||||||
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
|
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
|
||||||
Content: applyMarkdown([]byte(p.Content)),
|
Content: applyMarkdown([]byte(p.Content), ""),
|
||||||
Author: &Author{author, ""},
|
Author: &Author{author, ""},
|
||||||
Created: p.Created,
|
Created: p.Created,
|
||||||
Updated: p.Updated,
|
Updated: p.Updated,
|
||||||
|
|
Loading…
Reference in New Issue