| @@ -66,13 +66,18 @@ func (s *Server) snippetsPage(w http.ResponseWriter, r *http.Request) { |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | type snippetFileView struct { |
| 69 | | Name string |
| 70 | | Size int64 |
| 71 | | Lines int |
| 72 | | Content string |
| 73 | | HTML template.HTML |
| 69 | Name string |
| 70 | Size int64 |
| 71 | Lines int |
| 72 | Content string |
| 73 | HTML template.HTML |
| 74 | TooLarge bool |
| 74 | 75 | } |
| 75 | 76 | |
| 77 | // snippetPage highlights files up to a shared budget across the page: a |
| 78 | // snippet with many or large files does not make one request highlight |
| 79 | // megabytes of markup. Content is filled only for the owner, whose edit |
| 80 | // textarea needs the raw text regardless of the budget. |
| 76 | 81 | func (s *Server) snippetPage(w http.ResponseWriter, r *http.Request) { |
| 77 | 82 | sn, viewer, ok := s.snippetScope(w, r) |
| 78 | 83 | if !ok { |
| @@ -83,13 +88,25 @@ func (s *Server) snippetPage(w http.ResponseWriter, r *http.Request) { |
| 83 | 88 | http.Error(w, "internal error", http.StatusInternalServerError) |
| 84 | 89 | return |
| 85 | 90 | } |
| 91 | canWrite := policy.CanWriteSnippet(viewer, sn) |
| 92 | budget := int64(maxRenderBytes) |
| 86 | 93 | views := make([]snippetFileView, 0, len(files)) |
| 87 | 94 | for _, f := range files { |
| 88 | 95 | lines := bytes.Count(f.Content, []byte("\n")) |
| 89 | 96 | if len(f.Content) > 0 && f.Content[len(f.Content)-1] != '\n' { |
| 90 | 97 | lines++ |
| 91 | 98 | } |
| 92 | | views = append(views, snippetFileView{f.Name, f.Size, lines, string(f.Content), highlight(f.Name, f.Content)}) |
| 99 | view := snippetFileView{Name: f.Name, Size: f.Size, Lines: lines} |
| 100 | if canWrite { |
| 101 | view.Content = string(f.Content) |
| 102 | } |
| 103 | if f.Size <= budget { |
| 104 | view.HTML = highlightPlain(f.Name, f.Content) |
| 105 | budget -= f.Size |
| 106 | } else { |
| 107 | view.TooLarge = true |
| 108 | } |
| 109 | views = append(views, view) |
| 93 | 110 | } |
| 94 | 111 | s.render(w, "snippet.html", struct { |
| 95 | 112 | basePage |
| @@ -98,7 +115,7 @@ func (s *Server) snippetPage(w http.ResponseWriter, r *http.Request) { |
| 98 | 115 | Files []snippetFileView |
| 99 | 116 | CanWrite bool |
| 100 | 117 | Notice string |
| 101 | | }{s.baseFor(viewer), sn.OwnerName, sn, views, policy.CanWriteSnippet(viewer, sn), s.takeFlash(w, r)}) |
| 118 | }{s.baseFor(viewer), sn.OwnerName, sn, views, canWrite, s.takeFlash(w, r)}) |
| 102 | 119 | } |
| 103 | 120 | |
| 104 | 121 | // snippetRaw serves one file as text, inert on the forge's origin. |
| @@ -147,28 +164,27 @@ func (s *Server) snippetNewSubmit(w http.ResponseWriter, r *http.Request, u stor |
| 147 | 164 | http.Redirect(w, r, "/"+u.Username+"/-/snippets/"+out.ID, http.StatusSeeOther) |
| 148 | 165 | } |
| 149 | 166 | |
| 150 | | // snippetAction runs a write on the snippet in the URL and returns to |
| 151 | | // its page with the message, or to the list after a delete. A snippet |
| 152 | | // the viewer may not read is the 404 page, as on every read. |
| 167 | // snippetAction runs a write on the snippet in the URL and returns to its |
| 168 | // page with the message, or to dest (the list, for a delete) on success. |
| 169 | // A snippet the viewer may not read is the 404 page, as on every read. |
| 153 | 170 | func (s *Server) snippetAction(w http.ResponseWriter, r *http.Request, u store.User, argv []string, stdin string, dest string) { |
| 154 | 171 | sn, _, ok := s.snippetScope(w, r) |
| 155 | 172 | if !ok { |
| 156 | 173 | return |
| 157 | 174 | } |
| 175 | page := "/" + sn.OwnerName + "/-/snippets/" + sn.PublicID |
| 158 | 176 | if dest == "" { |
| 159 | | dest = "/" + sn.OwnerName + "/-/snippets/" + sn.PublicID |
| 177 | dest = page |
| 160 | 178 | } |
| 161 | 179 | back := func(w http.ResponseWriter, r *http.Request, msg string) { |
| 162 | 180 | s.setFlash(w, msg) |
| 163 | | http.Redirect(w, r, dest, http.StatusSeeOther) |
| 164 | | } |
| 165 | | var msg string |
| 166 | | var code int |
| 167 | | if stdin == "" { |
| 168 | | _, msg, code = s.runControlCode(u, argv) |
| 169 | | } else { |
| 170 | | msg, code = s.runControlStdinCode(u, argv, stdin) |
| 181 | to := dest |
| 182 | if msg != "" { |
| 183 | to = page |
| 184 | } |
| 185 | http.Redirect(w, r, to, http.StatusSeeOther) |
| 171 | 186 | } |
| 187 | msg, code := s.runControlStdinCode(u, argv, stdin) |
| 172 | 188 | if code == protocol.ExitDenied { |
| 173 | 189 | http.Error(w, msg, http.StatusForbidden) |
| 174 | 190 | return |