internal/httpd/preview.go
87 lines · 3247 bytes
8 symbols in this file
1package httpd
2
3import (
4 "html/template"
5 "net/http"
6 "net/url"
7 "path"
8 "strings"
9
10 "gitbay.org/gitbay/internal/store"
11)
12
13// A markup form's Preview button posts to the form's own action with
14// preview set. The handler renders the body and hands back the page it
15// came from instead of writing anything, so what appears above the
16// textarea is the rendering the thread will show. There is no
17// JavaScript on this site (script-src 'none'), so a preview is a round
18// trip, the way the blob page's rendered/source toggle is (#235).
19//
20// Form names the textarea the draft belongs to, because a page can
21// carry more than one: an issue has both an edit form and a compose
22// box, and only the one that was submitted gets the preview. The other
23// fields of that form ride along in vals so the page can put them back.
24type draft struct {
25 Form string
26 Body string
27 Format string
28 HTML template.HTML
29 vals url.Values
30}
31
32// Is reports whether this draft belongs to the named form. Templates
33// call it to place the preview and open the right box; a nil draft
34// answers false, so a page renders unchanged when nothing was
35// previewed.
36func (d *draft) Is(form string) bool { return d != nil && d.Form == form }
37
38// Or is what a field of the named form should show: what was typed when
39// this draft is that form's, and the stored value otherwise. It is how
40// a previewed form comes back with everything still in it.
41func (d *draft) Or(form, field, fallback string) string {
42 if !d.Is(form) {
43 return fallback
44 }
45 return d.vals.Get(field)
46}
47
48// wantsPreview reports whether the submission asked to see the markup
49// rather than save it.
50func wantsPreview(r *http.Request) bool { return r.FormValue("preview") != "" }
51
52// draftFor renders a repository-scoped body the way the thread renders
53// it, autolinks and all. The caller supplies the format rather than the
54// helper reading the picker, because only the create forms have one: an
55// edit keeps the item's stored format, and a comment is markdown, which
56// is what the command does with no --format.
57func (s *Server) draftFor(r *http.Request, repo store.Repo, form, field, format string) *draft {
58 return s.draftWith(r, form, format, r.FormValue(field), s.ugcFor(r, repo))
59}
60
61// draftWith is draftFor for text with no repository behind it: a
62// profile's about, or a file in the editor. The renderer is the one
63// that surface uses, so the preview matches where the text will land.
64func (s *Server) draftWith(r *http.Request, form, format, body string, render ugcRenderer) *draft {
65 r.ParseForm() // Or reads r.Form; a handler may not have touched it yet.
66 return &draft{Form: form, Body: body, Format: format,
67 HTML: render(strings.TrimSpace(body), format), vals: r.Form}
68}
69
70// markupFile reports whether a path is one the forge renders: the blob
71// page's rendered view and the editor's preview take the same set.
72func markupFile(filePath string) bool {
73 switch path.Ext(strings.ToLower(filePath)) {
74 case ".md", ".markdown", ".org":
75 return true
76 }
77 return false
78}
79
80// bodyFormat reads the format picker, defaulting to markdown the way
81// every submit handler does.
82func bodyFormat(r *http.Request) string {
83 if r.FormValue("format") == "org" {
84 return "org"
85 }
86 return "md"
87}