mirror of
https://github.com/logseq/logseq.git
synced 2026-04-25 06:35:02 +00:00
refactor: move db graph page operations to outliner dep
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
(ns frontend.worker.handler.page.db-based.page
|
||||
"Page operations for DB graphs"
|
||||
(ns logseq.outliner.page
|
||||
"Page-related fns"
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
@@ -10,6 +10,7 @@
|
||||
[logseq.db.common.entity-plus :as entity-plus]
|
||||
[logseq.db.common.order :as db-order]
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
[logseq.db.frontend.content :as db-content]
|
||||
[logseq.db.frontend.entity-util :as entity-util]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
@@ -18,6 +19,68 @@
|
||||
[logseq.graph-parser.text :as text]
|
||||
[logseq.outliner.validate :as outliner-validate]))
|
||||
|
||||
(defn db-refs->page
|
||||
"Replace [[page name]] with page name"
|
||||
[page-entity]
|
||||
(let [refs (:block/_refs page-entity)
|
||||
id-ref->page #(db-content/content-id-ref->page % [page-entity])]
|
||||
(when (seq refs)
|
||||
(let [tx-data (mapcat (fn [{:block/keys [raw-title] :as ref}]
|
||||
;; block content
|
||||
(let [content' (id-ref->page raw-title)
|
||||
content-tx (when (not= raw-title content')
|
||||
{:db/id (:db/id ref)
|
||||
:block/title content'})
|
||||
tx content-tx]
|
||||
(concat
|
||||
[[:db/retract (:db/id ref) :block/refs (:db/id page-entity)]]
|
||||
(when tx [tx])))) refs)]
|
||||
tx-data))))
|
||||
|
||||
(defn delete!
|
||||
"Deletes a page. Returns true if able to delete page. If unable to delete,
|
||||
calls error-handler fn and returns false"
|
||||
[conn page-uuid & {:keys [persist-op? rename? error-handler]
|
||||
:or {persist-op? true
|
||||
error-handler (fn [{:keys [msg]}] (js/console.error msg))}}]
|
||||
(assert (uuid? page-uuid) (str "frontend.worker.handler.page/delete! srong page-uuid: " (if page-uuid page-uuid "nil")))
|
||||
(when page-uuid
|
||||
(when-let [page (d/entity @conn [:block/uuid page-uuid])]
|
||||
(let [blocks (:block/_page page)
|
||||
truncate-blocks-tx-data (mapv
|
||||
(fn [block]
|
||||
[:db.fn/retractEntity [:block/uuid (:block/uuid block)]])
|
||||
blocks)]
|
||||
;; TODO: maybe we should add $$$favorites to built-in pages?
|
||||
(if (or (ldb/built-in? page) (ldb/hidden? page))
|
||||
(do
|
||||
(error-handler {:msg "Built-in page cannot be deleted"})
|
||||
false)
|
||||
(let [delete-property-tx (when (ldb/property? page)
|
||||
(concat
|
||||
(let [datoms (d/datoms @conn :avet (:db/ident page))]
|
||||
(map (fn [d] [:db/retract (:e d) (:a d)]) datoms))
|
||||
(map (fn [d] [:db/retractEntity (:e d)])
|
||||
(d/datoms @conn :avet :logseq.property.history/property (:db/ident page)))))
|
||||
delete-page-tx (concat (db-refs->page page)
|
||||
delete-property-tx
|
||||
[[:db.fn/retractEntity (:db/id page)]])
|
||||
restore-class-parent-tx (->> (filter ldb/class? (:logseq.property.class/_extends page))
|
||||
(map (fn [p]
|
||||
{:db/id (:db/id p)
|
||||
:logseq.property.class/extends :logseq.class/Root})))
|
||||
tx-data (concat truncate-blocks-tx-data
|
||||
restore-class-parent-tx
|
||||
delete-page-tx)]
|
||||
|
||||
(ldb/transact! conn tx-data
|
||||
(cond-> {:outliner-op :delete-page
|
||||
:deleted-page (str (:block/uuid page))
|
||||
:persist-op? persist-op?}
|
||||
rename?
|
||||
(assoc :real-outliner-op :rename-page)))
|
||||
true))))))
|
||||
|
||||
(defn- build-page-tx [db properties page {:keys [whiteboard? class? tags]}]
|
||||
(when (:block/uuid page)
|
||||
(let [type-tag (cond class? :logseq.class/Tag
|
||||
21
deps/outliner/src/logseq/outliner/property.cljs
vendored
21
deps/outliner/src/logseq/outliner/property.cljs
vendored
@@ -307,13 +307,24 @@
|
||||
a property ref value for a string value if necessary"
|
||||
[conn property-id v property-type]
|
||||
(let [number-property? (= property-type :number)]
|
||||
(if (and (integer? v)
|
||||
(or (not number-property?)
|
||||
(cond
|
||||
(and (integer? v)
|
||||
(or (not number-property?)
|
||||
;; Allows :number property to use number as a ref (for closed value) or value
|
||||
(and number-property?
|
||||
(or (= property-id (:db/ident (:logseq.property/created-from-property (d/entity @conn v))))
|
||||
(= :logseq.property/empty-placeholder (:db/ident (d/entity @conn v)))))))
|
||||
(and number-property?
|
||||
(or (= property-id (:db/ident (:logseq.property/created-from-property (d/entity @conn v))))
|
||||
(= :logseq.property/empty-placeholder (:db/ident (d/entity @conn v)))))))
|
||||
v
|
||||
|
||||
(= property-type :page)
|
||||
(if (or (string/blank? v) (not (string? v)))
|
||||
(throw (ex-info "Value should be non-empty string" {:property-id property-id
|
||||
:property-type property-type
|
||||
:v v}))
|
||||
|
||||
;; TODO: create page
|
||||
nil)
|
||||
:else
|
||||
;; only value-ref-property types should call this
|
||||
(when-let [v' (if (and number-property? (string? v))
|
||||
(parse-double v)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
(ns frontend.worker.handler.page.db-based.page-test
|
||||
(ns logseq.outliner.page-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[datascript.core :as d]
|
||||
[frontend.worker.handler.page.db-based.page :as worker-db-page]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.db :as db-db]
|
||||
[logseq.db.test.helper :as db-test]))
|
||||
[logseq.db.test.helper :as db-test]
|
||||
[logseq.outliner.page :as outliner-page]))
|
||||
|
||||
(deftest create-class
|
||||
(let [conn (db-test/create-conn)
|
||||
_ (worker-db-page/create! conn "movie" {:class? true})
|
||||
_ (worker-db-page/create! conn "Movie" {:class? true})
|
||||
_ (outliner-page/create! conn "movie" {:class? true})
|
||||
_ (outliner-page/create! conn "Movie" {:class? true})
|
||||
movie-class (ldb/get-case-page @conn "movie")
|
||||
Movie-class (ldb/get-case-page @conn "Movie")]
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
:pages-and-blocks [{:page {:block/title "page1"}}]})]
|
||||
|
||||
(testing "Basic valid workflows"
|
||||
(let [[_ child-uuid] (worker-db-page/create! conn "foo/bar/baz" {:split-namespace? true})
|
||||
(let [[_ child-uuid] (outliner-page/create! conn "foo/bar/baz" {:split-namespace? true})
|
||||
child-page (d/entity @conn [:block/uuid child-uuid])
|
||||
;; Create a 2nd child page using existing parent pages
|
||||
[_ child-uuid2] (worker-db-page/create! conn "foo/bar/baz2" {:split-namespace? true})
|
||||
[_ child-uuid2] (outliner-page/create! conn "foo/bar/baz2" {:split-namespace? true})
|
||||
child-page2 (d/entity @conn [:block/uuid child-uuid2])
|
||||
;; Create a child page for a class
|
||||
[_ child-uuid3] (worker-db-page/create! conn "c1/c2" {:split-namespace? true :class? true})
|
||||
[_ child-uuid3] (outliner-page/create! conn "c1/c2" {:split-namespace? true :class? true})
|
||||
child-page3 (d/entity @conn [:block/uuid child-uuid3])
|
||||
library (ldb/get-built-in-page @conn common-config/library-page-name)
|
||||
bar (ldb/get-page @conn "bar")]
|
||||
@@ -48,7 +48,7 @@
|
||||
(is (= #{"Root Tag" "c1"} (set (map :block/title (ldb/get-classes-parents [child-page3]))))
|
||||
"Child class with new parent has correct parents")
|
||||
|
||||
(worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true})
|
||||
(outliner-page/create! conn "foo/class1/baz3" {:split-namespace? true})
|
||||
(is (= #{"Tag" "Page"}
|
||||
(set (d/q '[:find [?tag-title ...]
|
||||
:where
|
||||
@@ -58,8 +58,8 @@
|
||||
"Using an existing class page in a multi-parent namespace doesn't allow a page to have a class parent and instead creates a new page")))
|
||||
|
||||
(testing "Child pages with same name and different parents"
|
||||
(let [_ (worker-db-page/create! conn "vim/keys" {:split-namespace? true})
|
||||
_ (worker-db-page/create! conn "emacs/keys" {:split-namespace? true})]
|
||||
(let [_ (outliner-page/create! conn "vim/keys" {:split-namespace? true})
|
||||
_ (outliner-page/create! conn "emacs/keys" {:split-namespace? true})]
|
||||
(is (= #{"vim" "emacs"}
|
||||
(->> (d/q '[:find [(pull ?b [{:block/parent [:block/title]}]) ...] :where [?b :block/title "keys"]] @conn)
|
||||
(map #(get-in % [:block/parent :block/title]))
|
||||
@@ -70,34 +70,34 @@
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"Cannot create"
|
||||
(worker-db-page/create! conn "class1/page" {:split-namespace? true}))
|
||||
(outliner-page/create! conn "class1/page" {:split-namespace? true}))
|
||||
"Page can't have a class parent")
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"Cannot create"
|
||||
(worker-db-page/create! conn "property1/page" {:split-namespace? true}))
|
||||
(outliner-page/create! conn "property1/page" {:split-namespace? true}))
|
||||
"Page can't have a property parent")
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"Cannot create"
|
||||
(worker-db-page/create! conn "property1/class" {:split-namespace? true :class? true}))
|
||||
(outliner-page/create! conn "property1/class" {:split-namespace? true :class? true}))
|
||||
"Class can't have a property parent"))))
|
||||
|
||||
(deftest create-page
|
||||
(let [conn (db-test/create-conn)
|
||||
[_ page-uuid] (worker-db-page/create! conn "fooz" {})]
|
||||
[_ page-uuid] (outliner-page/create! conn "fooz" {})]
|
||||
(is (= "fooz" (:block/title (d/entity @conn [:block/uuid page-uuid])))
|
||||
"Page created correctly")
|
||||
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"can't include \"/"
|
||||
(worker-db-page/create! conn "foo/bar" {}))
|
||||
(outliner-page/create! conn "foo/bar" {}))
|
||||
"Page can't have '/'n title")))
|
||||
|
||||
(deftest create-journal
|
||||
(let [conn (db-test/create-conn)
|
||||
[_ page-uuid] (worker-db-page/create! conn "Dec 16th, 2024" {})]
|
||||
[_ page-uuid] (outliner-page/create! conn "Dec 16th, 2024" {})]
|
||||
|
||||
(is (= "Dec 16th, 2024" (:block/title (d/entity @conn [:block/uuid page-uuid])))
|
||||
"Journal created correctly")
|
||||
@@ -44,8 +44,7 @@
|
||||
"DB graph paths with :block/name"
|
||||
["deps/db/src/logseq/db/frontend"
|
||||
"deps/db/src/logseq/db/sqlite"
|
||||
"deps/outliner/src/logseq/outliner/property.cljs"
|
||||
"src/main/frontend/worker/handler/page/db_based"])
|
||||
"deps/outliner/src/logseq/outliner/property.cljs"])
|
||||
|
||||
(def db-graph-paths
|
||||
"Paths _only_ for DB graphs"
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
(:require [cljs-time.coerce :as tc]
|
||||
[cljs-time.core :as t]
|
||||
[datascript.core :as d]
|
||||
[frontend.worker.handler.page.db-based.page :as worker-db-page]
|
||||
[logseq.common.util.date-time :as date-time-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.frontend.property.build :as db-property-build]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.outliner.page :as outliner-page]
|
||||
[logseq.outliner.pipeline :as outliner-pipeline]))
|
||||
|
||||
;; TODO: allow users to add command or configure it through #Command (which parent should be #Code)
|
||||
@@ -172,7 +172,7 @@
|
||||
{:page-uuid (:block/uuid (d/entity db journal-day))}
|
||||
(let [formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal))
|
||||
title (date-time-util/format (t/to-default-time-zone (tc/to-date-time next-time-long)) formatter)]
|
||||
(worker-db-page/create db title {})))
|
||||
(outliner-page/create db title {})))
|
||||
value (if date? [:block/uuid page-uuid] next-time-long)]
|
||||
(concat
|
||||
tx-data
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
(ns frontend.worker.handler.page
|
||||
"Page operations"
|
||||
(:require [datascript.core :as d]
|
||||
[frontend.worker.handler.page.db-based.page :as db-worker-page]
|
||||
(:require [frontend.worker.handler.page.file-based.delete :as file-worker-page-delete]
|
||||
[frontend.worker.handler.page.file-based.page :as file-worker-page]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.content :as db-content]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[logseq.graph-parser.db :as gp-db]))
|
||||
[logseq.outliner.page :as outliner-page]))
|
||||
|
||||
(defn rtc-create-page!
|
||||
[conn config title {:keys [uuid old-db-id]}]
|
||||
(assert (uuid? uuid) (str "rtc-create-page! `uuid` is not a uuid " uuid))
|
||||
(let [date-formatter (common-config/get-date-formatter config)
|
||||
title (db-worker-page/sanitize-title title)
|
||||
title (outliner-page/sanitize-title title)
|
||||
page-name (common-util/page-name-sanity-lc title)
|
||||
page (cond-> (gp-block/page-name->map title @conn true date-formatter
|
||||
{:page-uuid uuid
|
||||
@@ -39,79 +36,13 @@
|
||||
TODO: Add other options"
|
||||
[repo conn config title & {:as options}]
|
||||
(if (ldb/db-based-graph? @conn)
|
||||
(db-worker-page/create! conn title options)
|
||||
(outliner-page/create! conn title options)
|
||||
(file-worker-page/create! repo conn config title options)))
|
||||
|
||||
(defn db-refs->page
|
||||
"Replace [[page name]] with page name"
|
||||
[repo page-entity]
|
||||
(when (sqlite-util/db-based-graph? repo)
|
||||
(let [refs (:block/_refs page-entity)
|
||||
id-ref->page #(db-content/content-id-ref->page % [page-entity])]
|
||||
(when (seq refs)
|
||||
(let [tx-data (mapcat (fn [{:block/keys [raw-title] :as ref}]
|
||||
;; block content
|
||||
(let [content' (id-ref->page raw-title)
|
||||
content-tx (when (not= raw-title content')
|
||||
{:db/id (:db/id ref)
|
||||
:block/title content'})
|
||||
tx content-tx]
|
||||
(concat
|
||||
[[:db/retract (:db/id ref) :block/refs (:db/id page-entity)]]
|
||||
(when tx [tx])))) refs)]
|
||||
tx-data)))))
|
||||
|
||||
(defn delete!
|
||||
"Deletes a page. Returns true if able to delete page. If unable to delete,
|
||||
calls error-handler fn and returns false"
|
||||
[repo conn page-uuid & {:keys [persist-op? rename? error-handler]
|
||||
:or {persist-op? true
|
||||
error-handler (fn [{:keys [msg]}] (js/console.error msg))}}]
|
||||
(assert (uuid? page-uuid) (str "frontend.worker.handler.page/delete! srong page-uuid: " (if page-uuid page-uuid "nil")))
|
||||
(when (and repo page-uuid)
|
||||
(when-let [page (d/entity @conn [:block/uuid page-uuid])]
|
||||
(let [page-name (:block/name page)
|
||||
blocks (:block/_page page)
|
||||
truncate-blocks-tx-data (mapv
|
||||
(fn [block]
|
||||
[:db.fn/retractEntity [:block/uuid (:block/uuid block)]])
|
||||
blocks)
|
||||
db-based? (sqlite-util/db-based-graph? repo)]
|
||||
;; TODO: maybe we should add $$$favorites to built-in pages?
|
||||
(if (or (ldb/built-in? page) (ldb/hidden? page))
|
||||
(do
|
||||
(error-handler {:msg "Built-in page cannot be deleted"})
|
||||
false)
|
||||
(let [db @conn
|
||||
file (when-not db-based? (gp-db/get-page-file db page-name))
|
||||
file-path (:file/path file)
|
||||
delete-file-tx (when file
|
||||
[[:db.fn/retractEntity [:file/path file-path]]])
|
||||
delete-property-tx (when (ldb/property? page)
|
||||
(concat
|
||||
(let [datoms (d/datoms @conn :avet (:db/ident page))]
|
||||
(map (fn [d] [:db/retract (:e d) (:a d)]) datoms))
|
||||
(map (fn [d] [:db/retractEntity (:e d)])
|
||||
(d/datoms @conn :avet :logseq.property.history/property (:db/ident page)))))
|
||||
delete-page-tx (concat (db-refs->page repo page)
|
||||
delete-property-tx
|
||||
[[:db.fn/retractEntity (:db/id page)]])
|
||||
restore-class-parent-tx (when db-based?
|
||||
(->> (filter ldb/class? (:logseq.property.class/_extends page))
|
||||
(map (fn [p]
|
||||
{:db/id (:db/id p)
|
||||
:logseq.property.class/extends :logseq.class/Root}))))
|
||||
tx-data (concat truncate-blocks-tx-data
|
||||
restore-class-parent-tx
|
||||
delete-page-tx
|
||||
delete-file-tx)]
|
||||
|
||||
(ldb/transact! conn tx-data
|
||||
(cond-> {:outliner-op :delete-page
|
||||
:deleted-page (str (:block/uuid page))
|
||||
:persist-op? persist-op?}
|
||||
rename?
|
||||
(assoc :real-outliner-op :rename-page)
|
||||
file-path
|
||||
(assoc :file-path file-path)))
|
||||
true))))))
|
||||
[repo conn page-uuid & {:as options}]
|
||||
(if (ldb/db-based-graph? @conn)
|
||||
(outliner-page/delete! conn page-uuid options)
|
||||
(file-worker-page-delete/delete! repo conn page-uuid options)))
|
||||
|
||||
39
src/main/frontend/worker/handler/page/file_based/delete.cljs
Normal file
39
src/main/frontend/worker/handler/page/file_based/delete.cljs
Normal file
@@ -0,0 +1,39 @@
|
||||
(ns frontend.worker.handler.page.file-based.delete
|
||||
"File graph page delete"
|
||||
(:require [datascript.core :as d]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.graph-parser.db :as gp-db]))
|
||||
|
||||
(defn delete!
|
||||
"Deletes a page. Returns true if able to delete page. If unable to delete,
|
||||
calls error-handler fn and returns false"
|
||||
[repo conn page-uuid & {:keys [persist-op? rename?]
|
||||
:or {persist-op? true}}]
|
||||
(assert (uuid? page-uuid) (str "frontend.worker.handler.page/delete! requires page-uuid: " (if page-uuid page-uuid "nil")))
|
||||
(when (and repo page-uuid)
|
||||
(when-let [page (d/entity @conn [:block/uuid page-uuid])]
|
||||
(let [page-name (:block/name page)
|
||||
blocks (:block/_page page)
|
||||
truncate-blocks-tx-data (mapv
|
||||
(fn [block]
|
||||
[:db.fn/retractEntity [:block/uuid (:block/uuid block)]])
|
||||
blocks)
|
||||
db @conn
|
||||
file (gp-db/get-page-file db page-name)
|
||||
file-path (:file/path file)
|
||||
delete-file-tx (when file
|
||||
[[:db.fn/retractEntity [:file/path file-path]]])
|
||||
delete-page-tx [[:db.fn/retractEntity (:db/id page)]]
|
||||
tx-data (concat truncate-blocks-tx-data
|
||||
delete-page-tx
|
||||
delete-file-tx)]
|
||||
|
||||
(ldb/transact! conn tx-data
|
||||
(cond-> {:outliner-op :delete-page
|
||||
:deleted-page (str (:block/uuid page))
|
||||
:persist-op? persist-op?}
|
||||
rename?
|
||||
(assoc :real-outliner-op :rename-page)
|
||||
file-path
|
||||
(assoc :file-path file-path)))
|
||||
true))))
|
||||
@@ -760,7 +760,7 @@
|
||||
:properties (when (not db-base?)
|
||||
(merge properties
|
||||
(when custom-uuid {:id custom-uuid})))})
|
||||
_ (when (and db-base? (some? properties))
|
||||
_ (when (and db-base? (seq properties))
|
||||
(api-block/save-db-based-block-properties! new-block properties this))]
|
||||
(bean/->js (sdk-utils/normalize-keyword-for-json new-block)))))))
|
||||
|
||||
@@ -800,7 +800,7 @@
|
||||
opts (bean/->clj opts)]
|
||||
(when block
|
||||
(p/do!
|
||||
(when (and db-base? (some? (:properties opts)))
|
||||
(when (and db-base? (seq (:properties opts)))
|
||||
(api-block/save-db-based-block-properties! block (:properties opts)))
|
||||
(editor-handler/save-block! repo
|
||||
(sdk-utils/uuid-or-throw-error block-uuid) content
|
||||
@@ -947,8 +947,7 @@
|
||||
value (bean/->clj value)]
|
||||
(when block
|
||||
(if db-base?
|
||||
(p/do!
|
||||
(api-block/save-db-based-block-properties! block {key' value} this))
|
||||
(api-block/save-db-based-block-properties! block {key' value} this)
|
||||
(property-handler/set-block-property! repo block-uuid key' value))))))
|
||||
|
||||
(defn ^:export remove_block_property
|
||||
|
||||
Reference in New Issue
Block a user