appview: render markdown in titles, commit messages and descriptions #508

merged
opened by oppi.li targeting master from push-sssuxsytslts

uses the newly added description filter that only autlinks, and renders backticks, bold and italics.

Signed-off-by: oppiliappan me@oppi.li

Changed files
+26 -8
appview
issues
pages
pulls
+12
appview/issues/issues.go
··· 7 "net/http" 8 "slices" 9 "strconv" 10 "time" 11 12 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 21 "tangled.sh/tangled.sh/core/appview/notify" 22 "tangled.sh/tangled.sh/core/appview/oauth" 23 "tangled.sh/tangled.sh/core/appview/pages" 24 "tangled.sh/tangled.sh/core/appview/pagination" 25 "tangled.sh/tangled.sh/core/appview/reporesolver" 26 "tangled.sh/tangled.sh/core/idresolver" ··· 643 return 644 } 645 646 tx, err := rp.db.BeginTx(r.Context(), nil) 647 if err != nil { 648 rp.pages.Notice(w, "issues", "Failed to create issue, try again later")
··· 7 "net/http" 8 "slices" 9 "strconv" 10 + "strings" 11 "time" 12 13 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 22 "tangled.sh/tangled.sh/core/appview/notify" 23 "tangled.sh/tangled.sh/core/appview/oauth" 24 "tangled.sh/tangled.sh/core/appview/pages" 25 + "tangled.sh/tangled.sh/core/appview/pages/markup" 26 "tangled.sh/tangled.sh/core/appview/pagination" 27 "tangled.sh/tangled.sh/core/appview/reporesolver" 28 "tangled.sh/tangled.sh/core/idresolver" ··· 645 return 646 } 647 648 + sanitizer := markup.NewSanitizer() 649 + if st := strings.TrimSpace(sanitizer.SanitizeDescription(title)); st == "" { 650 + rp.pages.Notice(w, "issues", "Title is empty after HTML sanitization") 651 + return 652 + } 653 + if sb := strings.TrimSpace(sanitizer.SanitizeDefault(body)); sb == "" { 654 + rp.pages.Notice(w, "issues", "Body is empty after HTML sanitization") 655 + return 656 + } 657 + 658 tx, err := rp.db.BeginTx(r.Context(), nil) 659 if err != nil { 660 rp.pages.Notice(w, "issues", "Failed to create issue, try again later")
+1 -1
appview/pages/templates/repo/fragments/repoDescription.html
··· 1 {{ define "repo/fragments/repoDescription" }} 2 <span id="repo-description" class="flex flex-wrap items-center gap-2 text-sm" hx-target="this" hx-swap="outerHTML"> 3 {{ if .RepoInfo.Description }} 4 - {{ .RepoInfo.Description }} 5 {{ else }} 6 <span class="italic">this repo has no description</span> 7 {{ end }}
··· 1 {{ define "repo/fragments/repoDescription" }} 2 <span id="repo-description" class="flex flex-wrap items-center gap-2 text-sm" hx-target="this" hx-swap="outerHTML"> 3 {{ if .RepoInfo.Description }} 4 + {{ .RepoInfo.Description | description }} 5 {{ else }} 6 <span class="italic">this repo has no description</span> 7 {{ end }}
+1 -1
appview/pages/templates/repo/issues/issue.html
··· 11 {{ define "repoContent" }} 12 <header class="pb-4"> 13 <h1 class="text-2xl"> 14 - {{ .Issue.Title }} 15 <span class="text-gray-500 dark:text-gray-400">#{{ .Issue.IssueId }}</span> 16 </h1> 17 </header>
··· 11 {{ define "repoContent" }} 12 <header class="pb-4"> 13 <h1 class="text-2xl"> 14 + {{ .Issue.Title | description }} 15 <span class="text-gray-500 dark:text-gray-400">#{{ .Issue.IssueId }}</span> 16 </h1> 17 </header>
+1 -1
appview/pages/templates/repo/issues/issues.html
··· 45 href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" 46 class="no-underline hover:underline" 47 > 48 - {{ .Title }} 49 <span class="text-gray-500">#{{ .IssueId }}</span> 50 </a> 51 </div>
··· 45 href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" 46 class="no-underline hover:underline" 47 > 48 + {{ .Title | description }} 49 <span class="text-gray-500">#{{ .IssueId }}</span> 50 </a> 51 </div>
+1 -1
appview/pages/templates/repo/pulls/fragments/pullHeader.html
··· 1 {{ define "repo/pulls/fragments/pullHeader" }} 2 <header class="pb-4"> 3 <h1 class="text-2xl dark:text-white"> 4 - {{ .Pull.Title }} 5 <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span> 6 </h1> 7 </header>
··· 1 {{ define "repo/pulls/fragments/pullHeader" }} 2 <header class="pb-4"> 3 <h1 class="text-2xl dark:text-white"> 4 + {{ .Pull.Title | description }} 5 <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span> 6 </h1> 7 </header>
+1 -1
appview/pages/templates/repo/pulls/fragments/summarizedPullHeader.html
··· 9 </div> 10 <span class="truncate text-sm text-gray-800 dark:text-gray-200"> 11 <span class="text-gray-500 dark:text-gray-400">#{{ .PullId }}</span> 12 - {{ .Title }} 13 </span> 14 </div> 15
··· 9 </div> 10 <span class="truncate text-sm text-gray-800 dark:text-gray-200"> 11 <span class="text-gray-500 dark:text-gray-400">#{{ .PullId }}</span> 12 + {{ .Title | description }} 13 </span> 14 </div> 15
+1 -1
appview/pages/templates/repo/pulls/pull.html
··· 122 {{ end }} 123 </div> 124 <div class="flex items-center"> 125 - <span>{{ .Title }}</span> 126 {{ if gt (len .Body) 0 }} 127 <button 128 class="py-1/2 px-1 mx-2 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
··· 122 {{ end }} 123 </div> 124 <div class="flex items-center"> 125 + <span>{{ .Title | description }}</span> 126 {{ if gt (len .Body) 0 }} 127 <button 128 class="py-1/2 px-1 mx-2 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
+1 -1
appview/pages/templates/repo/pulls/pulls.html
··· 50 <div class="px-6 py-4 z-5"> 51 <div class="pb-2"> 52 <a href="/{{ $.RepoInfo.FullName }}/pulls/{{ .PullId }}" class="dark:text-white"> 53 - {{ .Title }} 54 <span class="text-gray-500 dark:text-gray-400">#{{ .PullId }}</span> 55 </a> 56 </div>
··· 50 <div class="px-6 py-4 z-5"> 51 <div class="pb-2"> 52 <a href="/{{ $.RepoInfo.FullName }}/pulls/{{ .PullId }}" class="dark:text-white"> 53 + {{ .Title | description }} 54 <span class="text-gray-500 dark:text-gray-400">#{{ .PullId }}</span> 55 </a> 56 </div>
+1 -1
appview/pages/templates/user/fragments/repoCard.html
··· 21 </div> 22 {{ with .Description }} 23 <div class="text-gray-600 dark:text-gray-300 text-sm"> 24 - {{ . }} 25 </div> 26 {{ end }} 27
··· 21 </div> 22 {{ with .Description }} 23 <div class="text-gray-600 dark:text-gray-300 text-sm"> 24 + {{ . | description }} 25 </div> 26 {{ end }} 27
+6
appview/pulls/pulls.go
··· 19 "tangled.sh/tangled.sh/core/appview/notify" 20 "tangled.sh/tangled.sh/core/appview/oauth" 21 "tangled.sh/tangled.sh/core/appview/pages" 22 "tangled.sh/tangled.sh/core/appview/reporesolver" 23 "tangled.sh/tangled.sh/core/idresolver" 24 "tangled.sh/tangled.sh/core/knotclient" ··· 740 s.pages.Notice(w, "pull", "Title is required for git-diff patches.") 741 return 742 } 743 } 744 745 // Validate we have at least one valid PR creation method
··· 19 "tangled.sh/tangled.sh/core/appview/notify" 20 "tangled.sh/tangled.sh/core/appview/oauth" 21 "tangled.sh/tangled.sh/core/appview/pages" 22 + "tangled.sh/tangled.sh/core/appview/pages/markup" 23 "tangled.sh/tangled.sh/core/appview/reporesolver" 24 "tangled.sh/tangled.sh/core/idresolver" 25 "tangled.sh/tangled.sh/core/knotclient" ··· 741 s.pages.Notice(w, "pull", "Title is required for git-diff patches.") 742 return 743 } 744 + sanitizer := markup.NewSanitizer() 745 + if st := strings.TrimSpace(sanitizer.SanitizeDescription(title)); (st) == "" { 746 + s.pages.Notice(w, "pull", "Title is empty after HTML sanitization") 747 + return 748 + } 749 } 750 751 // Validate we have at least one valid PR creation method