export.cljs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. (ns ^:no-doc frontend.handler.export
  2. (:require
  3. ["@capacitor/filesystem" :refer [Encoding Filesystem]]
  4. [cljs.pprint :as pprint]
  5. [clojure.set :as s]
  6. [clojure.string :as string]
  7. [clojure.walk :as walk]
  8. [frontend.config :as config]
  9. [frontend.db :as db]
  10. [frontend.extensions.zip :as zip]
  11. [frontend.external.roam-export :as roam-export]
  12. [frontend.handler.notification :as notification]
  13. [frontend.mobile.util :as mobile-util]
  14. [logseq.publishing.html :as publish-html]
  15. [frontend.state :as state]
  16. [frontend.util :as util]
  17. [goog.dom :as gdom]
  18. [lambdaisland.glogi :as log]
  19. [promesa.core :as p]
  20. [frontend.persist-db :as persist-db]
  21. [cljs-bean.core :as bean]
  22. [frontend.handler.export.common :as export-common-handler])
  23. (:import
  24. [goog.string StringBuffer]))
  25. (defn download-repo-as-html!
  26. "download public pages as html"
  27. [repo]
  28. (when-let [db (db/get-db repo)]
  29. (let [{:keys [asset-filenames html]}
  30. (publish-html/build-html db
  31. {:app-state (select-keys @state/state
  32. [:ui/theme
  33. :ui/sidebar-collapsed-blocks])
  34. :repo-config (get-in @state/state [:config repo])
  35. :db-graph? (config/db-based-graph? repo)})
  36. html-str (str "data:text/html;charset=UTF-8,"
  37. (js/encodeURIComponent html))]
  38. (if (util/electron?)
  39. (js/window.apis.exportPublishAssets
  40. html
  41. (config/get-repo-dir repo)
  42. (clj->js asset-filenames)
  43. (util/mocked-open-dir-path))
  44. (when-let [anchor (gdom/getElement "download-as-html")]
  45. (.setAttribute anchor "href" html-str)
  46. (.setAttribute anchor "download" "index.html")
  47. (.click anchor))))))
  48. (defn export-repo-as-zip!
  49. [repo]
  50. (p/let [files (export-common-handler/<get-file-contents repo "md")
  51. [owner repo-name] (util/get-git-owner-and-repo repo)
  52. repo-name (str owner "-" repo-name)
  53. files (map (fn [{:keys [path content]}] [path content]) files)]
  54. (when (seq files)
  55. (p/let [zipfile (zip/make-zip repo-name files repo)]
  56. (when-let [anchor (gdom/getElement "download")]
  57. (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
  58. (.setAttribute anchor "download" (.-name zipfile))
  59. (.click anchor))))))
  60. (defn- export-file-on-mobile [data path]
  61. (p/catch
  62. (.writeFile Filesystem (clj->js {:path path
  63. :data data
  64. :encoding (.-UTF8 Encoding)
  65. :recursive true}))
  66. (notification/show! "Export succeeded! You can find you exported file in the root directory of your graph." :success)
  67. (fn [error]
  68. (notification/show! "Export failed!" :error)
  69. (log/error :export-file-failed error))))
  70. ;; FIXME: All uses of :block/properties in this ns
  71. (defn- dissoc-properties [m ks]
  72. (if (:block/properties m)
  73. (update m :block/properties
  74. (fn [v]
  75. (apply dissoc v ks)))
  76. m))
  77. (defn- nested-select-keys
  78. [keyseq vec-tree]
  79. (walk/postwalk
  80. (fn [x]
  81. (cond
  82. (and (map? x) (contains? x :block/uuid))
  83. (-> x
  84. (s/rename-keys {:block/uuid :block/id
  85. :block/original-name :block/page-name})
  86. (dissoc-properties [:id])
  87. (select-keys keyseq))
  88. :else
  89. x))
  90. vec-tree))
  91. (defn- <build-blocks
  92. [repo]
  93. (p/let [pages (export-common-handler/<get-all-pages repo)]
  94. {:version 1
  95. :blocks
  96. (nested-select-keys [:block/id
  97. :block/type
  98. :block/page-name
  99. :block/properties
  100. :block/format
  101. :block/children
  102. :block/content
  103. :block/created-at
  104. :block/updated-at]
  105. pages)}))
  106. (defn- file-name [repo extension]
  107. (-> (string/replace repo config/local-db-prefix "")
  108. (string/replace #"^/+" "")
  109. (str "_" (quot (util/time-ms) 1000))
  110. (str "." (string/lower-case (name extension)))))
  111. (defn- <export-repo-as-edn-str [repo]
  112. (p/let [result (<build-blocks repo)]
  113. (prn :debug :result result)
  114. (let [sb (StringBuffer.)]
  115. (pprint/pprint result (StringBufferWriter. sb))
  116. (str sb))))
  117. (defn export-repo-as-edn!
  118. [repo]
  119. (p/let [edn-str (<export-repo-as-edn-str repo)]
  120. (when edn-str
  121. (let [data-str (some->> edn-str
  122. js/encodeURIComponent
  123. (str "data:text/edn;charset=utf-8,"))
  124. filename (file-name repo :edn)]
  125. (if (mobile-util/native-platform?)
  126. (export-file-on-mobile edn-str filename)
  127. (when-let [anchor (gdom/getElement "download-as-edn-v2")]
  128. (.setAttribute anchor "href" data-str)
  129. (.setAttribute anchor "download" filename)
  130. (.click anchor)))))))
  131. (defn- nested-update-id
  132. [vec-tree]
  133. (walk/postwalk
  134. (fn [x]
  135. (if (and (map? x) (contains? x :block/id))
  136. (update x :block/id str)
  137. x))
  138. vec-tree))
  139. (defn export-repo-as-json!
  140. [repo]
  141. (p/let [result (<build-blocks repo)
  142. json-str (-> result
  143. nested-update-id
  144. clj->js
  145. js/JSON.stringify)
  146. filename (file-name repo :json)
  147. data-str (str "data:text/json;charset=utf-8,"
  148. (js/encodeURIComponent json-str))]
  149. (if (mobile-util/native-platform?)
  150. (export-file-on-mobile json-str filename)
  151. (when-let [anchor (gdom/getElement "download-as-json-v2")]
  152. (.setAttribute anchor "href" data-str)
  153. (.setAttribute anchor "download" filename)
  154. (.click anchor)))))
  155. (defn export-repo-as-sqlite-db!
  156. [repo]
  157. (p/let [data (persist-db/<export-db repo {:return-data? true})
  158. filename (file-name repo "sqlite")
  159. url (js/URL.createObjectURL (js/Blob. #js [data]))]
  160. (when-not (mobile-util/native-platform?)
  161. (when-let [anchor (gdom/getElement "download-as-sqlite-db")]
  162. (.setAttribute anchor "href" url)
  163. (.setAttribute anchor "download" filename)
  164. (.click anchor)))))
  165. ;;;;;;;;;;;;;;;;;;;;;;;;;
  166. ;; Export to roam json ;;
  167. ;;;;;;;;;;;;;;;;;;;;;;;;;
  168. ;; https://roamresearch.com/#/app/help/page/Nxz8u0vXU
  169. ;; export to roam json according to above spec
  170. (defn- <roam-data [repo]
  171. (p/let [pages (export-common-handler/<get-all-pages repo)]
  172. (let [non-empty-pages (remove #(empty? (:block/children %)) pages)]
  173. (roam-export/traverse
  174. [:page/title
  175. :block/string
  176. :block/uid
  177. :block/children]
  178. non-empty-pages))))
  179. (defn export-repo-as-roam-json!
  180. [repo]
  181. (p/let [data (<roam-data repo)
  182. json-str (-> data
  183. bean/->js
  184. js/JSON.stringify)
  185. data-str (str "data:text/json;charset=utf-8,"
  186. (js/encodeURIComponent json-str))]
  187. (when-let [anchor (gdom/getElement "download-as-roam-json")]
  188. (.setAttribute anchor "href" data-str)
  189. (.setAttribute anchor "download" (file-name (str repo "_roam") :json))
  190. (.click anchor))))