Tidy up Markdown at-mentions in plain text form

This commit is contained in:
Noah Petherbridge 2024-11-28 12:31:04 -08:00
parent 688d2e0baa
commit 1935b28ee5
7 changed files with 77 additions and 7 deletions

View File

@ -10,11 +10,11 @@ import (
// returned by the scope list function. // returned by the scope list function.
func TestAdminScopesCount(t *testing.T) { func TestAdminScopesCount(t *testing.T) {
var scopes = config.ListAdminScopes() var scopes = config.ListAdminScopes()
if len(scopes) != config.QuantityAdminScopes || len(scopes) != len(config.AdminScopeDescriptions) { if len(scopes) != config.QuantityAdminScopes || len(scopes) != len(config.AdminScopeDescriptions)-1 {
t.Errorf( t.Errorf(
"The list of scopes returned by ListAdminScopes doesn't match the expected count. "+ "The list of scopes returned by ListAdminScopes doesn't match the expected count. "+
"Expected %d, got %d", "Expected %d (with %d descriptions), got %d",
config.QuantityAdminScopes, config.QuantityAdminScopes, len(config.AdminScopeDescriptions),
len(scopes), len(scopes),
) )
} }

View File

@ -2,12 +2,18 @@
package markdown package markdown
import ( import (
"regexp"
"strings" "strings"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/shurcooL/github_flavored_markdown" "github.com/shurcooL/github_flavored_markdown"
) )
var (
RegexpHTMLTag = regexp.MustCompile(`<(.|\n)+?>`)
RegexpMarkdownLink = regexp.MustCompile(`\[([^\[\]]*)\]\((.*?)\)`)
)
// Render markdown from untrusted sources. // Render markdown from untrusted sources.
func Render(input string) string { func Render(input string) string {
// Render Markdown to HTML. // Render Markdown to HTML.
@ -27,3 +33,28 @@ func Quotify(input string) string {
} }
return strings.Join(lines, "\n") return strings.Join(lines, "\n")
} }
/*
DeMarkify strips some Markdown syntax from plain text snippets.
It is especially useful on Forum views that show plain text contents of posts, and especially
for linked at-mentions of the form `[@username](/go/comment?id=1234)` so that those can be
simplified down to just the `@username` text contents.
This function will strip and simplify:
- Markdown hyperlinks as spelled out above
- Literal HTML tags such as <a href="">
*/
func DeMarkify(input string) string {
// Strip all standard HTML tags.
input = RegexpHTMLTag.ReplaceAllString(input, "")
// Replace Markdown hyperlinks.
matches := RegexpMarkdownLink.FindAllStringSubmatch(input, -1)
for _, m := range matches {
input = strings.ReplaceAll(input, m[0], m[1])
}
return input
}

View File

@ -0,0 +1,38 @@
package markdown_test
import (
"testing"
"code.nonshy.com/nonshy/website/pkg/markdown"
)
func TestDeMarkify(t *testing.T) {
var cases = []struct {
Input string
Expect string
}{
{
Input: "Hello world!",
Expect: "Hello world!",
},
{
Input: "[@username](/go/comment?id=1234) Very well said!",
Expect: "@username Very well said!",
},
{
Input: `<a href="https://wikipedia.org">Wikipedia</a> said **otherwise.**`,
Expect: "Wikipedia said **otherwise.**",
},
{
Input: "[Here](/here) is one [link](https://example.com), while [Here](/here) is [another](/another).",
Expect: "Here is one link, while Here is another.",
},
}
for i, tc := range cases {
actual := markdown.DeMarkify(tc.Input)
if actual != tc.Expect {
t.Errorf("Test #%d: expected '%s' but got '%s'", i, tc.Expect, actual)
}
}
}

View File

@ -36,6 +36,7 @@ func TemplateFuncs(r *http.Request) template.FuncMap {
"ComputeAge": utility.Age, "ComputeAge": utility.Age,
"Split": strings.Split, "Split": strings.Split,
"ToMarkdown": ToMarkdown, "ToMarkdown": ToMarkdown,
"DeMarkify": markdown.DeMarkify,
"ToJSON": ToJSON, "ToJSON": ToJSON,
"ToHTML": ToHTML, "ToHTML": ToHTML,
"ToString": func(v interface{}) string { "ToString": func(v interface{}) string {

View File

@ -132,7 +132,7 @@
{{if .Pinned}}<sup class="fa fa-thumbtack has-text-success mr-2 is-size-6" title="Pinned"></sup>{{end}} {{if .Pinned}}<sup class="fa fa-thumbtack has-text-success mr-2 is-size-6" title="Pinned"></sup>{{end}}
{{or .Title "Untitled"}} {{or .Title "Untitled"}}
</h2> </h2>
{{TrimEllipses .Comment.Message 256}} {{TrimEllipses (DeMarkify .Comment.Message) 256}}
</a> </a>
<hr class="has-background-success my-2"> <hr class="has-background-success my-2">

View File

@ -126,7 +126,7 @@
</h2> </h2>
<a href="/forum/thread/{{.ThreadID}}" class="has-text-dark"> <a href="/forum/thread/{{.ThreadID}}" class="has-text-dark">
{{TrimEllipses .Thread.Comment.Message 256}} {{TrimEllipses (DeMarkify .Thread.Comment.Message) 256}}
</a> </a>
{{$Photos := $Root.PhotoMap.Get .Thread.Comment.ID}} {{$Photos := $Root.PhotoMap.Get .Thread.Comment.ID}}
@ -191,7 +191,7 @@
</div> </div>
<a href="/go/comment?id={{.Comment.ID}}" class="has-text-dark"> <a href="/go/comment?id={{.Comment.ID}}" class="has-text-dark">
{{TrimEllipses .Comment.Message 256}} {{TrimEllipses (DeMarkify .Comment.Message) 256}}
</a> </a>
{{$Photos := $Root.PhotoMap.Get .Comment.ID}} {{$Photos := $Root.PhotoMap.Get .Comment.ID}}

View File

@ -165,7 +165,7 @@
</div> </div>
<a href="/go/comment?id={{.ID}}" class="has-text-dark"> <a href="/go/comment?id={{.ID}}" class="has-text-dark">
{{TrimEllipses .Message 512}} {{TrimEllipses (DeMarkify .Message) 512}}
</a> </a>
{{$Photos := $Root.PhotoMap.Get .ID}} {{$Photos := $Root.PhotoMap.Get .ID}}