import.cljs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. (ns frontend.handler.import
  2. "Fns related to import from external services"
  3. (:require [cljs.core.async.interop :refer [p->c]]
  4. [clojure.core.async :as async]
  5. [clojure.edn :as edn]
  6. [clojure.string :as string]
  7. [clojure.walk :as walk]
  8. [frontend.db :as db]
  9. [frontend.db.async :as db-async]
  10. [frontend.format.block :as block]
  11. [frontend.format.mldoc :as mldoc]
  12. [frontend.handler.editor :as editor]
  13. [frontend.handler.notification :as notification]
  14. [frontend.handler.page :as page-handler]
  15. [frontend.state :as state]
  16. [frontend.util :as util]
  17. [logseq.graph-parser.mldoc :as gp-mldoc]
  18. [logseq.graph-parser.whiteboard :as gp-whiteboard]
  19. [medley.core :as medley]
  20. [promesa.core :as p]))
  21. ;;; import OPML files
  22. (defn import-from-opml!
  23. [data finished-ok-handler]
  24. #_:clj-kondo/ignore
  25. (when-let [repo (state/get-current-repo)]
  26. (let [config (gp-mldoc/default-config :markdown)
  27. [headers parsed-blocks] (mldoc/opml->edn config data)
  28. ;; add empty pos metadata
  29. parsed-blocks (map (fn [b] [b {}]) parsed-blocks)
  30. page-name (:title headers)
  31. parsed-blocks (->>
  32. (block/extract-blocks parsed-blocks "" :markdown {:page-name page-name})
  33. (mapv editor/wrap-parse-block))]
  34. (p/do!
  35. (when (not (db/page-exists? page-name))
  36. (page-handler/<create! page-name {:redirect? false}))
  37. (let [page-block (db/get-page page-name)
  38. children (:block/_parent page-block)
  39. blocks (db/sort-by-order children)
  40. last-block (last blocks)
  41. snd-last-block (last (butlast blocks))
  42. [target-block sibling?] (if (and last-block (seq (:block/title last-block)))
  43. [last-block true]
  44. (if snd-last-block
  45. [snd-last-block true]
  46. [page-block false]))]
  47. (editor/paste-blocks
  48. parsed-blocks
  49. {:target-block target-block
  50. :sibling? sibling?})
  51. (finished-ok-handler [page-name]))))))
  52. (defn create-page-with-exported-tree!
  53. "Create page from the per page object generated in `export-repo-as-edn-v2!`
  54. Return page-name (title)
  55. Extension to `insert-block-tree-after-target`
  56. :id - page's uuid
  57. :title - page's title (original name)
  58. :children - tree
  59. :properties - map
  60. "
  61. [{:keys [type uuid title children properties] :as tree}]
  62. (let [title (string/trim title)
  63. has-children? (seq children)
  64. page-format (or (some-> tree (:children) (first) (:format)) :markdown)
  65. whiteboard? (= type "whiteboard")]
  66. (p/do!
  67. (try (page-handler/<create! title {:redirect? false
  68. :format page-format
  69. :uuid uuid
  70. :create-first-block? false
  71. :properties properties
  72. :whiteboard? whiteboard?})
  73. (catch :default e
  74. (js/console.error e)
  75. (prn {:tree tree})
  76. (notification/show! (str "Error happens when creating page " title ":\n"
  77. e
  78. "\nSkipped and continue the remaining import.") :error)))
  79. (when has-children?
  80. (let [page-name (util/page-name-sanity-lc title)
  81. page-block (db/get-page page-name)]
  82. ;; Missing support for per block format (or deprecated?)
  83. (try (if whiteboard?
  84. ;; only works for file graph :block/properties
  85. (let [blocks (->> children
  86. (map (partial medley/map-keys (fn [k] (keyword "block" k))))
  87. (map gp-whiteboard/migrate-shape-block)
  88. (map #(merge % (gp-whiteboard/with-whiteboard-block-props % [:block/uuid uuid]))))]
  89. (db/transact! blocks))
  90. (editor/insert-block-tree children page-format
  91. {:target-block page-block
  92. :sibling? false
  93. :keep-uuid? true}))
  94. (catch :default e
  95. (js/console.error e)
  96. (prn {:tree tree})
  97. (notification/show! (str "Error happens when creating block content of page " title "\n"
  98. e
  99. "\nSkipped and continue the remaining import.") :error)))))))
  100. title)
  101. (defn- pre-transact-uuids!
  102. "Collect all uuids from page trees and write them to the db before hand."
  103. [pages]
  104. (let [uuids (mapv (fn [block]
  105. {:block/uuid (:uuid block)})
  106. (mapcat #(tree-seq map? :children %)
  107. pages))]
  108. (db/transact! uuids)))
  109. (defn- import-from-tree!
  110. "Not rely on file system - backend compatible.
  111. tree-translator-fn: translate exported tree structure to the desired tree for import"
  112. [data tree-translator-fn]
  113. (let [imported-chan (async/promise-chan)]
  114. (try
  115. (let [blocks (->> (:blocks data)
  116. (mapv tree-translator-fn)
  117. (sort-by :title)
  118. (medley/indexed))
  119. job-chan (async/to-chan! blocks)]
  120. (state/set-state! [:graph/importing-state :total] (count blocks))
  121. (pre-transact-uuids! blocks)
  122. (async/go-loop []
  123. (if-let [[i block] (async/<! job-chan)]
  124. (do
  125. (state/set-state! [:graph/importing-state :current-idx] (inc i))
  126. (state/set-state! [:graph/importing-state :current-page] (:title block))
  127. (async/<! (async/timeout 10))
  128. (create-page-with-exported-tree! block)
  129. (recur))
  130. (let [result (async/<! (p->c (db-async/<get-all-referenced-blocks-uuid (state/get-current-repo))))]
  131. (editor/set-blocks-id! result)
  132. (async/offer! imported-chan true)))))
  133. (catch :default e
  134. (notification/show! (str "Error happens when importing:\n" e) :error)
  135. (async/offer! imported-chan true)))))
  136. (defn tree-vec-translate-edn
  137. "Actions to do for loading edn tree structure.
  138. 1) Removes namespace `:block/` from all levels of the `tree-vec`
  139. 2) Rename all :block/page-name to :title
  140. 3) Rename all :block/id to :uuid"
  141. ([tree-vec]
  142. (let [kw-trans-fn #(-> %
  143. str
  144. (string/replace ":block/page-name" ":block/title")
  145. (string/replace ":block/id" ":block/uuid")
  146. (string/replace ":block/" "")
  147. keyword)
  148. map-trans-fn (fn [acc k v]
  149. (assoc acc (kw-trans-fn k) v))
  150. tree-trans-fn (fn [form]
  151. (if (and (map? form)
  152. (:block/id form))
  153. (reduce-kv map-trans-fn {} form)
  154. form))]
  155. (walk/postwalk tree-trans-fn tree-vec))))
  156. (defn import-from-edn!
  157. [raw finished-ok-handler]
  158. (try
  159. (let [data (edn/read-string raw)]
  160. (async/go
  161. (async/<! (import-from-tree! data tree-vec-translate-edn))
  162. (finished-ok-handler nil)))
  163. (catch :default e
  164. (js/console.error e)
  165. (notification/show!
  166. (str (.-message e))
  167. :error)))) ;; it was designed to accept a list of imported page names but now deprecated
  168. (defn tree-vec-translate-json
  169. "Actions to do for loading json tree structure.
  170. 1) Rename all :id to :uuid
  171. 2) Rename all :page-name to :title
  172. 3) Rename all :format \"markdown\" to :format `:markdown`"
  173. ([tree-vec]
  174. (let [kw-trans-fn #(-> %
  175. str
  176. (string/replace ":page-name" ":title")
  177. (string/replace ":id" ":uuid")
  178. (string/replace #"^:" "")
  179. keyword)
  180. map-trans-fn (fn [acc k v]
  181. (cond (= :format k)
  182. (assoc acc (kw-trans-fn k) (keyword v))
  183. (= :id k)
  184. (assoc acc (kw-trans-fn k) (uuid v))
  185. :else
  186. (assoc acc (kw-trans-fn k) v)))
  187. tree-trans-fn (fn [form]
  188. (if (and (map? form)
  189. (:id form))
  190. (reduce-kv map-trans-fn {} form)
  191. form))]
  192. (walk/postwalk tree-trans-fn tree-vec))))
  193. (defn import-from-json!
  194. [raw finished-ok-handler]
  195. (let [json (js/JSON.parse raw)
  196. clj-data (js->clj json :keywordize-keys true)]
  197. (async/go
  198. (async/<! (import-from-tree! clj-data tree-vec-translate-json))
  199. (finished-ok-handler nil)))) ;; it was designed to accept a list of imported page names but now deprecated