code.gitea.io/gitea@v1.19.3/modules/markup/csv/csv.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package markup 5 6 import ( 7 "bufio" 8 "bytes" 9 "html" 10 "io" 11 "regexp" 12 "strconv" 13 14 "code.gitea.io/gitea/modules/csv" 15 "code.gitea.io/gitea/modules/markup" 16 "code.gitea.io/gitea/modules/setting" 17 ) 18 19 func init() { 20 markup.RegisterRenderer(Renderer{}) 21 } 22 23 // Renderer implements markup.Renderer for csv files 24 type Renderer struct{} 25 26 // Name implements markup.Renderer 27 func (Renderer) Name() string { 28 return "csv" 29 } 30 31 // Extensions implements markup.Renderer 32 func (Renderer) Extensions() []string { 33 return []string{".csv", ".tsv"} 34 } 35 36 // SanitizerRules implements markup.Renderer 37 func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { 38 return []setting.MarkupSanitizerRule{ 39 {Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`data-table`)}, 40 {Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)}, 41 {Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)}, 42 } 43 } 44 45 func writeField(w io.Writer, element, class, field string) error { 46 if _, err := io.WriteString(w, "<"); err != nil { 47 return err 48 } 49 if _, err := io.WriteString(w, element); err != nil { 50 return err 51 } 52 if len(class) > 0 { 53 if _, err := io.WriteString(w, " class=\""); err != nil { 54 return err 55 } 56 if _, err := io.WriteString(w, class); err != nil { 57 return err 58 } 59 if _, err := io.WriteString(w, "\""); err != nil { 60 return err 61 } 62 } 63 if _, err := io.WriteString(w, ">"); err != nil { 64 return err 65 } 66 if _, err := io.WriteString(w, html.EscapeString(field)); err != nil { 67 return err 68 } 69 if _, err := io.WriteString(w, "</"); err != nil { 70 return err 71 } 72 if _, err := io.WriteString(w, element); err != nil { 73 return err 74 } 75 _, err := io.WriteString(w, ">") 76 return err 77 } 78 79 // Render implements markup.Renderer 80 func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { 81 tmpBlock := bufio.NewWriter(output) 82 83 // FIXME: don't read all to memory 84 rawBytes, err := io.ReadAll(input) 85 if err != nil { 86 return err 87 } 88 89 if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) { 90 if _, err := tmpBlock.WriteString("<pre>"); err != nil { 91 return err 92 } 93 if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil { 94 return err 95 } 96 _, err = tmpBlock.WriteString("</pre>") 97 return err 98 } 99 100 rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes)) 101 if err != nil { 102 return err 103 } 104 105 if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil { 106 return err 107 } 108 row := 1 109 for { 110 fields, err := rd.Read() 111 if err == io.EOF { 112 break 113 } 114 if err != nil { 115 continue 116 } 117 if _, err := tmpBlock.WriteString("<tr>"); err != nil { 118 return err 119 } 120 element := "td" 121 if row == 1 { 122 element = "th" 123 } 124 if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil { 125 return err 126 } 127 for _, field := range fields { 128 if err := writeField(tmpBlock, element, "", field); err != nil { 129 return err 130 } 131 } 132 if _, err := tmpBlock.WriteString("</tr>"); err != nil { 133 return err 134 } 135 136 row++ 137 } 138 if _, err = tmpBlock.WriteString("</table>"); err != nil { 139 return err 140 } 141 return tmpBlock.Flush() 142 }