gitea源码

csv.go 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. "strconv"
  9. "code.gitea.io/gitea/modules/csv"
  10. "code.gitea.io/gitea/modules/markup"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/translation"
  13. "code.gitea.io/gitea/modules/util"
  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: `^data-table$`},
  32. {Element: "th", AllowAttr: "class", Regexp: `^line-num$`},
  33. {Element: "td", AllowAttr: "class", Regexp: `^line-num$`},
  34. }
  35. }
  36. func writeField(w io.Writer, element, class, field string) error {
  37. if _, err := io.WriteString(w, "<"); err != nil {
  38. return err
  39. }
  40. if _, err := io.WriteString(w, element); err != nil {
  41. return err
  42. }
  43. if len(class) > 0 {
  44. if _, err := io.WriteString(w, ` class="`); err != nil {
  45. return err
  46. }
  47. if _, err := io.WriteString(w, class); err != nil {
  48. return err
  49. }
  50. if _, err := io.WriteString(w, `"`); err != nil {
  51. return err
  52. }
  53. }
  54. if _, err := io.WriteString(w, ">"); err != nil {
  55. return err
  56. }
  57. if _, err := io.WriteString(w, html.EscapeString(field)); err != nil {
  58. return err
  59. }
  60. if _, err := io.WriteString(w, "</"); err != nil {
  61. return err
  62. }
  63. if _, err := io.WriteString(w, element); err != nil {
  64. return err
  65. }
  66. _, err := io.WriteString(w, ">")
  67. return err
  68. }
  69. // Render implements markup.Renderer
  70. func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
  71. tmpBlock := bufio.NewWriter(output)
  72. maxSize := setting.UI.CSV.MaxFileSize
  73. maxRows := setting.UI.CSV.MaxRows
  74. if maxSize != 0 {
  75. input = io.LimitReader(input, maxSize+1)
  76. }
  77. rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
  78. if err != nil {
  79. return err
  80. }
  81. if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
  82. return err
  83. }
  84. row := 0
  85. for {
  86. fields, err := rd.Read()
  87. if err == io.EOF || (row >= maxRows && maxRows != 0) {
  88. break
  89. }
  90. if err != nil {
  91. continue
  92. }
  93. if _, err := tmpBlock.WriteString("<tr>"); err != nil {
  94. return err
  95. }
  96. element := "td"
  97. if row == 0 {
  98. element = "th"
  99. }
  100. if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil {
  101. return err
  102. }
  103. for _, field := range fields {
  104. if err := writeField(tmpBlock, element, "", field); err != nil {
  105. return err
  106. }
  107. }
  108. if _, err := tmpBlock.WriteString("</tr>"); err != nil {
  109. return err
  110. }
  111. row++
  112. }
  113. if _, err = tmpBlock.WriteString("</table>"); err != nil {
  114. return err
  115. }
  116. // Check if maxRows or maxSize is reached, and if true, warn.
  117. if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
  118. warn := `<table class="data-table"><tr><td>`
  119. rawLink := ` <a href="` + ctx.RenderHelper.ResolveLink(util.PathEscapeSegments(ctx.RenderOptions.RelativePath), markup.LinkTypeRaw) + `">`
  120. // Try to get the user translation
  121. if locale, ok := ctx.Value(translation.ContextKey).(translation.Locale); ok {
  122. warn += locale.TrString("repo.file_too_large")
  123. rawLink += locale.TrString("repo.file_view_raw")
  124. } else {
  125. warn += "The file is too large to be shown."
  126. rawLink += "View Raw"
  127. }
  128. warn += rawLink + `</a></td></tr></table>`
  129. // Write the HTML string to the output
  130. if _, err := tmpBlock.WriteString(warn); err != nil {
  131. return err
  132. }
  133. }
  134. return tmpBlock.Flush()
  135. }