model.cljs 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. (ns frontend.db.model
  2. "Core db functions."
  3. ;; TODO: Remove this config once how repos are passed to this ns are standardized
  4. {:clj-kondo/config {:linters {:unused-binding {:level :off}}}}
  5. (:require [clojure.set :as set]
  6. [clojure.string :as string]
  7. [clojure.walk :as walk]
  8. [datascript.core :as d]
  9. [frontend.date :as date]
  10. [frontend.db.conn :as conn]
  11. [frontend.db.react :as react]
  12. [frontend.db.utils :as db-utils]
  13. [frontend.state :as state]
  14. [frontend.util :as util :refer [react]]
  15. [frontend.util.drawer :as drawer]
  16. [logseq.db.frontend.rules :as rules]
  17. [logseq.db.frontend.content :as db-content]
  18. [logseq.graph-parser.text :as text]
  19. [logseq.graph-parser.util.db :as db-util]
  20. [logseq.common.util :as common-util]
  21. [cljs-time.core :as t]
  22. [cljs-time.format :as tf]
  23. [frontend.config :as config]
  24. [logseq.db :as ldb]))
  25. ;; TODO: extract to specific models and move data transform logic to the
  26. ;; corresponding handlers.
  27. (def block-attrs ldb/block-attrs)
  28. (defn get-tag-pages
  29. [repo tag-name]
  30. (when tag-name
  31. (d/q '[:find ?original-name ?name
  32. :in $ ?tag
  33. :where
  34. [?e :block/name ?tag]
  35. [?page :block/tags ?e]
  36. [?page :block/original-name ?original-name]
  37. [?page :block/name ?name]]
  38. (conn/get-db repo)
  39. (util/page-name-sanity-lc tag-name))))
  40. (defn get-tag-blocks
  41. [repo tag-name]
  42. (ldb/get-tag-blocks (conn/get-db repo) tag-name))
  43. (defn get-all-tagged-pages
  44. [repo]
  45. (d/q '[:find ?page-name ?tag
  46. :where
  47. [?page :block/tags ?e]
  48. [?e :block/original-name ?tag]
  49. [?page :block/name ?page-name]]
  50. (conn/get-db repo)))
  51. (defn get-all-namespace-relation
  52. [repo]
  53. (d/q '[:find ?page-name ?parent
  54. :where
  55. [?page :block/name ?page-name]
  56. [?page :block/namespace ?e]
  57. [?e :block/original-name ?parent]]
  58. (conn/get-db repo)))
  59. (defn get-all-namespace-parents
  60. [repo]
  61. (->> (get-all-namespace-relation repo)
  62. (map second)))
  63. (def hidden-page? ldb/hidden-page?)
  64. (defn get-all-pages
  65. [repo]
  66. (->>
  67. (d/q
  68. '[:find [(pull ?page [*]) ...]
  69. :where
  70. [?page :block/name]]
  71. (conn/get-db repo))
  72. (remove hidden-page?)))
  73. (defn get-all-page-original-names
  74. [repo]
  75. (let [db (conn/get-db repo)]
  76. (->>
  77. (d/datoms db :avet :block/original-name)
  78. (map :v)
  79. (remove hidden-page?))))
  80. (defn get-pages-with-file
  81. "Return full file entity for calling file renaming"
  82. [repo]
  83. (d/q
  84. '[:find (pull ?page [:block/name :block/properties :block/journal?]) (pull ?file [*])
  85. :where
  86. [?page :block/name ?page-name]
  87. [?page :block/file ?file]]
  88. (conn/get-db repo)))
  89. (defn get-page-alias
  90. [repo page-name]
  91. (when-let [db (and repo (conn/get-db repo))]
  92. (some->> (d/q '[:find ?alias
  93. :in $ ?page-name
  94. :where
  95. [?page :block/name ?page-name]
  96. [?page :block/alias ?alias]]
  97. db
  98. (util/page-name-sanity-lc page-name))
  99. db-utils/seq-flatten
  100. distinct)))
  101. (defn get-alias-source-page
  102. "return the source page (page-name) of an alias"
  103. [repo alias]
  104. (when-let [db (conn/get-db repo)]
  105. (ldb/get-alias-source-page db alias)))
  106. (defn get-files
  107. [repo]
  108. (when-let [db (conn/get-db repo)]
  109. (->> (d/q
  110. '[:find ?path ?modified-at
  111. :where
  112. [?file :file/path ?path]
  113. [(get-else $ ?file :file/last-modified-at 0) ?modified-at]]
  114. db)
  115. (seq)
  116. (reverse))))
  117. (defn get-files-blocks
  118. [repo-url paths]
  119. (let [paths (set paths)
  120. pred (fn [_db e]
  121. (contains? paths e))]
  122. (-> (d/q '[:find ?block
  123. :in $ ?pred
  124. :where
  125. [?file :file/path ?path]
  126. [(?pred $ ?path)]
  127. [?p :block/file ?file]
  128. [?block :block/page ?p]]
  129. (conn/get-db repo-url) pred)
  130. db-utils/seq-flatten)))
  131. (defn set-file-last-modified-at!
  132. "Refresh file timestamps to DB"
  133. [repo path last-modified-at]
  134. (when (and repo path last-modified-at)
  135. (db-utils/transact! repo
  136. [{:file/path path
  137. :file/last-modified-at last-modified-at}]
  138. {:skip-refresh? true})))
  139. (defn get-file-last-modified-at
  140. [repo path]
  141. (when (and repo path)
  142. (when-let [db (conn/get-db repo)]
  143. (-> (db-utils/entity db [:file/path path])
  144. :file/last-modified-at))))
  145. (defn file-exists?
  146. [repo path]
  147. (when (and repo path)
  148. (when-let [db (conn/get-db repo)]
  149. (db-utils/entity db [:file/path path]))))
  150. (defn get-files-full
  151. [repo]
  152. (when-let [db (conn/get-db repo)]
  153. (->>
  154. (d/q
  155. '[:find (pull ?file [*])
  156. :where
  157. [?file :file/path]]
  158. db)
  159. (flatten))))
  160. (defn get-file
  161. ([path]
  162. (get-file (state/get-current-repo) path))
  163. ([repo path]
  164. (when (and repo path)
  165. (when-let [db (conn/get-db repo)]
  166. (:file/content (db-utils/entity db [:file/path path]))))))
  167. (defn get-custom-css
  168. []
  169. (when-let [repo (state/get-current-repo)]
  170. (get-file repo "logseq/custom.css")))
  171. (defn get-block-by-uuid
  172. [id]
  173. (db-utils/entity [:block/uuid (if (uuid? id) id (uuid id))]))
  174. (defn query-block-by-uuid
  175. "Return block or page entity, depends on the uuid"
  176. [id]
  177. (db-utils/pull [:block/uuid (if (uuid? id) id (uuid id))]))
  178. (defn heading-content->route-name
  179. "Converts a heading block's content to its route name. This works
  180. independent of format as format specific heading characters are stripped"
  181. [block-content]
  182. (some->> block-content
  183. (re-find #"^#{0,}\s*(.*)(?:\n|$)")
  184. second
  185. string/lower-case))
  186. (defn get-block-by-page-name-and-block-route-name
  187. "Returns first block for given page name and block's route name. Block's route
  188. name must match the content of a page's block header"
  189. [repo page-name route-name]
  190. (let [db (conn/get-db repo)]
  191. (if (config/db-based-graph? repo)
  192. (->> (d/q '[:find (pull ?b [:block/uuid])
  193. :in $ ?page-name ?route-name ?content-matches
  194. :where
  195. [?page :block/name ?page-name]
  196. [?b :block/page ?page]
  197. [?b :block/properties ?prop]
  198. [?prop-b :block/name "heading"]
  199. [?prop-b :block/type "property"]
  200. [?prop-b :block/uuid ?prop-uuid]
  201. [(get ?prop ?prop-uuid) _]
  202. [?b :block/content ?content]
  203. [(?content-matches ?content ?route-name ?b)]]
  204. db
  205. page-name
  206. route-name
  207. (fn content-matches? [block-content external-content block-id]
  208. (let [block (db-utils/entity repo block-id)
  209. ref-tags (distinct (concat (:block/tags block) (:block/refs block)))]
  210. (= (-> block-content
  211. (db-content/special-id->page ref-tags)
  212. (db-content/special-id-ref->page ref-tags)
  213. heading-content->route-name)
  214. (string/lower-case external-content)))))
  215. ffirst)
  216. (->> (d/q '[:find (pull ?b [:block/uuid])
  217. :in $ ?page-name ?route-name ?content-matches
  218. :where
  219. [?page :block/name ?page-name]
  220. [?b :block/page ?page]
  221. [?b :block/properties ?prop]
  222. [(get ?prop :heading) _]
  223. [?b :block/content ?content]
  224. [(?content-matches ?content ?route-name)]]
  225. db
  226. page-name
  227. route-name
  228. (fn content-matches? [block-content external-content]
  229. (= (heading-content->route-name block-content)
  230. (string/lower-case external-content))))
  231. ffirst))))
  232. (defn get-page-format
  233. [page-name]
  234. {:post [(keyword? %)]}
  235. (keyword
  236. (or
  237. (let [page (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page-name)])]
  238. (or
  239. (:block/format page)
  240. (when-let [file (:block/file page)]
  241. (when-let [path (:file/path (db-utils/entity (:db/id file)))]
  242. (common-util/get-format path)))))
  243. (state/get-preferred-format)
  244. :markdown)))
  245. (defn page-alias-set
  246. [repo-url page]
  247. (when-let [page-id (:db/id (db-utils/entity repo-url [:block/name (util/safe-page-name-sanity-lc page)]))]
  248. (->>
  249. (d/q '[:find ?e
  250. :in $ ?page-name %
  251. :where
  252. [?page :block/name ?page-name]
  253. (alias ?page ?e)]
  254. (conn/get-db repo-url)
  255. (util/safe-page-name-sanity-lc page)
  256. (:alias rules/rules))
  257. db-utils/seq-flatten
  258. (set)
  259. (set/union #{page-id}))))
  260. (defn get-page-names-by-ids
  261. ([ids]
  262. (get-page-names-by-ids (state/get-current-repo) ids))
  263. ([repo ids]
  264. (when repo
  265. (->> (db-utils/pull-many repo '[:block/name] ids)
  266. (map :block/name)))))
  267. (defn get-page-alias-names
  268. [repo page-name]
  269. (let [alias-ids (->> (page-alias-set repo page-name)
  270. (remove nil?))]
  271. (when (seq alias-ids)
  272. (let [names (->> (get-page-names-by-ids repo alias-ids)
  273. (remove nil?)
  274. distinct
  275. (remove #(= (util/page-name-sanity-lc %) (util/page-name-sanity-lc page-name))))
  276. lookup-refs (map (fn [name]
  277. [:block/name (util/page-name-sanity-lc name)]) names)]
  278. (->> (db-utils/pull-many repo '[:block/name :block/original-name] lookup-refs)
  279. (map (fn [m]
  280. (or (:block/original-name m) (:block/name m)))))))))
  281. (defn with-pages
  282. [blocks]
  283. (let [pages-ids (->> (map (comp :db/id :block/page) blocks)
  284. (remove nil?))
  285. pages (when (seq pages-ids)
  286. (db-utils/pull-many '[:db/id :block/name :block/original-name :block/journal-day] pages-ids))
  287. pages-map (reduce (fn [acc p] (assoc acc (:db/id p) p)) {} pages)
  288. blocks (map
  289. (fn [block]
  290. (assoc block :block/page
  291. (get pages-map (:db/id (:block/page block)))))
  292. blocks)]
  293. blocks))
  294. (defn get-page-properties
  295. [page]
  296. (when-let [page (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)])]
  297. (:block/properties page)))
  298. (def sort-by-left ldb/sort-by-left)
  299. (defn sub-block
  300. [id]
  301. (when-let [repo (state/get-current-repo)]
  302. (->
  303. (react/q repo [:frontend.worker.react/block id]
  304. {:query-fn (fn [_]
  305. (let [e (db-utils/entity id)
  306. children (map :db/id (sort-by-left (:block/_parent e) e))]
  307. [e {:name (:block/name e)
  308. :original-name (:block/original-name e)
  309. :link (:block/link e)
  310. :namespace (:block/namespace e)
  311. :types (:block/type e)
  312. :schema (:block/schema e)
  313. :content (:block/content e)
  314. :marker (:block/marker e)
  315. :priority (:block/priority e)
  316. :properties (:block/properties e)
  317. :properties-values (:block/properties-text-values e)
  318. :alias (:block/alias e)
  319. :tags (:block/tags e)
  320. :children children
  321. :collapsed? (:block/collapsed? e)
  322. :collapsed-properties (:block/collapsed-properties e)
  323. :refs-count (count (:block/_refs e))}]))}
  324. nil)
  325. react
  326. first)))
  327. (defn sort-by-left-recursive
  328. [form]
  329. (walk/postwalk (fn [f]
  330. (if (and (map? f)
  331. (:block/_parent f))
  332. (let [children (:block/_parent f)]
  333. (-> f
  334. (dissoc :block/_parent)
  335. (assoc :block/children (sort-by-left children f))))
  336. f))
  337. form))
  338. ;; Diverged of get-sorted-page-block-ids
  339. (defn get-sorted-page-block-ids-and-levels
  340. "page-name: the page name, original name
  341. return: a list with elements in:
  342. :id - a list of block ids, sorted by :block/left
  343. :level - the level of the block, 1 for root, 2 for children of root, etc."
  344. [page-name]
  345. {:pre [(string? page-name)]}
  346. (let [sanitized-page (common-util/page-name-sanity-lc page-name)
  347. page-id (:db/id (db-utils/entity [:block/name sanitized-page]))
  348. root (db-utils/entity page-id)]
  349. (loop [result []
  350. children (sort-by-left (:block/_parent root) root)
  351. ;; BFS log of walking depth
  352. levels (repeat (count children) 1)]
  353. (if (seq children)
  354. (let [child (first children)
  355. cur-level (first levels)
  356. next-children (sort-by-left (:block/_parent child) child)]
  357. (recur (conj result {:id (:db/id child) :level cur-level})
  358. (concat
  359. next-children
  360. (rest children))
  361. (concat
  362. (repeat (count next-children) (inc cur-level))
  363. (rest levels))))
  364. result))))
  365. (defn has-children?
  366. ([block-id]
  367. (has-children? (conn/get-db) block-id))
  368. ([db block-id]
  369. (ldb/has-children? db block-id)))
  370. (def get-by-parent-&-left ldb/get-by-parent-&-left)
  371. (defn top-block?
  372. [block]
  373. (= (:db/id (:block/parent block))
  374. (:db/id (:block/page block))))
  375. (defn get-block-parent
  376. ([block-id]
  377. (get-block-parent (state/get-current-repo) block-id))
  378. ([repo block-id]
  379. (when-let [db (conn/get-db repo)]
  380. (when-let [block (db-utils/entity db [:block/uuid block-id])]
  381. (:block/parent block)))))
  382. (defn get-block-parents
  383. [repo block-id opts]
  384. (when-let [db (conn/get-db repo)]
  385. (ldb/get-block-parents db block-id opts)))
  386. ;; Use built-in recursive
  387. (defn get-block-parents-v2
  388. [repo block-id]
  389. (d/pull (conn/get-db repo)
  390. '[:db/id :block/collapsed? :block/properties {:block/parent ...}]
  391. [:block/uuid block-id]))
  392. (def get-block-last-direct-child-id ldb/get-block-last-direct-child-id)
  393. (defn get-block-deep-last-open-child-id
  394. [db db-id]
  395. (loop [node (db-utils/entity db db-id)]
  396. (if-let [last-child-id (get-block-last-direct-child-id db (:db/id node) true)]
  397. (let [e (db-utils/entity db last-child-id)]
  398. (if (or (:block/collapsed? e) (empty? (:block/_parent e)))
  399. last-child-id
  400. (recur e)))
  401. nil)))
  402. (def get-prev-sibling ldb/get-prev-sibling)
  403. (def get-right-sibling ldb/get-right-sibling)
  404. (defn get-next
  405. "Get next block, either its right sibling, or loop to find its next block."
  406. [db db-id & {:keys [skip-collapsed? init?]
  407. :or {skip-collapsed? true
  408. init? true}
  409. :as opts}]
  410. (when-let [entity (db-utils/entity db db-id)]
  411. (or (when-not (and (:block/collapsed? entity) skip-collapsed? init?)
  412. (get-right-sibling db db-id))
  413. (let [parent-id (:db/id (:block/parent (db-utils/entity db db-id)))]
  414. (get-next db parent-id (assoc opts :init? false))))))
  415. (defn get-prev
  416. "Get prev block, either its left sibling if the sibling is collapsed or no children,
  417. or get sibling's last deep displayable child (collaspsed parent or non-collapsed child)."
  418. [db db-id]
  419. (when-let [entity (db-utils/entity db db-id)]
  420. (or
  421. (when-let [prev-sibling (get-prev-sibling db db-id)]
  422. (if (or (:block/collapsed? prev-sibling)
  423. (empty? (:block/_parent prev-sibling)))
  424. prev-sibling
  425. (some->> (get-block-deep-last-open-child-id db (:db/id prev-sibling))
  426. (db-utils/entity db))))
  427. (let [parent (:block/parent entity)]
  428. (when-not (:block/name parent)
  429. parent)))))
  430. (defn get-page-blocks-no-cache
  431. ([page]
  432. (get-page-blocks-no-cache (state/get-current-repo) page nil))
  433. ([repo page]
  434. (get-page-blocks-no-cache repo page nil))
  435. ([repo page opts]
  436. (when-let [db (conn/get-db repo)]
  437. (ldb/get-page-blocks db page opts))))
  438. (defn get-page-blocks-count
  439. [repo page-id]
  440. (when-let [db (conn/get-db repo)]
  441. (ldb/get-page-blocks-count db page-id)))
  442. (defn page-exists?
  443. "Whether a page exists."
  444. [page-name]
  445. (let [repo (state/get-current-repo)]
  446. (when-let [db (conn/get-db repo)]
  447. (ldb/page-exists? db page-name))))
  448. (defn page-empty?
  449. "Whether a page is empty. Does it has a non-page block?
  450. `page-id` could be either a string or a db/id."
  451. [repo page-id]
  452. (when-let [db (conn/get-db repo)]
  453. (ldb/page-empty? db page-id)))
  454. (defn page-empty-or-dummy?
  455. [repo page-id]
  456. (or
  457. (page-empty? repo page-id)
  458. (when-let [db (conn/get-db repo)]
  459. (let [datoms (d/datoms db :avet :block/page page-id)]
  460. (and (= (count datoms) 1)
  461. (= "" (:block/content (db-utils/pull (:e (first datoms))))))))))
  462. (defn parents-collapsed?
  463. [repo block-uuid]
  464. (when-let [block (:block/parent (get-block-parents-v2 repo block-uuid))]
  465. (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
  466. (some util/collapsed?))))
  467. (defn get-block-page
  468. [repo block-uuid]
  469. (assert (uuid? block-uuid) (str "get-block-page requires block-uuid to be of type uuid but got " block-uuid))
  470. (when-let [block (db-utils/entity repo [:block/uuid block-uuid])]
  471. (db-utils/entity repo (:db/id (:block/page block)))))
  472. (defn get-block-immediate-children
  473. "Doesn't include nested children."
  474. [repo block-uuid]
  475. (when-let [db (conn/get-db repo)]
  476. (ldb/get-block-immediate-children db block-uuid)))
  477. (defn get-block-children
  478. "Including nested children."
  479. [repo block-uuid]
  480. (when-let [db (conn/get-db repo)]
  481. (let [ids (ldb/get-block-children-ids db block-uuid)]
  482. (when (seq ids)
  483. (let [ids' (map (fn [id] [:block/uuid id]) ids)]
  484. (db-utils/pull-many repo '[*] ids'))))))
  485. (defn get-block-and-children
  486. [repo block-uuid]
  487. (let [db (conn/get-db repo)]
  488. (ldb/get-block-and-children repo db block-uuid)))
  489. (defn get-file-page
  490. ([file-path]
  491. (get-file-page file-path true))
  492. ([file-path original-name?]
  493. (when-let [repo (state/get-current-repo)]
  494. (when-let [db (conn/get-db repo)]
  495. (some->
  496. (d/q
  497. (if original-name?
  498. '[:find ?page-name
  499. :in $ ?path
  500. :where
  501. [?file :file/path ?path]
  502. [?page :block/file ?file]
  503. [?page :block/original-name ?page-name]]
  504. '[:find ?page-name
  505. :in $ ?path
  506. :where
  507. [?file :file/path ?path]
  508. [?page :block/file ?file]
  509. [?page :block/name ?page-name]])
  510. db file-path)
  511. db-utils/seq-flatten
  512. first)))))
  513. (defn get-page-file
  514. ([page-name]
  515. (get-page-file (state/get-current-repo) page-name))
  516. ([repo page-name]
  517. (when-let [db (conn/get-db repo)]
  518. (ldb/get-page-file db page-name))))
  519. (defn get-block-file-path
  520. [block]
  521. (when-let [page-id (:db/id (:block/page block))]
  522. (:file/path (:block/file (db-utils/entity page-id)))))
  523. (defn get-file-page-id
  524. [file-path]
  525. (when-let [repo (state/get-current-repo)]
  526. (when-let [db (conn/get-db repo)]
  527. (some->
  528. (d/q
  529. '[:find ?page
  530. :in $ ?path
  531. :where
  532. [?file :file/path ?path]
  533. [?page :block/name]
  534. [?page :block/file ?file]]
  535. db file-path)
  536. db-utils/seq-flatten
  537. first))))
  538. (defn get-page
  539. [page-name]
  540. (if-let [id (if (uuid? page-name) page-name
  541. (parse-uuid page-name))]
  542. (db-utils/entity [:block/uuid id])
  543. (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])))
  544. (defn get-redirect-page-name
  545. "Given any readable page-name, return the exact page-name in db. If page
  546. doesn't exists yet, will return the passed `page-name`. Accepts both
  547. sanitized or unsanitized names.
  548. alias?: if true, alias is allowed to be returned; otherwise, it would be deref."
  549. ([page-name] (get-redirect-page-name page-name false))
  550. ([page-name alias?]
  551. (when page-name
  552. (let [page-name' (util/page-name-sanity-lc page-name)
  553. page-entity (db-utils/entity [:block/name page-name'])]
  554. (cond
  555. alias?
  556. page-name'
  557. (nil? page-entity)
  558. (if-let [journal-name (date/journal-title->custom-format page-name)]
  559. (util/page-name-sanity-lc journal-name)
  560. page-name)
  561. (page-empty-or-dummy? (state/get-current-repo) (:db/id page-entity))
  562. (let [source-page (get-alias-source-page (state/get-current-repo) page-name')]
  563. (or (when source-page (:block/name source-page))
  564. page-name'))
  565. :else
  566. page-name')))))
  567. (defn get-page-original-name
  568. [page-name]
  569. (when (string? page-name)
  570. (let [page (db-utils/pull [:block/name (util/page-name-sanity-lc page-name)])]
  571. (or (:block/original-name page)
  572. (:block/name page)))))
  573. (defn get-journals-length
  574. []
  575. (let [today (db-util/date->int (js/Date.))]
  576. (d/q '[:find (count ?page) .
  577. :in $ ?today
  578. :where
  579. [?page :block/journal? true]
  580. [?page :block/journal-day ?journal-day]
  581. [(<= ?journal-day ?today)]]
  582. (conn/get-db (state/get-current-repo))
  583. today)))
  584. (defn get-latest-journals
  585. ([n]
  586. (get-latest-journals (state/get-current-repo) n))
  587. ([repo-url n]
  588. (when (conn/get-db repo-url)
  589. (let [date (js/Date.)
  590. _ (.setDate date (- (.getDate date) (dec n)))
  591. today (db-util/date->int (js/Date.))]
  592. (->>
  593. (react/q repo-url [:frontend.worker.react/journals] {:use-cache? false}
  594. '[:find [(pull ?page [*]) ...]
  595. :in $ ?today
  596. :where
  597. [?page :block/name ?page-name]
  598. [?page :block/journal? true]
  599. [?page :block/journal-day ?journal-day]
  600. [(<= ?journal-day ?today)]]
  601. today)
  602. (react)
  603. (sort-by :block/journal-day)
  604. (reverse)
  605. (take n))))))
  606. ;; get pages that this page referenced
  607. (defn get-page-referenced-pages
  608. [repo page]
  609. (when-let [db (conn/get-db repo)]
  610. (let [page-name (util/safe-page-name-sanity-lc page)
  611. pages (page-alias-set repo page)
  612. page-id (:db/id (db-utils/entity [:block/name page-name]))
  613. ref-pages (d/q
  614. '[:find [?ref-page-name ...]
  615. :in $ ?pages
  616. :where
  617. [(untuple ?pages) [?page ...]]
  618. [?block :block/page ?page]
  619. [?block :block/refs ?ref-page]
  620. [?ref-page :block/name ?ref-page-name]]
  621. db
  622. pages)]
  623. (mapv (fn [page] [page (get-page-alias repo page)]) ref-pages))))
  624. (def ns-char "/")
  625. (def ns-re #"/")
  626. (defn- get-parents-namespace-list
  627. "Return list of parents namespace"
  628. [page-namespace & nested-found]
  629. (if (text/namespace-page? page-namespace)
  630. (let [pre-nested-vec (drop-last (string/split page-namespace ns-re))
  631. my-nested-found (if (nil? nested-found)
  632. []
  633. nested-found)]
  634. (if (= (count pre-nested-vec) 1)
  635. (conj my-nested-found (nth pre-nested-vec 0))
  636. (let [pre-nested-str (string/join ns-char pre-nested-vec)]
  637. (recur pre-nested-str (conj my-nested-found pre-nested-str)))))
  638. []))
  639. (defn- get-unnecessary-namespaces-name
  640. "Return unnecessary namespace from a list of page's name"
  641. [pages-list]
  642. (->> pages-list
  643. (remove nil?)
  644. (mapcat get-parents-namespace-list)
  645. distinct))
  646. (defn- remove-nested-namespaces-link
  647. "Remove relations between pages and their nested namespace"
  648. [pages-relations]
  649. (let [pages-relations-to-return (distinct (mapcat
  650. identity
  651. (for [item (for [a-link-from (mapv (fn [a-rel] (first a-rel)) pages-relations)]
  652. [a-link-from (mapv
  653. (fn [a-rel] (second a-rel))
  654. (filterv
  655. (fn [link-target] (= a-link-from (first link-target)))
  656. pages-relations))])
  657. :let [list-to (get item 1)
  658. page (get item 0)
  659. namespaces-to-remove (get-unnecessary-namespaces-name list-to)
  660. list-to-without-nested-ns (filterv (fn [elem] (not (some #{elem} namespaces-to-remove))) list-to)
  661. node-links (for [item-ok list-to-without-nested-ns]
  662. [page item-ok])]]
  663. (seq node-links))))]
  664. pages-relations-to-return))
  665. ;; Ignore files with empty blocks for now
  666. (defn get-pages-relation
  667. [repo with-journal?]
  668. (when-let [db (conn/get-db repo)]
  669. (let [q (if with-journal?
  670. '[:find ?page ?ref-page-name
  671. :where
  672. [?p :block/name ?page]
  673. [?block :block/page ?p]
  674. [?block :block/refs ?ref-page]
  675. [?ref-page :block/name ?ref-page-name]]
  676. '[:find ?page ?ref-page-name
  677. :where
  678. [?p :block/journal? false]
  679. [?p :block/name ?page]
  680. [?block :block/page ?p]
  681. [?block :block/refs ?ref-page]
  682. [?ref-page :block/name ?ref-page-name]])]
  683. (->>
  684. (d/q q db)
  685. (map (fn [[page ref-page-name]]
  686. [page ref-page-name]))
  687. (remove-nested-namespaces-link)))))
  688. ;; get pages who mentioned this page
  689. ;; TODO: use :block/_refs
  690. (defn get-pages-that-mentioned-page
  691. [repo page include-journals]
  692. (when (conn/get-db repo)
  693. (let [page-id (:db/id (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)]))
  694. pages (page-alias-set repo page)
  695. query-base '[:find ?mentioned-page-name
  696. :in $ ?pages ?page-name
  697. :where
  698. [?block :block/refs ?p]
  699. [(contains? ?pages ?p)]
  700. [?block :block/page ?mentioned-page]
  701. [?mentioned-page :block/name ?mentioned-page-name]]
  702. query (if include-journals
  703. query-base
  704. (conj query-base '[?mentioned-page :block/journal? false]))
  705. mentioned-pages (->> (react/q repo [:frontend.worker.react/page<-pages page-id] {:use-cache? false}
  706. query
  707. pages
  708. page)
  709. react
  710. db-utils/seq-flatten)]
  711. (mapv (fn [page] [page (get-page-alias repo page)]) mentioned-pages))))
  712. (defn get-page-referenced-blocks-full
  713. ([page]
  714. (get-page-referenced-blocks-full (state/get-current-repo) page nil))
  715. ([page options]
  716. (get-page-referenced-blocks-full (state/get-current-repo) page options))
  717. ([repo page options]
  718. (when repo
  719. (when-let [db (conn/get-db repo)]
  720. (let [page-id (:db/id (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)]))
  721. pages (page-alias-set repo page)
  722. aliases (set/difference pages #{page-id})]
  723. (->>
  724. (d/q
  725. '[:find [(pull ?block ?block-attrs) ...]
  726. :in $ [?ref-page ...] ?block-attrs
  727. :where
  728. [?block :block/path-refs ?ref-page]]
  729. db
  730. pages
  731. (butlast block-attrs))
  732. (remove (fn [block] (= page-id (:db/id (:block/page block)))))
  733. db-utils/group-by-page
  734. (map (fn [[k blocks]]
  735. (let [k (if (contains? aliases (:db/id k))
  736. (assoc k :block/alias? true)
  737. k)]
  738. [k blocks])))))))))
  739. (defn get-page-referenced-blocks
  740. ([page]
  741. (get-page-referenced-blocks (state/get-current-repo) page nil))
  742. ([page options]
  743. (get-page-referenced-blocks (state/get-current-repo) page options))
  744. ([repo page options]
  745. (when repo
  746. (when (conn/get-db repo)
  747. (let [page-id (:db/id (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)]))
  748. pages (page-alias-set repo page)
  749. aliases (set/difference pages #{page-id})]
  750. (->>
  751. (react/q repo
  752. [:frontend.worker.react/refs page-id]
  753. {:use-cache? false
  754. :query-fn (fn []
  755. (let [entities (mapcat (fn [id]
  756. (:block/_path-refs (db-utils/entity id))) pages)
  757. blocks (map (fn [e]
  758. {:block/parent (:block/parent e)
  759. :block/left (:block/left e)
  760. :block/page (:block/page e)
  761. :block/collapsed? (:block/collapsed? e)}) entities)]
  762. {:entities entities
  763. :blocks blocks}))}
  764. nil)
  765. react
  766. :entities
  767. (remove (fn [block] (= page-id (:db/id (:block/page block)))))))))))
  768. (defn get-date-scheduled-or-deadlines
  769. [journal-title]
  770. (when-let [date (date/journal-title->int journal-title)]
  771. (let [future-days (state/get-scheduled-future-days)
  772. date-format (tf/formatter "yyyyMMdd")
  773. current-day (tf/parse date-format (str date))
  774. future-day (some->> (t/plus current-day (t/days future-days))
  775. (tf/unparse date-format)
  776. (parse-long))]
  777. (when future-day
  778. (when-let [repo (state/get-current-repo)]
  779. (->> (react/q repo [:custom :scheduled-deadline journal-title]
  780. {:use-cache? false}
  781. '[:find [(pull ?block ?block-attrs) ...]
  782. :in $ ?day ?future ?block-attrs
  783. :where
  784. (or
  785. [?block :block/scheduled ?d]
  786. [?block :block/deadline ?d])
  787. [(get-else $ ?block :block/repeated? false) ?repeated]
  788. [(get-else $ ?block :block/marker "NIL") ?marker]
  789. [(not= ?marker "DONE")]
  790. [(not= ?marker "CANCELED")]
  791. [(not= ?marker "CANCELLED")]
  792. [(<= ?d ?future)]
  793. (or-join [?repeated ?d ?day]
  794. [(true? ?repeated)]
  795. [(>= ?d ?day)])]
  796. date
  797. future-day
  798. block-attrs)
  799. react
  800. (sort-by-left-recursive)
  801. db-utils/group-by-page))))))
  802. (defn- pattern [name]
  803. (re-pattern (str "(?i)(^|[^\\[#0-9a-zA-Z]|((^|[^\\[])\\[))"
  804. (util/regex-escape name)
  805. "($|[^0-9a-zA-Z])")))
  806. (defn get-page-unlinked-references
  807. [page]
  808. (when-let [repo (state/get-current-repo)]
  809. (let [page (util/safe-page-name-sanity-lc page)
  810. page-id (:db/id (db-utils/entity [:block/name page]))
  811. alias-names (get-page-alias-names repo page)
  812. patterns (->> (conj alias-names page)
  813. (map pattern))
  814. filter-fn (fn [datom]
  815. (some (fn [p]
  816. (re-find p (->> (:v datom)
  817. (drawer/remove-logbook))))
  818. patterns))]
  819. (->> (react/q repo [:frontend.worker.react/page-unlinked-refs page-id]
  820. {:query-fn (fn [db _result]
  821. (let [ids
  822. (->> (d/datoms db :aevt :block/content)
  823. (filter filter-fn)
  824. (map :e))
  825. result (db-utils/pull-many repo block-attrs ids)]
  826. (remove (fn [block] (= page-id (:db/id (:block/page block)))) result)))}
  827. nil)
  828. react
  829. (sort-by-left-recursive)
  830. db-utils/group-by-page))))
  831. (defn get-block-referenced-blocks
  832. ([block-uuid]
  833. (get-block-referenced-blocks block-uuid {}))
  834. ([block-uuid options]
  835. (when-let [repo (state/get-current-repo)]
  836. (when (conn/get-db repo)
  837. (let [block (db-utils/entity [:block/uuid block-uuid])
  838. query-result (->> (react/q repo [:frontend.worker.react/refs
  839. (:db/id block)]
  840. {}
  841. '[:find [(pull ?ref-block ?block-attrs) ...]
  842. :in $ ?block-uuid ?block-attrs
  843. :where
  844. [?block :block/uuid ?block-uuid]
  845. [?ref-block :block/refs ?block]]
  846. block-uuid
  847. block-attrs)
  848. react
  849. (sort-by-left-recursive))]
  850. (db-utils/group-by-page query-result))))))
  851. (defn journal-page?
  852. "sanitized page-name only"
  853. [page-name]
  854. (:block/journal? (db-utils/entity [:block/name page-name])))
  855. (defn get-block-property-values
  856. "Get blocks which have this property."
  857. [property-uuid]
  858. (ldb/get-block-property-values (conn/get-db) property-uuid))
  859. (defn get-classes-with-property
  860. "Get classes which have given property as a class property"
  861. [property-uuid]
  862. (ldb/get-classes-with-property (conn/get-db) property-uuid))
  863. (defn get-template-by-name
  864. [name]
  865. (when (string? name)
  866. (->> (d/q
  867. '[:find [(pull ?b [*]) ...]
  868. :in $ ?name
  869. :where
  870. [?b :block/properties ?p]
  871. [(get ?p :template) ?t]
  872. [(= ?t ?name)]]
  873. (conn/get-db)
  874. name)
  875. (sort-by :block/name)
  876. (first))))
  877. (defn get-all-referenced-blocks-uuid
  878. "Get all uuids of blocks with any back link exists."
  879. []
  880. (when-let [db (conn/get-db)]
  881. (d/q '[:find [?refed-uuid ...]
  882. :where
  883. ;; ?referee-b is block with ref towards ?refed-b
  884. [?refed-b :block/uuid ?refed-uuid]
  885. [?referee-b :block/refs ?refed-b]] db)))
  886. (defn delete-blocks
  887. [repo-url files _delete-page?]
  888. (when (seq files)
  889. (let [blocks (get-files-blocks repo-url files)]
  890. (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
  891. (defn delete-files
  892. [files]
  893. (mapv (fn [path] [:db.fn/retractEntity [:file/path path]]) files))
  894. (defn delete-pages-by-files
  895. [files]
  896. (let [pages (->> (mapv get-file-page files)
  897. (remove nil?))]
  898. (when (seq pages)
  899. (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
  900. (defn set-file-content!
  901. ([repo path content]
  902. (set-file-content! repo path content {}))
  903. ([repo path content opts]
  904. (when (and repo path)
  905. (let [tx-data {:file/path path
  906. :file/content content}]
  907. (db-utils/transact! repo [tx-data] (merge opts {:skip-refresh? true}))))))
  908. (defn get-pre-block
  909. [repo page-id]
  910. (-> (d/q '[:find (pull ?b [*])
  911. :in $ ?page
  912. :where
  913. [?b :block/page ?page]
  914. [?b :block/pre-block? true]]
  915. (conn/get-db repo)
  916. page-id)
  917. ffirst))
  918. (defn get-namespace-pages
  919. "Accepts both sanitized and unsanitized namespaces"
  920. [repo namespace]
  921. (ldb/get-namespace-pages (conn/get-db repo) namespace {:db-graph? (config/db-based-graph? repo)}))
  922. (defn- tree [flat-col root]
  923. (let [sort-fn #(sort-by :block/name %)
  924. children (group-by :block/namespace flat-col)
  925. namespace-children (fn namespace-children [parent-id]
  926. (map (fn [m]
  927. (assoc m :namespace/children
  928. (sort-fn (namespace-children {:db/id (:db/id m)}))))
  929. (sort-fn (get children parent-id))))]
  930. (namespace-children root)))
  931. (defn get-namespace-hierarchy
  932. "Unsanitized namespaces"
  933. [repo namespace]
  934. (let [children (get-namespace-pages repo namespace)
  935. namespace-id (:db/id (db-utils/entity [:block/name (util/page-name-sanity-lc namespace)]))
  936. root {:db/id namespace-id}
  937. col (conj children root)]
  938. (tree col root)))
  939. (defn get-page-namespace
  940. [repo page]
  941. (:block/namespace (db-utils/entity repo [:block/name (util/page-name-sanity-lc page)])))
  942. (defn get-page-namespace-routes
  943. [repo page]
  944. (assert (string? page))
  945. (when-let [db (conn/get-db repo)]
  946. (when-not (string/blank? page)
  947. (let [page (util/page-name-sanity-lc (string/trim page))
  948. page-exist? (db-utils/entity repo [:block/name page])
  949. ids (if page-exist?
  950. '()
  951. (->> (d/datoms db :aevt :block/name)
  952. (filter (fn [datom]
  953. (string/ends-with? (:v datom) (str "/" page))))
  954. (map :e)))]
  955. (when (seq ids)
  956. (db-utils/pull-many repo
  957. '[:db/id :block/name :block/original-name
  958. {:block/file [:db/id :file/path]}]
  959. ids))))))
  960. (defn whiteboard-page?
  961. "Given a page name or a page object, check if it is a whiteboard page"
  962. [page]
  963. (ldb/whiteboard-page? (conn/get-db) page))
  964. (defn get-orphaned-pages
  965. [opts]
  966. (let [db (conn/get-db)]
  967. (ldb/get-orphaned-pages db opts)))
  968. ;; FIXME: replace :logseq.macro-name with id
  969. (defn get-macro-blocks
  970. [repo macro-name]
  971. (d/q
  972. '[:find [(pull ?b [*]) ...]
  973. :in $ ?macro-name
  974. :where
  975. [?b :block/type "macro"]
  976. [?b :block/properties ?properties]
  977. [(get ?properties :logseq.macro-name) ?name]
  978. [(= ?name ?macro-name)]]
  979. (conn/get-db repo)
  980. macro-name))
  981. (defn- block-or-page
  982. [page-name-or-uuid]
  983. (let [entity (get-page (str page-name-or-uuid))]
  984. (if-not (some? (:block/name entity)) :block :page)))
  985. (defn page?
  986. [page-name-or-uuid]
  987. (= :page (block-or-page page-name-or-uuid)))
  988. (defn untitled-page?
  989. [page-name]
  990. (when-let [entity (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])]
  991. (some? (parse-uuid page-name))))
  992. (defn get-all-whiteboards
  993. [repo]
  994. (d/q
  995. '[:find [(pull ?page [:block/name
  996. :block/original-name
  997. :block/created-at
  998. :block/updated-at]) ...]
  999. :where
  1000. [?page :block/name]
  1001. [?page :block/type "whiteboard"]]
  1002. (conn/get-db repo)))
  1003. (defn get-whiteboard-id-nonces
  1004. [repo page-name]
  1005. (let [key (if (config/db-based-graph? repo)
  1006. (:block/uuid (db-utils/entity [:block/name "logseq.tldraw.shape"]))
  1007. :logseq.tldraw.shape)
  1008. page (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])]
  1009. (->> (:block/_page page)
  1010. (keep (fn [{:block/keys [uuid properties]}]
  1011. (when-let [shape (get properties key)]
  1012. {:id (str uuid)
  1013. :nonce (:nonce shape)}))))))
  1014. (defn get-all-classes
  1015. [repo]
  1016. (d/q
  1017. '[:find ?name ?id
  1018. :where
  1019. [?page :block/type ?t]
  1020. [(= ?t "class")]
  1021. [?page :block/original-name ?name]
  1022. [?page :block/uuid ?id]]
  1023. (conn/get-db repo)))
  1024. (defn get-namespace-children
  1025. [repo eid]
  1026. (->>
  1027. (d/q '[:find [?children ...]
  1028. :in $ ?parent %
  1029. :where
  1030. (namespace ?parent ?children)]
  1031. (conn/get-db repo)
  1032. eid
  1033. (:namespace rules/rules))
  1034. distinct))
  1035. (defn get-class-objects
  1036. [repo class-id]
  1037. (when-let [class (db-utils/entity repo class-id)]
  1038. (if (first (:block/_namespace class)) ; has children classes
  1039. (d/q
  1040. '[:find [?object ...]
  1041. :in $ % ?parent
  1042. :where
  1043. (namespace ?parent ?c)
  1044. (or-join [?object ?c]
  1045. [?object :block/tags ?parent]
  1046. [?object :block/tags ?c])]
  1047. (conn/get-db repo)
  1048. (:namespace rules/rules)
  1049. class-id)
  1050. (map :db/id (:block/_tags class)))))
  1051. (comment
  1052. ;; For debugging
  1053. (defn get-all-blocks
  1054. []
  1055. (let [repo (state/get-current-repo)]
  1056. (d/q
  1057. '[:find [(pull ?b [*]) ...]
  1058. :where
  1059. [?b :block/uuid]]
  1060. (conn/get-db repo))))
  1061. )