export.cljs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. (ns frontend.handler.export
  2. (:require [frontend.state :as state]
  3. [frontend.db :as db]
  4. [frontend.format.protocol :as fp]
  5. [frontend.format :as f]
  6. [datascript.core :as d]
  7. [frontend.util :as util]
  8. [cljs-bean.core :as bean]
  9. [clojure.string :as string]
  10. [goog.dom :as gdom]
  11. [frontend.publishing.html :as html]
  12. [frontend.text :as text]
  13. [frontend.handler.common :as common-handler]
  14. [frontend.extensions.zip :as zip]
  15. [promesa.core :as p]))
  16. (defn copy-block!
  17. [block-id]
  18. (when-let [block (db/pull [:block/uuid block-id])]
  19. (let [content (:block/content block)]
  20. (common-handler/copy-to-clipboard-without-id-property! content))))
  21. (defn copy-block-as-json!
  22. [block-id]
  23. (when-let [repo (state/get-current-repo)]
  24. (let [block-children (db/get-block-and-children repo block-id)]
  25. (util/copy-to-clipboard! (js/JSON.stringify (bean/->js block-children))))))
  26. (defn copy-page-as-json!
  27. [page-name]
  28. (when-let [repo (state/get-current-repo)]
  29. (let [properties (db/get-page-properties page-name)
  30. blocks (db/get-page-blocks repo page-name)]
  31. (util/copy-to-clipboard!
  32. (js/JSON.stringify
  33. (bean/->js
  34. {:properties properties
  35. :blocks blocks}))))))
  36. (defn export-repo-as-json!
  37. [repo]
  38. (when-let [db (db/get-conn repo)]
  39. (let [db-json (db/db->json db)
  40. data-str (str "data:text/json;charset=utf-8," (js/encodeURIComponent db-json))]
  41. (when-let [anchor (gdom/getElement "download-as-json")]
  42. (.setAttribute anchor "href" data-str)
  43. (.setAttribute anchor "download" (str (last (string/split repo #"/")) ".json"))
  44. (.click anchor)))))
  45. (defn export-repo-as-edn!
  46. [repo]
  47. (when-let [db (db/get-conn repo)]
  48. (let [db-edn (db/db->edn-str db)
  49. data-str (str "data:text/edn;charset=utf-8," (js/encodeURIComponent db-edn))]
  50. (when-let [anchor (gdom/getElement "download-as-edn")]
  51. (.setAttribute anchor "href" data-str)
  52. (.setAttribute anchor "download" (str (last (string/split repo #"/")) ".edn"))
  53. (.click anchor)))))
  54. (defn download-file!
  55. [file-path]
  56. (when-let [repo (state/get-current-repo)]
  57. (when-let [content (db/get-file repo file-path)]
  58. (let [data (js/Blob. ["\ufeff" (array content)] ; prepend BOM
  59. (clj->js {:type "text/plain;charset=utf-8,"}))]
  60. (let [anchor (gdom/getElement "download")
  61. url (js/window.URL.createObjectURL data)]
  62. (.setAttribute anchor "href" url)
  63. (.setAttribute anchor "download" file-path)
  64. (.click anchor))))))
  65. (defn export-repo-as-html!
  66. [repo]
  67. (when-let [db (db/get-conn repo)]
  68. (let [db (if (state/all-pages-public?)
  69. (db/clean-export! db)
  70. (db/filter-only-public-pages-and-blocks db))
  71. db-str (db/db->string db)
  72. state (select-keys @state/state
  73. [:ui/theme :ui/cycle-collapse
  74. :ui/collapsed-blocks
  75. :ui/sidebar-collapsed-blocks
  76. :ui/show-recent?
  77. :config])
  78. state (update state :config (fn [config]
  79. {"local" (get config repo)}))
  80. raw-html-str (html/publishing-html db-str (pr-str state))
  81. html-str (str "data:text/html;charset=UTF-8,"
  82. (js/encodeURIComponent raw-html-str))]
  83. (if (util/electron?)
  84. (js/window.apis.exportPublishAssets raw-html-str)
  85. (when-let [anchor (gdom/getElement "download-as-html")]
  86. (.setAttribute anchor "href" html-str)
  87. (.setAttribute anchor "download" "index.html")
  88. (.click anchor))))))
  89. (defn export-repo-as-zip!
  90. [repo]
  91. (let [files (db/get-file-contents repo)
  92. [owner repo-name] (util/get-git-owner-and-repo repo)
  93. repo-name (str owner "-" repo-name)]
  94. (when (seq files)
  95. (p/let [zipfile (zip/make-zip repo-name files)]
  96. (when-let [anchor (gdom/getElement "download-as-zip")]
  97. (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
  98. (.setAttribute anchor "download" (.-name zipfile))
  99. (.click anchor))))))
  100. (defn- get-file-contents-with-suffix
  101. [repo]
  102. (let [conn (db/get-conn repo)]
  103. (->>
  104. (filterv (fn [[path _]]
  105. (or (string/ends-with? path ".md")
  106. (string/ends-with? path ".org")))
  107. (db/get-file-contents repo))
  108. (mapv (fn [[path content]] {:path path :content content
  109. :names (d/q '[:find [?n ?n2]
  110. :in $ ?p
  111. :where [?e :file/path ?p]
  112. [?e2 :page/file ?e]
  113. [?e2 :page/name ?n]
  114. [?e2 :page/original-name ?n2]] conn path)
  115. :format (f/get-format path)})))))
  116. (defn- get-embed-and-refs-blocks-pages-aux
  117. [repo page-or-block is-block? exclude-blocks exclude-pages]
  118. (let [[ref-blocks ref-pages]
  119. (->> (if is-block?
  120. [page-or-block]
  121. (db/get-page-blocks
  122. repo page-or-block {:use-cache? false
  123. :pull-keys '[:block/ref-pages :block/ref-blocks]}))
  124. (filterv #(or (:block/ref-blocks %) (:block/ref-pages %)))
  125. (mapv (fn [b] [(:block/ref-blocks b), (:block/ref-pages b)]))
  126. (apply mapv vector)
  127. (mapv #(vec (distinct (flatten (remove nil? %))))))
  128. ref-block-ids
  129. (->> ref-blocks
  130. (#(remove (fn [b] (contains? exclude-blocks (:db/id b))) %))
  131. (mapv #(:db/id %)))
  132. ref-page-ids
  133. (->> ref-pages
  134. (#(remove (fn [b] (contains? exclude-pages (:db/id b))) %))
  135. (mapv #(:db/id %)))
  136. ref-blocks
  137. (->> ref-block-ids
  138. (db/pull-many repo '[*])
  139. (flatten))
  140. ref-pages
  141. (->> ref-page-ids
  142. (db/pull-many repo '[*])
  143. (flatten))
  144. [next-ref-blocks1 next-ref-pages1]
  145. (->> ref-blocks
  146. (mapv #(get-embed-and-refs-blocks-pages-aux repo % true
  147. (set (concat ref-block-ids exclude-blocks)) exclude-pages))
  148. (apply mapv vector))
  149. [next-ref-blocks2 next-ref-pages2]
  150. (->> ref-pages
  151. (mapv #(get-embed-and-refs-blocks-pages-aux repo (:page/name %) false
  152. exclude-blocks (set (concat ref-page-ids exclude-pages))))
  153. (apply mapv vector))]
  154. [(->> (concat ref-block-ids next-ref-blocks1 next-ref-blocks2)
  155. (flatten)
  156. (distinct))
  157. (->> (concat ref-page-ids next-ref-pages1 next-ref-pages2)
  158. (flatten)
  159. (distinct))]))
  160. (defn- get-embed-and-refs-blocks-pages
  161. [repo page]
  162. (let [[block-ids page-ids]
  163. (get-embed-and-refs-blocks-pages-aux repo page false #{} #{})
  164. blocks
  165. (db/pull-many repo '[*] block-ids)
  166. pages-name-and-content
  167. (->> page-ids
  168. (d/q '[:find ?n (pull ?p [:file/path])
  169. :in $ [?e ...]
  170. :where
  171. [?e :page/file ?p]
  172. [?e :page/name ?n]] (db/get-conn repo))
  173. (mapv (fn [[page-name file-path]] [page-name (:file/path file-path)]))
  174. (d/q '[:find ?n ?c
  175. :in $ [[?n ?p] ...]
  176. :where
  177. [?e :file/path ?p]
  178. [?e :file/content ?c]] @(db/get-files-conn repo)))
  179. embed-blocks
  180. (mapv (fn [b] [(str (:block/uuid b))
  181. [(apply str
  182. (mapv #(:block/content %)
  183. (db/get-block-and-children repo (:block/uuid b))))
  184. (:block/title b)]])
  185. blocks)]
  186. {:embed_blocks embed-blocks
  187. :embed_pages pages-name-and-content}))
  188. (defn export-repo-as-markdown!
  189. [repo]
  190. (when-let [repo (state/get-current-repo)]
  191. (when-let [files (get-file-contents-with-suffix repo)]
  192. (mapv (fn [{:keys [path content names format]}]
  193. [path (fp/exportMarkdown f/mldoc-record content
  194. (f/get-default-config format)
  195. (js/JSON.stringify
  196. (clj->js (get-embed-and-refs-blocks-pages repo (first names)))))]) files))))