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  }