diff --git a/deps/outliner/src/logseq/outliner/core.cljs b/deps/outliner/src/logseq/outliner/core.cljs index 24e5bfb1d6..453c823bcf 100644 --- a/deps/outliner/src/logseq/outliner/core.cljs +++ b/deps/outliner/src/logseq/outliner/core.cljs @@ -11,6 +11,7 @@ [logseq.db :as ldb] [logseq.db.common.order :as db-order] [logseq.db.frontend.class :as db-class] + [logseq.db.frontend.malli-schema :as db-malli-schema] [logseq.db.frontend.schema :as db-schema] [logseq.db.sqlite.create-graph :as sqlite-create-graph] [logseq.outliner.datascript :as ds] @@ -836,15 +837,18 @@ (let [top-level-blocks (filter-top-level-blocks db blocks) non-consecutive? (and (> (count top-level-blocks) 1) (seq (ldb/get-non-consecutive-blocks db top-level-blocks))) top-level-blocks* (get-top-level-blocks top-level-blocks non-consecutive?) - top-level-blocks (remove :logseq.property/built-in? top-level-blocks*) + undeletable? (fn [b] (or (:logseq.property/built-in? b) + (:file/path b) + (some-> (:db/ident b) db-malli-schema/internal-ident?))) + top-level-blocks (remove undeletable? top-level-blocks*) txs-state (ds/new-outliner-txs-state) block-ids (map (fn [b] [:block/uuid (:block/uuid b)]) top-level-blocks) start-block (first top-level-blocks) end-block (last top-level-blocks) delete-one-block? (or (= 1 (count top-level-blocks)) (= start-block end-block))] - ;; Validate before `when` since top-level-blocks will be empty when deleting one built-in block - (when (seq (filter :logseq.property/built-in? top-level-blocks*)) + ;; Validate before `when` since top-level-blocks will be empty when deleting one built-in/internal block + (when (seq (filter undeletable? top-level-blocks*)) (throw (ex-info "Built-in nodes can't be deleted" {:type :notification :payload {:message "Built-in nodes can't be deleted" diff --git a/deps/outliner/test/logseq/outliner/core_test.cljs b/deps/outliner/test/logseq/outliner/core_test.cljs index 276f144829..97f657db93 100644 --- a/deps/outliner/test/logseq/outliner/core_test.cljs +++ b/deps/outliner/test/logseq/outliner/core_test.cljs @@ -3,7 +3,8 @@ [datascript.core :as d] [logseq.db :as ldb] [logseq.db.test.helper :as db-test] - [logseq.outliner.core :as outliner-core])) + [logseq.outliner.core :as outliner-core] + [logseq.common.config :as common-config])) (deftest test-delete-block-with-default-property (testing "Delete block with default property hard retracts the block subtree" @@ -50,3 +51,32 @@ child' (db-test/find-block-by-content @conn "child")] (is (nil? parent')) (is (nil? child'))))) + +(deftest delete-blocks-rejects-built-in-entities + (let [conn (db-test/create-conn)] + (testing "built-in page is rejected" + (let [recycle-page (ldb/get-page @conn common-config/recycle-page-name)] + (is (true? (:logseq.property/built-in? recycle-page))) + (is (thrown-with-msg? js/Error #"Built-in nodes can't be deleted" + (db-test/silence-stderr (outliner-core/delete-blocks! conn [recycle-page] {})))))) + + (testing "built-in idents that are not a class or property like empty-placeholder are rejected" + (let [placeholder (d/entity @conn :logseq.property/empty-placeholder)] + (is (some? (:block/uuid placeholder))) + (is (thrown-with-msg? js/Error #"Built-in nodes can't be deleted" + (db-test/silence-stderr (outliner-core/delete-blocks! conn [placeholder] {})))))) + + (testing "file entity is rejected" + (let [file (->> (d/datoms @conn :avet :file/path) + first + :e + (d/entity @conn))] + (is (some? (:file/path file))) + (is (thrown-with-msg? js/Error #"Built-in nodes can't be deleted" + (db-test/silence-stderr (outliner-core/delete-blocks! conn [file] {})))))) + + (testing "KV entity is rejected" + (let [kv (d/entity @conn :logseq.kv/db-type)] + (is (some? (:db/id kv))) + (is (thrown-with-msg? js/Error #"Built-in nodes can't be deleted" + (db-test/silence-stderr (outliner-core/delete-blocks! conn [kv] {}))))))))