You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

csv.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package markup
  4. import (
  5. "bufio"
  6. "html"
  7. "io"
  8. "regexp"
  9. "strconv"
  10. "code.gitea.io/gitea/modules/csv"
  11. "code.gitea.io/gitea/modules/markup"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/translation"
  14. )
  15. func init() {
  16. markup.RegisterRenderer(Renderer{})
  17. }
  18. // Renderer implements markup.Renderer for csv files
  19. type Renderer struct{}
  20. // Name implements markup.Renderer
  21. func (Renderer) Name() string {
  22. return "csv"
  23. }
  24. // Extensions implements markup.Renderer
  25. func (Renderer) Extensions() []string {
  26. return []string{".csv", ".tsv"}
  27. }
  28. // SanitizerRules implements markup.Renderer
  29. func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
  30. return []setting.MarkupSanitizerRule{
  31. {Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`data-table`)},
  32. {Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
  33. {Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
  34. {Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile(`tw-flex tw-justify-center tw-items-center tw-py-4 tw-text-14`)},
  35. {Element: "a", AllowAttr: "href", Regexp: regexp.MustCompile(``)},
  36. }
  37. }
  38. func writeField(w io.Writer, element, class, field string) error {
  39. if _, err := io.WriteString(w, "<"); err != nil {
  40. return err
  41. }
  42. if _, err := io.WriteString(w, element); err != nil {
  43. return err
  44. }
  45. if len(class) > 0 {
  46. if _, err := io.WriteString(w, " class=\""); err != nil {
  47. return err
  48. }
  49. if _, err := io.WriteString(w, class); err != nil {
  50. return err
  51. }
  52. if _, err := io.WriteString(w, "\""); err != nil {
  53. return err
  54. }
  55. }
  56. if _, err := io.WriteString(w, ">"); err != nil {
  57. return err
  58. }
  59. if _, err := io.WriteString(w, html.EscapeString(field)); err != nil {
  60. return err
  61. }
  62. if _, err := io.WriteString(w, "</"); err != nil {
  63. return err
  64. }
  65. if _, err := io.WriteString(w, element); err != nil {
  66. return err
  67. }
  68. _, err := io.WriteString(w, ">")
  69. return err
  70. }
  71. // Render implements markup.Renderer
  72. func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
  73. tmpBlock := bufio.NewWriter(output)
  74. maxSize := setting.UI.CSV.MaxFileSize
  75. maxRows := setting.UI.CSV.MaxRows
  76. if maxSize != 0 {
  77. input = io.LimitReader(input, maxSize+1)
  78. }
  79. rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
  80. if err != nil {
  81. return err
  82. }
  83. if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
  84. return err
  85. }
  86. row := 0
  87. for {
  88. fields, err := rd.Read()
  89. if err == io.EOF || (row >= maxRows && maxRows != 0) {
  90. break
  91. }
  92. if err != nil {
  93. continue
  94. }
  95. if _, err := tmpBlock.WriteString("<tr>"); err != nil {
  96. return err
  97. }
  98. element := "td"
  99. if row == 0 {
  100. element = "th"
  101. }
  102. if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil {
  103. return err
  104. }
  105. for _, field := range fields {
  106. if err := writeField(tmpBlock, element, "", field); err != nil {
  107. return err
  108. }
  109. }
  110. if _, err := tmpBlock.WriteString("</tr>"); err != nil {
  111. return err
  112. }
  113. row++
  114. }
  115. if _, err = tmpBlock.WriteString("</table>"); err != nil {
  116. return err
  117. }
  118. // Check if maxRows or maxSize is reached, and if true, warn.
  119. if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
  120. locale := ctx.Ctx.Value(translation.ContextKey).(translation.Locale)
  121. // Construct the HTML string
  122. 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>`
  123. // Write the HTML string to the output
  124. if _, err := tmpBlock.WriteString(warn); err != nil {
  125. return err
  126. }
  127. }
  128. return tmpBlock.Flush()
  129. }