mirror of https://github.com/go-gitea/gitea
Merge fce76ef193
into b06aac40e6
This commit is contained in:
commit
97c629f5e0
|
@ -1333,6 +1333,9 @@ LEVEL = Info
|
|||
;;
|
||||
;; Maximum allowed file size in bytes to render CSV files as table. (Set to 0 for no limit).
|
||||
;MAX_FILE_SIZE = 524288
|
||||
;;
|
||||
;; Maximum allowed rows to render CSV files. (Set to 0 for no limit)
|
||||
;MAX_ROWS = 100000
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -5,8 +5,6 @@ package markup
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"regexp"
|
||||
|
@ -15,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/csv"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -40,6 +39,8 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
|
|||
{Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`data-table`)},
|
||||
{Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
|
||||
{Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
|
||||
{Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile(`tw-flex tw-justify-center tw-items-center tw-py-4 tw-text-14`)},
|
||||
{Element: "a", AllowAttr: "href", Regexp: regexp.MustCompile(``)},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,86 +82,38 @@ func writeField(w io.Writer, element, class, field string) error {
|
|||
func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||
tmpBlock := bufio.NewWriter(output)
|
||||
maxSize := setting.UI.CSV.MaxFileSize
|
||||
maxRows := setting.UI.CSV.MaxRows
|
||||
|
||||
if maxSize == 0 {
|
||||
return r.tableRender(ctx, input, tmpBlock)
|
||||
if maxSize != 0 {
|
||||
input = io.LimitReader(input, maxSize+1)
|
||||
}
|
||||
|
||||
rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if int64(len(rawBytes)) <= maxSize {
|
||||
return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock)
|
||||
}
|
||||
return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock)
|
||||
}
|
||||
|
||||
func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
|
||||
_, err := tmpBlock.WriteString("<pre>")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scan := bufio.NewScanner(input)
|
||||
scan.Split(bufio.ScanRunes)
|
||||
for scan.Scan() {
|
||||
switch scan.Text() {
|
||||
case `&`:
|
||||
_, err = tmpBlock.WriteString("&")
|
||||
case `'`:
|
||||
_, err = tmpBlock.WriteString("'") // "'" is shorter than "'" and apos was not in HTML until HTML5.
|
||||
case `<`:
|
||||
_, err = tmpBlock.WriteString("<")
|
||||
case `>`:
|
||||
_, err = tmpBlock.WriteString(">")
|
||||
case `"`:
|
||||
_, err = tmpBlock.WriteString(""") // """ is shorter than """.
|
||||
default:
|
||||
_, err = tmpBlock.Write(scan.Bytes())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = scan.Err(); err != nil {
|
||||
return fmt.Errorf("fallbackRender scan: %w", err)
|
||||
}
|
||||
|
||||
_, err = tmpBlock.WriteString("</pre>")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpBlock.Flush()
|
||||
}
|
||||
|
||||
func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error {
|
||||
rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
|
||||
return err
|
||||
}
|
||||
row := 1
|
||||
|
||||
row := 0
|
||||
for {
|
||||
fields, err := rd.Read()
|
||||
if err == io.EOF {
|
||||
if err == io.EOF || (row >= maxRows && maxRows != 0) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := tmpBlock.WriteString("<tr>"); err != nil {
|
||||
return err
|
||||
}
|
||||
element := "td"
|
||||
if row == 1 {
|
||||
if row == 0 {
|
||||
element = "th"
|
||||
}
|
||||
if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil {
|
||||
if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, field := range fields {
|
||||
|
@ -174,8 +127,23 @@ func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock
|
|||
|
||||
row++
|
||||
}
|
||||
|
||||
if _, err = tmpBlock.WriteString("</table>"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if maxRows or maxSize is reached, and if true, warn.
|
||||
if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
|
||||
locale := ctx.Ctx.Value(translation.ContextKey).(translation.Locale)
|
||||
|
||||
// Construct the HTML string
|
||||
warn := `<div class="tw-flex tw-justify-center tw-items-center tw-py-4 tw-text-14"><div>` + locale.TrString("repo.file_too_large") + ` <a class="source" href="` + ctx.Links.RawLink() + `/` + ctx.RelativePath + `">` + locale.TrString("repo.file_view_raw") + `</a></div></div>`
|
||||
|
||||
// Write the HTML string to the output
|
||||
if _, err := tmpBlock.WriteString(warn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tmpBlock.Flush()
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
package markup
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -31,12 +29,4 @@ func TestRenderCSV(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, v, buf.String())
|
||||
}
|
||||
|
||||
t.Run("fallbackRender", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf))
|
||||
assert.NoError(t, err)
|
||||
want := "<pre>1,<a>\n2,<b></pre>"
|
||||
assert.Equal(t, want, buf.String())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ var UI = struct {
|
|||
|
||||
CSV struct {
|
||||
MaxFileSize int64
|
||||
MaxRows int
|
||||
} `ini:"ui.csv"`
|
||||
|
||||
Admin struct {
|
||||
|
@ -108,8 +109,10 @@ var UI = struct {
|
|||
},
|
||||
CSV: struct {
|
||||
MaxFileSize int64
|
||||
MaxRows int
|
||||
}{
|
||||
MaxFileSize: 524288,
|
||||
MaxRows: 100000,
|
||||
},
|
||||
Admin: struct {
|
||||
UserPagingNum int
|
||||
|
|
Loading…
Reference in New Issue