tools.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //
  2. // Copyright (C) 2017-2019 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='../../assets/`+app.cfg.Skin+`/tools.xslt'?>
  48. `)
  49. data := map[string]string{
  50. "skin": app.cfg.Skin,
  51. "title": app.cfg.Title,
  52. "xml_base": app.cgi.String(),
  53. "tag_rename_old": "",
  54. "tag_rename_new": "",
  55. "other_shaarli_url": "",
  56. "other_shaarli_tag": time.Now().Format(time.RFC3339[:16]),
  57. "version": version,
  58. "gitsha1": GitSHA1,
  59. }
  60. if err := tmpl.Execute(w, data); err != nil {
  61. http.Error(w, "Coudln't render tools: "+err.Error(), http.StatusInternalServerError)
  62. }
  63. }
  64. case http.MethodPost:
  65. app.KeepAlive(w, r, now)
  66. if "" != r.FormValue("shaarli_import_submit") {
  67. if url, err := url.Parse(strings.TrimSpace(r.FormValue("shaarli_import_url")) + "?do=atom&nb=all"); err != nil {
  68. http.Error(w, "Coudln't parse shaarli_import_url "+err.Error(), http.StatusBadRequest)
  69. } else {
  70. if rq, err := HttpGetBody(url, timeoutShaarliImportFetch); err != nil {
  71. http.Error(w, "Coudln't fetch shaarli_import_url "+err.Error(), http.StatusBadRequest)
  72. } else {
  73. if importedFeed, err := FeedFromReader(rq); err != nil {
  74. http.Error(w, "Coudln't parse feed from shaarli_import_url "+err.Error(), http.StatusBadRequest)
  75. } else {
  76. log.Printf("Import %d entries from %v\n", len(importedFeed.Entries), url)
  77. cat := Category{Term: strings.TrimSpace(strings.TrimPrefix(r.FormValue("shaarli_import_tag"), "#"))}
  78. feed, _ := LoadFeed()
  79. feed.XmlBase = Iri(app.url.String())
  80. // feed.Id = feed.XmlBase
  81. impEnt := make([]*Entry, 0, len(importedFeed.Entries))
  82. for _, entry := range importedFeed.Entries {
  83. if et, err := entry.NormaliseAfterImport(); err != nil {
  84. log.Printf("Error with %v: %v\n", entry.Id, err.Error())
  85. } else {
  86. // log.Printf("done entry: %s\n", et.Id)
  87. if "" != cat.Term {
  88. et.Categories = append(et.Categories, cat)
  89. }
  90. if _, err := feed.Append(&et); err == nil {
  91. impEnt = append(impEnt, &et)
  92. } else {
  93. log.Printf("couldn't add entry: %s\n", err.Error())
  94. }
  95. }
  96. }
  97. if err := app.SaveFeed(feed); err != nil {
  98. http.Error(w, "couldn't store feed data: "+err.Error(), http.StatusInternalServerError)
  99. return
  100. }
  101. if err := app.PublishFeedsForModifiedEntries(feed, feed.Entries); err != nil {
  102. log.Println("couldn't write feeds: ", err.Error())
  103. http.Error(w, "couldn't write feeds: "+err.Error(), http.StatusInternalServerError)
  104. return
  105. }
  106. }
  107. }
  108. }
  109. }
  110. http.Redirect(w, r, "../..", http.StatusFound)
  111. }
  112. }
  113. }
  114. func (entry Entry) NormaliseAfterImport() (Entry, error) {
  115. // log.Printf("process entry: %s\n", entry.Id)
  116. // normalise Id
  117. if idx := strings.Index(string(entry.Id), "?"); idx >= 0 {
  118. entry.Id = entry.Id[idx+1:]
  119. }
  120. if entry.Published.IsZero() {
  121. entry.Published = entry.Updated
  122. }
  123. // normalise Links
  124. if nil != entry.Content {
  125. entry.Content = &HumanText{Body: cleanLegacyContent(entry.Content.Body)}
  126. }
  127. err := entry.Validate()
  128. return entry, err
  129. }