tools.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //
  2. // Copyright (C) 2017-2021 Marcus Rohrmoser, http://purl.mro.name/ShaarliGo
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. package main
  18. import (
  19. "encoding/xml"
  20. "html/template"
  21. "io"
  22. "log"
  23. "net/http"
  24. "net/url"
  25. "strings"
  26. "time"
  27. )
  28. const timeoutShaarliImportFetch = time.Minute
  29. func (app *Server) handleTools() http.HandlerFunc {
  30. return func(w http.ResponseWriter, r *http.Request) {
  31. now := time.Now()
  32. if !app.IsLoggedIn(now) {
  33. http.Redirect(w, r, cgiName+"?do=login&returnurl="+url.QueryEscape(r.URL.String()), http.StatusUnauthorized)
  34. return
  35. }
  36. if !app.cfg.IsConfigured() {
  37. http.Redirect(w, r, cgiName+"/config", http.StatusPreconditionFailed)
  38. return
  39. }
  40. switch r.Method {
  41. case http.MethodGet:
  42. app.KeepAlive(w, r, now)
  43. byt, _ := tplToolsHtmlBytes()
  44. if tmpl, err := template.New("tools").Parse(string(byt)); err == nil {
  45. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  46. io.WriteString(w, xml.Header)
  47. io.WriteString(w, `<?xml-stylesheet type='text/xsl' href='../../themes/current/tools.xslt'?>
  48. `)
  49. data := map[string]string{
  50. "title": app.cfg.Title,
  51. "xml_base": app.cgi.String(),
  52. "tag_rename_old": "",
  53. "tag_rename_new": "",
  54. "other_shaarli_url": "",
  55. "other_shaarli_tag": time.Now().Format(time.RFC3339[:16]),
  56. "version": version,
  57. "gitsha1": GitSHA1,
  58. }
  59. if err := tmpl.Execute(w, data); err != nil {
  60. http.Error(w, "Coudln't render tools: "+err.Error(), http.StatusInternalServerError)
  61. }
  62. }
  63. case http.MethodPost:
  64. app.KeepAlive(w, r, now)
  65. if "" != r.FormValue("shaarli_import_submit") {
  66. if url, err := url.Parse(strings.TrimSpace(r.FormValue("shaarli_import_url")) + "?do=atom&nb=all"); err != nil {
  67. http.Error(w, "Coudln't parse shaarli_import_url "+err.Error(), http.StatusBadRequest)
  68. } else {
  69. if rq, err := HttpGetBody(url, timeoutShaarliImportFetch); err != nil {
  70. http.Error(w, "Coudln't fetch shaarli_import_url "+err.Error(), http.StatusBadRequest)
  71. } else {
  72. if importedFeed, err := FeedFromReader(rq); err != nil {
  73. http.Error(w, "Coudln't parse feed from shaarli_import_url "+err.Error(), http.StatusBadRequest)
  74. } else {
  75. log.Printf("Import %d entries from %v\n", len(importedFeed.Entries), url)
  76. cat := Category{Term: strings.TrimSpace(strings.TrimPrefix(r.FormValue("shaarli_import_tag"), "#"))}
  77. feed, _ := LoadFeed()
  78. feed.XmlBase = Iri(app.url.String())
  79. // feed.Id = feed.XmlBase
  80. impEnt := make([]*Entry, 0, len(importedFeed.Entries))
  81. for _, entry := range importedFeed.Entries {
  82. if et, err := entry.NormaliseAfterImport(); err != nil {
  83. log.Printf("Error with %v: %v\n", entry.Id, err.Error())
  84. } else {
  85. // log.Printf("done entry: %s\n", et.Id)
  86. if "" != cat.Term {
  87. et.Categories = append(et.Categories, cat)
  88. }
  89. if _, err := feed.Append(&et); err == nil {
  90. impEnt = append(impEnt, &et)
  91. } else {
  92. log.Printf("couldn't add entry: %s\n", err.Error())
  93. }
  94. }
  95. }
  96. if err := app.SaveFeed(feed); err != nil {
  97. http.Error(w, "couldn't store feed data: "+err.Error(), http.StatusInternalServerError)
  98. return
  99. }
  100. if err := app.PublishFeedsForModifiedEntries(feed, feed.Entries); err != nil {
  101. log.Println("couldn't write feeds: ", err.Error())
  102. http.Error(w, "couldn't write feeds: "+err.Error(), http.StatusInternalServerError)
  103. return
  104. }
  105. }
  106. }
  107. }
  108. }
  109. http.Redirect(w, r, "../..", http.StatusFound)
  110. }
  111. }
  112. }
  113. func (entry Entry) NormaliseAfterImport() (Entry, error) {
  114. // log.Printf("process entry: %s\n", entry.Id)
  115. // normalise Id
  116. if idx := strings.Index(string(entry.Id), "?"); idx >= 0 {
  117. entry.Id = entry.Id[idx+1:]
  118. }
  119. if entry.Published.IsZero() {
  120. entry.Published = entry.Updated
  121. }
  122. // normalise Links
  123. if nil != entry.Content {
  124. entry.Content = &HumanText{Body: cleanLegacyContent(entry.Content.Body)}
  125. }
  126. err := entry.Validate()
  127. return entry, err
  128. }