markdown.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package tool
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "github.com/yuin/goldmark"
  9. "github.com/yuin/goldmark/extension"
  10. "github.com/yuin/goldmark/renderer/html"
  11. "mvdan.cc/xurls/v2"
  12. )
  13. func Markdown(db *sql.DB, db_set map[string]string, data map[string]string) map[string]interface{} {
  14. input := []byte(data["data"])
  15. backlink := map[string]map[string]string{}
  16. link_count := 0
  17. markdown := goldmark.New(
  18. goldmark.WithExtensions(
  19. extension.NewLinkify(
  20. extension.WithLinkifyAllowedProtocols([]string{
  21. "http:",
  22. "https:",
  23. }),
  24. extension.WithLinkifyURLRegexp(
  25. xurls.Strict(),
  26. ),
  27. ),
  28. extension.Strikethrough,
  29. extension.Table,
  30. ),
  31. goldmark.WithRendererOptions(html.WithHardWraps()),
  32. )
  33. var buf bytes.Buffer
  34. if err := markdown.Convert(input, &buf); err != nil {
  35. panic(err)
  36. }
  37. string_data := buf.String()
  38. code_stack := []int{}
  39. code_stack_idx := 0
  40. code_stack_end := map[string]string{}
  41. r := regexp.MustCompile(`(<code>|<\/code>)`)
  42. for idx := r.FindStringIndex(string_data); len(idx) != 0; idx = r.FindStringIndex(string_data) {
  43. if string_data[idx[0]:idx[1]] == "<code>" {
  44. code_stack = []int{idx[0], idx[1]}
  45. string_data = strings.Replace(string_data, "<code>", "<0001>", 1)
  46. } else {
  47. string_data = strings.Replace(string_data, "<0001>", "<code>", -1)
  48. code_stack_idx_str := strconv.Itoa(code_stack_idx)
  49. code_stack_end["code_"+code_stack_idx_str] = string_data[code_stack[0]:idx[1]]
  50. code_stack_idx++
  51. string_data = string_data[:code_stack[0]] + "<code_" + code_stack_idx_str + ">" + string_data[idx[1]:]
  52. }
  53. }
  54. r = regexp.MustCompile(`\[([^\[\]]+)\]\(([^\(\)]*)\)`)
  55. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  56. match := r.FindStringSubmatch(m)
  57. link := match[2]
  58. if link == "" {
  59. link = match[1]
  60. }
  61. return "<a href=\"" + link + "\">" + match[1] + "</a>"
  62. })
  63. // p := bluemonday.UGCPolicy()
  64. // string_data := p.Sanitize(string_data)
  65. r = regexp.MustCompile(`<code_[0-9]+>`)
  66. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  67. m = strings.Replace(m, "<", "", 1)
  68. m = strings.Replace(m, ">", "", 1)
  69. return code_stack_end[m]
  70. })
  71. r = regexp.MustCompile(`<a href="([^"]+)"`)
  72. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  73. match := r.FindStringSubmatch(m)
  74. m1, _ := regexp.MatchString(`^https?:\/\/`, match[1])
  75. if m1 {
  76. return "<a href=\"" + match[1] + "\" class=\"opennamu_link_out\" target=\"_blank\""
  77. } else {
  78. if _, ok := backlink[match[1]]; !ok {
  79. backlink[match[1]] = map[string]string{}
  80. }
  81. var exist string
  82. stmt, err := db.Prepare(DB_change(db_set, "select title from data where title = ?"))
  83. if err != nil {
  84. exist = ""
  85. }
  86. defer stmt.Close()
  87. err = stmt.QueryRow(match[1]).Scan(&exist)
  88. if err != nil {
  89. exist = ""
  90. }
  91. backlink[match[1]][""] = ""
  92. link_count += 1
  93. class := ""
  94. if exist == "" {
  95. class = "opennamu_not_exist_link"
  96. }
  97. return "<a href=\"/w/" + match[1] + "\" class=\"" + class + "\""
  98. }
  99. })
  100. string_data = strings.Replace(string_data, "<ul>", "<ul class=\"opennamu_ul\">", -1)
  101. end_backlink := [][]string{}
  102. for k1, v1 := range backlink {
  103. for k2, v2 := range v1 {
  104. end_backlink = append(end_backlink, []string{
  105. data["doc_name"],
  106. k1,
  107. k2,
  108. v2,
  109. })
  110. }
  111. }
  112. end_data := make(map[string]interface{})
  113. end_data["data"] = string_data
  114. end_data["js_data"] = ""
  115. end_data["backlink"] = end_backlink
  116. end_data["link_count"] = link_count
  117. return end_data
  118. }