From d130d72579e84f77ba90bc1cbed05573df550593 Mon Sep 17 00:00:00 2001 From: megayu Date: Thu, 21 May 2026 12:04:02 +0800 Subject: [PATCH] Fix external asset rendering and journal import namespace handling (#12673) * fix render external asset fail * fix: handle nil stat in exteranal asset size calculation * fix: normalize journal UUIDs and prevent namespace creation for slash-formatted journals * fix: update journal UUID generation and prevent namespace creation for slash-formatted journals * fix(import): avoid namespace pages for slash journal refs * fix: clarify journal uuid option docs Agent-Logs-Url: https://github.com/logseq/logseq/sessions/d8292bbe-fc6f-4c74-91cd-5705571a89b2 Co-authored-by: tiensonqin <479169+tiensonqin@users.noreply.github.com> --------- Co-authored-by: Tienson Qin Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tiensonqin <479169+tiensonqin@users.noreply.github.com> --- .../src/logseq/graph_parser/block.cljs | 5 +- .../src/logseq/graph_parser/exporter.cljs | 80 +++++++++++++++-- .../src/logseq/graph_parser/extract.cljc | 20 +++-- .../test/logseq/graph_parser/block_test.cljs | 14 ++- .../logseq/graph_parser/exporter_test.cljs | 88 +++++++++++++++++++ .../journals/2026_01_27.md | 5 +- deps/outliner/src/logseq/outliner/page.cljs | 14 ++- .../test/logseq/outliner/page_test.cljs | 18 ++++ src/main/frontend/components/block.cljs | 14 +-- src/main/frontend/components/block/asset.cljs | 34 +++++++ src/main/frontend/worker/handler/page.cljs | 35 ++++++-- .../frontend/components/block/asset_test.cljs | 18 ++++ src/test/frontend/worker/pipeline_test.cljs | 2 +- 13 files changed, 308 insertions(+), 39 deletions(-) create mode 100644 src/main/frontend/components/block/asset.cljs create mode 100644 src/test/frontend/components/block/asset_test.cljs diff --git a/deps/graph-parser/src/logseq/graph_parser/block.cljs b/deps/graph-parser/src/logseq/graph_parser/block.cljs index 84fa3f5a2a..90a8b11d82 100644 --- a/deps/graph-parser/src/logseq/graph_parser/block.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/block.cljs @@ -328,6 +328,7 @@ original-page-name (common-util/remove-boundary-slashes original-page-name) [original-page-name' page-name journal-day] (convert-page-if-journal original-page-name date-formatter {:export-to-db-graph? @*export-to-db-graph?}) namespace? (and (or (not db-based?) @*export-to-db-graph?) + (not journal-day) (not (boolean (text/get-nested-page-name original-page-name'))) (text/namespace-page? original-page-name')) page-entity (when (and db (not skip-existing-page-check?)) @@ -442,7 +443,9 @@ p)] (when (string? p) (let [p (or (text/get-nested-page-name p) p)] - (if (and (text/namespace-page? p) (not tag?)) + (if (and (text/namespace-page? p) + (not (common-date/valid-journal-title-with-slash? p)) + (not tag?)) (common-util/split-namespace-pages p) [p]))))) col) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index ed16ebd47d..0d8b26d52f 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -177,7 +177,8 @@ :page-names (sort (keys @page-names-to-uuids))}))))) (defn- replace-namespace-with-parent [block page-names-to-uuids parent-k] - (if (:block/namespace block) + (if (and (:block/namespace block) + (not (:block/journal-day block))) (-> (dissoc block :block/namespace) (assoc parent-k {:block/uuid (get-page-uuid page-names-to-uuids @@ -1571,14 +1572,15 @@ (not (get @assets asset-link-or-name)) (string/ends-with? path ".pdf") (fn? (p/let [^js stat ( (p/let [stat ( stat .-size) 0) :external-url (or asset-link-or-name path) :external-file-name asset-path})) (p/catch (fn [error] @@ -2017,7 +2019,7 @@ (cond-> page' true (dissoc :block/format) - (:block/namespace page) + (and (:block/namespace page) (not (:block/journal-day page))) ((fn [block'] (merge (build-new-namespace-page block') {;; save original name b/c it's still used for a few name lookups @@ -2523,6 +2525,68 @@ (when (seq tx) (d/transact! conn tx)))) +(defn- journal-uuid-normalizations + [db] + (keep (fn [datom] + (let [entity (d/entity db (:e datom)) + old-uuid (:block/uuid entity) + journal-day (:block/journal-day entity) + standard-uuid (common-uuid/gen-uuid :journal-page-uuid journal-day)] + (when (and old-uuid (not= old-uuid standard-uuid)) + (when-let [target (d/entity db [:block/uuid standard-uuid])] + (when (not= (:db/id target) (:db/id entity)) + (throw (ex-info "Cannot normalize journal uuid because the standard uuid is already used" + {:journal-day journal-day + :old-uuid old-uuid + :standard-uuid standard-uuid + :target-id (:db/id target)})))) + {:eid (:db/id entity) + :old-uuid old-uuid + :standard-uuid standard-uuid}))) + (d/datoms db :avet :block/journal-day))) + +(defn- replace-journal-uuid-refs + [value uuid-replacements] + (if (seq uuid-replacements) + (walk/postwalk + (fn [x] + (if (string? x) + (reduce (fn [s [old-uuid standard-uuid]] + (-> s + (string/replace (page-ref/->page-ref old-uuid) + (page-ref/->page-ref standard-uuid)) + (string/replace (block-ref/->block-ref old-uuid) + (block-ref/->block-ref standard-uuid)))) + x + uuid-replacements) + x)) + value) + value)) + +(defn- normalize-journal-uuids-tx + [db] + (let [normalizations (vec (journal-uuid-normalizations db)) + uuid-replacements (map (juxt :old-uuid :standard-uuid) normalizations) + uuid-tx (mapcat (fn [{:keys [eid old-uuid standard-uuid]}] + [[:db/retract eid :block/uuid old-uuid] + [:db/add eid :block/uuid standard-uuid]]) + normalizations) + text-tx (when (seq uuid-replacements) + (keep (fn [datom] + (let [value (:v datom) + value' (when (or (string? value) (coll? value)) + (replace-journal-uuid-refs value uuid-replacements))] + (when (and (some? value') (not= value value')) + [:db/add (:e datom) (:a datom) value']))) + (d/datoms db :eavt)))] + (vec (concat uuid-tx text-tx)))) + +(defn- normalize-journal-uuids! + [conn] + (let [tx (normalize-journal-uuids-tx @conn)] + (when (seq tx) + (d/transact! conn tx)))) + (defn export-doc-files "Exports all user created files i.e. under journals/ and pages/. Recommended to use build-doc-options and pass that as options" @@ -2544,9 +2608,11 @@ (p/recur (export-doc-file (get doc-files (inc i)) conn > (common-util/split-namespace-pages page) (map (fn [page] (-> (gp-block/page-name->map page db true date-formatter) @@ -204,9 +206,11 @@ pages (common-util/distinct-by :block/name pages) pages (remove nil? pages)] (map (fn [page] - (let [page-id (or (when db - (:block/uuid (ldb/get-page db (:block/name page)))) - (d/squuid))] + (let [page-id (if-let [journal-day (:block/journal-day page)] + (common-uuid/gen-uuid :journal-page-uuid journal-day) + (or (when db + (:block/uuid (ldb/get-page db (:block/name page)))) + (d/squuid)))] (assoc page :block/uuid page-id))) pages))) @@ -299,9 +303,11 @@ [pages] (->> (common-util/distinct-by :block/name pages) (map (fn [page] - (if (:block/uuid page) - page - (assoc page :block/uuid (d/squuid))))))) + (if-let [journal-day (:block/journal-day page)] + (assoc page :block/uuid (common-uuid/gen-uuid :journal-page-uuid journal-day)) + (cond-> page + (nil? (:block/uuid page)) + (assoc :block/uuid (d/squuid)))))))) (defn with-ref-pages [pages blocks] diff --git a/deps/graph-parser/test/logseq/graph_parser/block_test.cljs b/deps/graph-parser/test/logseq/graph_parser/block_test.cljs index e290b5464c..3c64cc8c33 100644 --- a/deps/graph-parser/test/logseq/graph_parser/block_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/block_test.cljs @@ -1,6 +1,7 @@ (ns logseq.graph-parser.block-test (:require [cljs.test :refer [deftest are testing is]] [datascript.core :as d] + [logseq.common.uuid :as common-uuid] [logseq.graph-parser.block :as gp-block] [logseq.graph-parser.mldoc :as gp-mldoc])) @@ -106,6 +107,17 @@ {:property-pages/enabled? true}))) "Only editable linkable built-in properties have page-refs in property values"))) +(deftest test-page-name-map-namespace-for-slash-journals + (testing "slash-formatted journals do not keep namespace metadata" + (let [journal (gp-block/page-name->map "2026/05/18" nil false "yyyy/MM/dd")] + (is (= 20260518 (:block/journal-day journal))) + (is (= (common-uuid/gen-uuid :journal-page-uuid 20260518) + (:block/uuid journal))) + (is (nil? (:block/namespace journal))))) + (testing "non-journal slash pages keep namespace metadata" + (is (= {:block/name "project"} + (:block/namespace (gp-block/page-name->map "project/child" nil false "yyyy/MM/dd")))))) + (defn find-block-for-content [db content] (->> (d/q '[:find (pull ?b [* {:block/refs [:block/uuid]}]) @@ -114,4 +126,4 @@ db content) (map first) - first)) \ No newline at end of file + first)) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index ab793837e7..2e7446eaad 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -9,7 +9,10 @@ [logseq.common.config :as common-config] [logseq.common.graph :as common-graph] [logseq.common.path :as path] + [logseq.common.util.block-ref :as block-ref] [logseq.common.util.date-time :as date-time-util] + [logseq.common.util.page-ref :as page-ref] + [logseq.common.uuid :as common-uuid] [logseq.db :as ldb] [logseq.db.common.entity-plus :as entity-plus] [logseq.db.frontend.asset :as db-asset] @@ -1296,6 +1299,91 @@ (:logseq.property/page-tags (db-test/readable-properties (db-test/find-page-by-title @conn "chat-gpt")))) "tagged page has new page and other pages marked with '#' and '[[]]` imported as tags to page-tags"))))) +(deftest-async import-journals-use-standard-uuids-and-keep-uuid-refs + (p/let [file-graph-dir "test/resources/exporter-test-graph" + files (mapv #(path/path-join file-graph-dir %) ["journals/2026_01_27.md"]) + conn (db-test/create-conn) + _ (import-files-to-db files conn {})] + (let [journal (db-test/find-journal-by-journal-day @conn 20260127) + ref-journal (db-test/find-journal-by-journal-day @conn 20260101) + ref-block (some->> (d/q '[:find [?b ...] + :in $ ?page ?ref-page + :where + [?b :block/page ?page] + [?b :block/refs ?ref-page]] + @conn (:db/id journal) (:db/id ref-journal)) + first + (d/entity @conn))] + (is (= (common-uuid/gen-uuid :journal-page-uuid 20260127) + (:block/uuid journal)) + "Imported journal page keeps the standard journal uuid") + (is (= (common-uuid/gen-uuid :journal-page-uuid 20260101) + (:block/uuid ref-journal)) + "Referenced journal page keeps the standard journal uuid") + (is (= #{(:block/uuid ref-journal)} + (set (map :block/uuid (:block/refs ref-block)))) + "Journal refs point at the standard journal uuid")))) + +(deftest-async import-journal-with-slash-title-format-does-not-create-namespace-pages + (p/let [file-graph-dir "test/resources/exporter-test-graph" + files (mapv #(path/path-join file-graph-dir %) ["journals/2026_01_27.md"]) + conn (db-test/create-conn) + _ (import-files-to-db files conn {:user-config {:journal/page-title-format "yyyy/MM/dd"}}) + journal (db-test/find-journal-by-journal-day @conn 20260127)] + (is (= "2026/01/27" (:block/title journal)) + "Journal title follows slash title format") + (is (= (common-uuid/gen-uuid :journal-page-uuid 20260127) + (:block/uuid journal)) + "Slash-formatted journal keeps the standard journal uuid") + (is (nil? (:block/namespace journal)) + "Slash-formatted journal does not keep a namespace attribute") + (is (nil? (db-test/find-page-by-title @conn "2026")) + "Journal title is not split into a year namespace page") + (is (nil? (db-test/find-page-by-title @conn "01")) + "Journal title is not split into a month namespace page") + (is (nil? (db-test/find-page-by-title @conn "27")) + "Journal title is not split into a day namespace page"))) + +(deftest-async import-slash-journal-ref-does-not-create-namespace-pages + (p/let [file (write-temp-graph-file "journals/2026_05_18.md" "- yes\n- [[Sun, 2026/05/17]]\n") + conn (db-test/create-conn) + _ (import-files-to-db [file] conn {:user-config {:journal/page-title-format "EEE, yyyy/MM/dd"}}) + ref-journal (db-test/find-journal-by-journal-day @conn 20260517)] + (is (= "Sun, 2026/05/17" (:block/title ref-journal)) + "Journal reference is imported as a journal page") + (is (nil? (:block/namespace ref-journal)) + "Referenced slash-formatted journal does not keep a namespace attribute") + (is (nil? (db-test/find-page-by-title @conn "Sun, 2026")) + "Journal reference is not split into a parent namespace page") + (is (nil? (db-test/find-page-by-title @conn "05")) + "Journal reference is not split into a child namespace page"))) + +(deftest-async import-normalizes-existing-random-journal-uuid-and-text-refs + (let [old-journal-uuid (random-uuid) + standard-journal-uuid (common-uuid/gen-uuid :journal-page-uuid 20260127) + title (str "refs " (page-ref/->page-ref old-journal-uuid) + " and " (block-ref/->block-ref old-journal-uuid)) + conn (db-test/create-conn-with-blocks + {:pages-and-blocks + [{:page {:build/journal 20260127 + :block/uuid old-journal-uuid + :build/keep-uuid? true} + :blocks [{:block/title title}]}]}) + file (write-temp-graph-file "pages/trigger-normalize.md" "- trigger normalize\n")] + (p/let [_ (import-files-to-db [file] conn {}) + journal (db-test/find-journal-by-journal-day @conn 20260127) + ref-block (db-test/find-block-by-content @conn #"refs")] + (is (= standard-journal-uuid (:block/uuid journal)) + "Existing random journal uuid is normalized to the standard journal uuid") + (is (nil? (d/entity @conn [:block/uuid old-journal-uuid])) + "Old journal uuid no longer resolves after normalization") + (is (= (str "refs " (page-ref/->page-ref standard-journal-uuid) + " and " (block-ref/->block-ref standard-journal-uuid)) + (:block/title ref-block)) + "Text references are rewritten to the standard journal uuid") + (is (= (:db/id journal) (get-in ref-block [:block/page :db/id])) + "Structured block page reference still points to the same journal entity")))) + (deftest-async export-files-with-tag-classes-option (p/let [file-graph-dir "test/resources/exporter-test-graph" files (mapv #(path/path-join file-graph-dir %) ["journals/2024_02_07.md" "pages/Interstellar.md"]) diff --git a/deps/graph-parser/test/resources/exporter-test-graph/journals/2026_01_27.md b/deps/graph-parser/test/resources/exporter-test-graph/journals/2026_01_27.md index bfafa78337..a0aec91612 100644 --- a/deps/graph-parser/test/resources/exporter-test-graph/journals/2026_01_27.md +++ b/deps/graph-parser/test/resources/exporter-test-graph/journals/2026_01_27.md @@ -1,4 +1,5 @@ - 2nd instance of block w/ longer ns tag #n1/n2/n3 - property pages with '/' should be valid -key/value:: 1 -- block with multi word tag #[[another test]] doesn't get removed by page-ref replacement \ No newline at end of file + key/value:: 1 +- block with multi word tag #[[another test]] doesn't get removed by page-ref replacement +- [[Jan 1st, 2026]] \ No newline at end of file diff --git a/deps/outliner/src/logseq/outliner/page.cljs b/deps/outliner/src/logseq/outliner/page.cljs index 0df4d7baae..481e6a117c 100644 --- a/deps/outliner/src/logseq/outliner/page.cljs +++ b/deps/outliner/src/logseq/outliner/page.cljs @@ -7,6 +7,7 @@ [logseq.common.util :as common-util] [logseq.common.util.date-time :as date-time-util] [logseq.common.util.namespace :as ns-util] + [logseq.common.uuid :as common-uuid] [logseq.db :as ldb] [logseq.db.common.entity-plus :as entity-plus] [logseq.db.common.order :as db-order] @@ -297,7 +298,7 @@ [db title* {uuid' :uuid :keys [tags properties persist-op? - class? today-journal? split-namespace? class-ident-namespace] + class? journal? today-journal? split-namespace? class-ident-namespace] :or {properties nil persist-op? true} :as options}] @@ -311,7 +312,7 @@ _ (outliner-validate/validate-page-title-no-hashtag title {:node {:block/title title}}) types (cond class? #{:logseq.class/Tag} - today-journal? + (or journal? today-journal?) #{:logseq.class/Journal} (seq tags) (set (map :db/ident tags)) @@ -375,7 +376,9 @@ {:class? class? :page-uuid (when (uuid? uuid') uuid') :skip-existing-page-check? true}) - [page parents'] (if (and (text/namespace-page? title) split-namespace?) + [page parents'] (if (and (not (:block/journal-day page)) + (text/namespace-page? title) + split-namespace?) (let [pages (split-namespace-pages db page date-formatter class?)] [(last pages) (butlast pages)]) [page nil])] @@ -389,7 +392,10 @@ (doseq [parent parents'] (outliner-validate/validate-page-title-characters (str (:block/title parent)) {:node parent}))) - (let [page-uuid (:block/uuid page) + (let [page-uuid (if-let [journal-day (:block/journal-day page)] + (common-uuid/gen-uuid :journal-page-uuid journal-day) + (:block/uuid page)) + page (assoc page :block/uuid page-uuid) page-txs (build-page-tx db properties page (select-keys options [:class? :tags :class-ident-namespace])) txs (concat ;; transact doesn't support entities diff --git a/deps/outliner/test/logseq/outliner/page_test.cljs b/deps/outliner/test/logseq/outliner/page_test.cljs index d5f679dabf..7520dca04e 100644 --- a/deps/outliner/test/logseq/outliner/page_test.cljs +++ b/deps/outliner/test/logseq/outliner/page_test.cljs @@ -5,6 +5,7 @@ [logseq.common.util :as common-util] [logseq.common.util.date-time :as date-time-util] [logseq.common.util.page-ref :as page-ref] + [logseq.common.uuid :as common-uuid] [logseq.db :as ldb] [logseq.db.common.order :as db-order] [logseq.db.frontend.db :as db-db] @@ -186,3 +187,20 @@ "Journal title follows configured formatter") (is (= default-name (:block/name page)) "Journal block/name remains the default formatter, independent of title format"))) + +(deftest create-slash-formatted-journal-does-not-create-namespace-pages + (let [conn (db-test/create-conn) + _ (d/transact! conn [[:db/add :logseq.class/Journal :logseq.property.journal/title-format "yyyy/MM/dd"]]) + [_ page-uuid] (outliner-page/create! conn "May 18th, 2026" {:split-namespace? true + :journal? true}) + page (d/entity @conn [:block/uuid page-uuid])] + (is (= "2026/05/18" (:block/title page)) + "Journal title follows slash title format") + (is (= (common-uuid/gen-uuid :journal-page-uuid 20260518) (:block/uuid page)) + "Journal page has the standard journal uuid") + (is (nil? (ldb/get-page @conn "2026")) + "Journal title is not split into a year namespace page") + (is (nil? (ldb/get-page @conn "05")) + "Journal title is not split into a month namespace page") + (is (nil? (ldb/get-page @conn "18")) + "Journal title is not split into a day namespace page"))) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index bb513a2062..8173be1808 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -12,6 +12,7 @@ [electron.ipc :as ipc] [frontend.components.avatar :as avatar] [frontend.components.block.breadcrumb-model :as breadcrumb-model] + [frontend.components.block.asset :as block-asset] [frontend.components.block.comments :as block-comments] [frontend.components.block.comments-model :as comments-model] [frontend.components.block.drop :as block-drop] @@ -518,8 +519,8 @@ (:image-placeholder config) (if (and (:image-placeholder config) (nil? @src)) (:image-placeholder config) - (let [ext (keyword (or (util/get-file-ext @src) - (util/get-file-ext href))) + (let [asset-block (:asset-block config) + ext (block-asset/link-ext @src href asset-block) repo (state/get-current-repo) repo-dir (config/get-repo-dir repo) share-fn (fn [event] @@ -560,15 +561,14 @@ title] util/web-platform? - (let [file-name (str (:block/title (:asset-block config)) "." (name ext))] + (let [file-name (block-asset/link-file-name asset-block ext)] [:a.asset-ref {:href @src :download file-name} file-name]) - (and (util/electron?) (:asset-block config)) - (let [asset-block (:asset-block config) - file-name (str (:block/title asset-block) "." (name ext))] + (and (util/electron?) asset-block) + (let [file-name (block-asset/link-file-name asset-block ext)] [:a.asset-ref {:on-click (fn [e] (util/stop e) @@ -582,7 +582,7 @@ file-fpath (if local-ext-url? ;; Plugin-sourced asset stored under assets/storages//... (path/path-join repo-dir (string/replace ext-url #"^[./]+" "")) - (path/path-join repo-dir (str "assets/" (:block/uuid asset-block) "." (name ext))))] + (path/path-join repo-dir (str "assets/" (:block/uuid asset-block) (when ext (str "." (name ext))))))] (if remote-ext-url? (js/window.apis.openExternal ext-url) (js/window.apis.openPath file-fpath))))} diff --git a/src/main/frontend/components/block/asset.cljs b/src/main/frontend/components/block/asset.cljs new file mode 100644 index 0000000000..ec7eeba142 --- /dev/null +++ b/src/main/frontend/components/block/asset.cljs @@ -0,0 +1,34 @@ +(ns frontend.components.block.asset + "Helpers for rendering asset links in block content. + + Asset links can point to local files, graph-relative files, remote URLs, or + protocol URLs. These helpers normalize the display-facing parts of an asset + without assuming that the URL itself contains a file extension." + (:require [clojure.string :as string] + [frontend.util :as util])) + +(defn- asset-type->keyword + "Coerces `asset-type` from an asset entity into a lowercase keyword. + + Returns `nil` when `asset-type` is absent or has an unsupported type." + [asset-type] + (cond + (keyword? asset-type) asset-type + (string? asset-type) (keyword (string/lower-case asset-type)))) + +(defn link-ext + "Resolves the extension keyword for an asset link. + + `src` is the resolved render URL, `href` is the original asset link, and + `asset-block` is the asset entity. The URL-derived extension has priority; + `:logseq.property.asset/type` is used when neither URL exposes an extension." + [src href asset-block] + (or (some-> (util/get-file-ext src) keyword) + (some-> (util/get-file-ext href) keyword) + (asset-type->keyword (:logseq.property.asset/type asset-block)))) + +(defn link-file-name + "Builds the display file name for `asset-block` using resolved extension `ext`." + [asset-block ext] + (cond-> (str (:block/title asset-block)) + ext (str "." (name ext)))) diff --git a/src/main/frontend/worker/handler/page.cljs b/src/main/frontend/worker/handler/page.cljs index fd746e147b..bacd1531b7 100644 --- a/src/main/frontend/worker/handler/page.cljs +++ b/src/main/frontend/worker/handler/page.cljs @@ -3,19 +3,36 @@ (:require [logseq.outliner.page :as outliner-page])) (defn create! - "Create page. Has the following options: + "Creates a page through the outliner page service. - * :uuid - when set, use this uuid instead of generating a new one. - * :class? - when true, adds a :block/tags ':logseq.class/Tag' - * :tags - tag uuids that are added to :block/tags - * :persist-op? - when true, add an update-page op - * :properties - properties to add to the page - TODO: Add other options" + Supported options: + + * :uuid - when set, use this uuid instead of generating a new one; ignored + when :journal? or :today-journal? uses a deterministic journal + uuid from :block/journal-day. + * :class? - create the page as a Tag class page. + * :journal? - create the page as a Journal page. + * :today-journal? - mark the create-page tx as today's journal creation. + * :tags - tag uuids or tag entities added to :block/tags. + * :properties - properties to add to the page. + * :split-namespace? - create namespace parent pages for non-journal slash pages. + * :class-ident-namespace - namespace used when creating a class ident. + * :persist-op? - when true, persist the create-page outliner op." [conn title & {:as options}] (outliner-page/create! conn title options)) (defn delete! - "Deletes a page. Returns true if able to delete page. If unable to delete, - calls error-handler fn and returns false" + "Deletes a page through the outliner page service. + + Returns true when the page can be deleted. If deletion is rejected, calls + :error-handler and returns false. + + Supported options: + + * :persist-op? - when true, persist the delete-page outliner op. + * :rename? - mark the tx as part of a rename flow. + * :error-handler - callback invoked with {:msg string} on rejection. + * :deleted-by-uuid - user uuid recorded in the delete op metadata. + * :now-ms - timestamp recorded in the delete op metadata." [conn page-uuid & {:as options}] (outliner-page/delete! conn page-uuid options)) diff --git a/src/test/frontend/components/block/asset_test.cljs b/src/test/frontend/components/block/asset_test.cljs new file mode 100644 index 0000000000..975333236c --- /dev/null +++ b/src/test/frontend/components/block/asset_test.cljs @@ -0,0 +1,18 @@ +(ns frontend.components.block.asset-test + (:require [cljs.test :refer [deftest is testing]] + [frontend.components.block.asset :as block-asset])) + +(deftest link-ext-test + (testing "falls back to asset type when the URL has no extension" + (is (= :pdf + (block-asset/link-ext + "zotero://select/library/items/QLUSY2JL" + "zotero://select/library/items/QLUSY2JL" + {:logseq.property.asset/type "pdf"}))))) + +(deftest link-file-name-test + (testing "uses the resolved extension in the displayed file name" + (is (= "test.pdf" + (block-asset/link-file-name + {:block/title "test"} + :pdf))))) diff --git a/src/test/frontend/worker/pipeline_test.cljs b/src/test/frontend/worker/pipeline_test.cljs index 906f1cc036..bd095abc07 100644 --- a/src/test/frontend/worker/pipeline_test.cljs +++ b/src/test/frontend/worker/pipeline_test.cljs @@ -189,7 +189,7 @@ (deftest create-journal-page-name-uses-default-formatter-test (let [conn (db-test/create-conn)] (d/transact! conn [[:db/add :logseq.class/Journal :logseq.property.journal/title-format "yyyy-MM-dd EEEE"]]) - (let [[_ page-uuid] (outliner-page/create! conn "Dec 16th, 2024" {}) + (let [[_ page-uuid] (outliner-page/create! conn "Dec 16th, 2024" {:journal? true}) page (d/entity @conn [:block/uuid page-uuid]) journal-day (:block/journal-day page) expected-title (date-time-util/int->journal-title journal-day "yyyy-MM-dd EEEE")