mirror of
https://github.com/logseq/logseq.git
synced 2026-05-21 11:22:44 +00:00
Merge pull request #11866 from logseq/refactor/page-parent
feat: Library
This commit is contained in:
@@ -7,17 +7,17 @@
|
||||
[logseq.e2e.fixtures :as fixtures]
|
||||
[logseq.e2e.graph :as graph]
|
||||
[logseq.e2e.keyboard :as k]
|
||||
[logseq.e2e.locator :as loc]
|
||||
[logseq.e2e.multi-tabs-basic-test]
|
||||
[logseq.e2e.outliner-basic-test]
|
||||
[logseq.e2e.plugins-basic-test]
|
||||
[logseq.e2e.property-basic-test]
|
||||
[logseq.e2e.reference-basic-test]
|
||||
[logseq.e2e.rtc-basic-test]
|
||||
[logseq.e2e.rtc-extra-test]
|
||||
[logseq.e2e.property-basic-test]
|
||||
[logseq.e2e.util :as util]
|
||||
[wally.main :as w]
|
||||
[wally.repl :as repl]
|
||||
[logseq.e2e.locator :as loc]))
|
||||
[wally.repl :as repl]))
|
||||
|
||||
;; Use port 3001 for local testing
|
||||
(reset! config/*port 3001)
|
||||
|
||||
18
deps/common/src/logseq/common/config.cljs
vendored
18
deps/common/src/logseq/common/config.cljs
vendored
@@ -1,7 +1,6 @@
|
||||
(ns logseq.common.config
|
||||
"Common config constants and fns that are shared between deps and app"
|
||||
(:require [clojure.string :as string]
|
||||
[goog.object :as gobj]))
|
||||
(:require [clojure.string :as string]))
|
||||
|
||||
(goog-define PUBLISHING false)
|
||||
|
||||
@@ -30,15 +29,12 @@
|
||||
(def app-name "logseq")
|
||||
|
||||
(defonce asset-protocol "assets://")
|
||||
(defonce capacitor-protocol "capacitor://")
|
||||
(defonce capacitor-prefix "_capacitor_file_")
|
||||
(defonce capacitor-protocol-with-prefix (str capacitor-protocol "localhost/" capacitor-prefix))
|
||||
(defonce capacitor-x-protocol-with-prefix (str (gobj/getValueByKeys js/globalThis "location" "href") capacitor-prefix))
|
||||
|
||||
(defonce local-assets-dir "assets")
|
||||
|
||||
(defonce favorites-page-name "$$$favorites")
|
||||
(defonce views-page-name "$$$views")
|
||||
(defonce library-page-name "Library")
|
||||
|
||||
(defn local-asset?
|
||||
[s]
|
||||
@@ -48,17 +44,13 @@
|
||||
(defn local-protocol-asset?
|
||||
[s]
|
||||
(when (string? s)
|
||||
(or (string/starts-with? s asset-protocol)
|
||||
(string/starts-with? s capacitor-protocol)
|
||||
(string/starts-with? s capacitor-x-protocol-with-prefix))))
|
||||
(string/starts-with? s asset-protocol)))
|
||||
|
||||
(defn remove-asset-protocol
|
||||
[s]
|
||||
(if (local-protocol-asset? s)
|
||||
(-> s
|
||||
(string/replace-first asset-protocol "file://")
|
||||
(string/replace-first capacitor-protocol-with-prefix "file://")
|
||||
(string/replace-first capacitor-x-protocol-with-prefix "file://"))
|
||||
(string/replace-first asset-protocol "file://"))
|
||||
s))
|
||||
|
||||
(defonce default-draw-directory "draws")
|
||||
@@ -154,4 +146,4 @@
|
||||
:favorites
|
||||
"is not stored in config for DB graphs"
|
||||
:default-templates
|
||||
"is replaced by #Template and the `Apply template to tags` property"}))
|
||||
"is replaced by #Template and the `Apply template to tags` property"}))
|
||||
|
||||
10
deps/common/test/logseq/common/config_test.cljc
vendored
10
deps/common/test/logseq/common/config_test.cljc
vendored
@@ -1,10 +1,10 @@
|
||||
(ns logseq.common.config-test
|
||||
(:require [cljs.test :refer [deftest is]]
|
||||
[clojure.string :as string]
|
||||
[logseq.common.config :as common-config]
|
||||
(:require ["fs" :as fs]
|
||||
["path" :as node-path]
|
||||
#?(:org.babashka/nbb [nbb.classpath :as cp])
|
||||
["fs" :as fs]
|
||||
["path" :as node-path]))
|
||||
[cljs.test :refer [deftest is]]
|
||||
[clojure.string :as string]
|
||||
[logseq.common.config :as common-config]))
|
||||
|
||||
(deftest remove-hidden-files
|
||||
(let [files ["pages/foo.md" "pages/bar.md"
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
(deftest escape-regex-chars
|
||||
(testing "ensure the result is a valid regex string"
|
||||
(are [x]
|
||||
(some? (re-find (re-pattern (common-util/escape-regex-chars x)) x))
|
||||
(some? (re-find (re-pattern (common-util/escape-regex-chars x)) x))
|
||||
"[[page-name]]"
|
||||
"end-with-backslash\\"
|
||||
"\\[]{}().+*?|$^")))
|
||||
|
||||
4
deps/db/script/create_graph.cljs
vendored
4
deps/db/script/create_graph.cljs
vendored
@@ -6,12 +6,12 @@
|
||||
[babashka.cli :as cli]
|
||||
[clojure.edn :as edn]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.sqlite.export :as sqlite-export]
|
||||
[logseq.outliner.cli :as outliner-cli]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]
|
||||
[validate-db]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]))
|
||||
[validate-db]))
|
||||
|
||||
(defn- resolve-path
|
||||
"If relative path, resolve with $ORIGINAL_PWD"
|
||||
|
||||
2
deps/db/script/validate_db.cljs
vendored
2
deps/db/script/validate_db.cljs
vendored
@@ -4,9 +4,9 @@
|
||||
(:require [babashka.cli :as cli]
|
||||
[cljs.pprint :as pprint]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]
|
||||
[logseq.db.frontend.validate :as db-validate]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[malli.core :as m]
|
||||
[malli.error :as me]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
30
deps/db/src/logseq/db.cljs
vendored
30
deps/db/src/logseq/db.cljs
vendored
@@ -7,6 +7,7 @@
|
||||
[clojure.walk :as walk]
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.common.uuid :as common-uuid]
|
||||
[logseq.db.common.delete-blocks :as delete-blocks] ;; Load entity extensions
|
||||
@@ -22,6 +23,14 @@
|
||||
[logseq.db.sqlite.util :as sqlite-util])
|
||||
(:refer-clojure :exclude [object?]))
|
||||
|
||||
(def built-in? entity-util/built-in?)
|
||||
(def built-in-class-property? db-db/built-in-class-property?)
|
||||
(def private-built-in-page? db-db/private-built-in-page?)
|
||||
|
||||
(def write-transit-str sqlite-util/write-transit-str)
|
||||
(def read-transit-str sqlite-util/read-transit-str)
|
||||
(def build-favorite-tx db-db/build-favorite-tx)
|
||||
|
||||
(defonce *transact-fn (atom nil))
|
||||
(defn register-transact-fn!
|
||||
[f]
|
||||
@@ -242,6 +251,17 @@
|
||||
(d/entity db [:block/uuid id])
|
||||
(d/entity db (get-first-page-by-name db (name page-id-name-or-uuid)))))))
|
||||
|
||||
(defn get-built-in-page
|
||||
[db title]
|
||||
(when db
|
||||
(let [id (common-uuid/gen-uuid :builtin-block-uuid title)]
|
||||
(d/entity db [:block/uuid id]))))
|
||||
|
||||
(defn library?
|
||||
[page]
|
||||
(and (built-in? page)
|
||||
(= common-config/library-page-name (:block/title page))))
|
||||
|
||||
(defn get-case-page
|
||||
"Case sensitive version of get-page. For use with DB graphs"
|
||||
[db page-name-or-uuid]
|
||||
@@ -470,14 +490,6 @@
|
||||
(when-not (hidden-or-internal-tag? e)
|
||||
e))))))
|
||||
|
||||
(def built-in? entity-util/built-in?)
|
||||
(def built-in-class-property? db-db/built-in-class-property?)
|
||||
(def private-built-in-page? db-db/private-built-in-page?)
|
||||
|
||||
(def write-transit-str sqlite-util/write-transit-str)
|
||||
(def read-transit-str sqlite-util/read-transit-str)
|
||||
(def build-favorite-tx db-db/build-favorite-tx)
|
||||
|
||||
(defn get-key-value
|
||||
[db key-ident]
|
||||
(:kv/value (d/entity db key-ident)))
|
||||
@@ -499,7 +511,7 @@
|
||||
(when db (get-key-value db :logseq.kv/remote-schema-version)))
|
||||
|
||||
(def get-all-properties db-db/get-all-properties)
|
||||
(def get-page-parents db-db/get-page-parents)
|
||||
(def get-class-extends db-db/get-class-extends)
|
||||
(def get-classes-parents db-db/get-classes-parents)
|
||||
(def get-title-with-parents db-db/get-title-with-parents)
|
||||
(def class-instance? db-db/class-instance?)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns logseq.db.common.initial-data
|
||||
"Provides db helper fns for graph initialization and lazy loading entities"
|
||||
(:require [datascript.core :as d]
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
@@ -329,7 +330,9 @@
|
||||
rseq
|
||||
(keep (fn [datom]
|
||||
(let [e (d/entity db (:e datom))]
|
||||
(when (and (common-entity-util/page? e) (not (entity-util/hidden? e)))
|
||||
(when (and (common-entity-util/page? e)
|
||||
(not (entity-util/hidden? e))
|
||||
(not (string/blank? (:block/title e))))
|
||||
e))))
|
||||
(take 30)))
|
||||
|
||||
|
||||
16
deps/db/src/logseq/db/frontend/class.cljs
vendored
16
deps/db/src/logseq/db/frontend/class.cljs
vendored
@@ -27,12 +27,12 @@
|
||||
|
||||
:logseq.class/Journal
|
||||
{:title "Journal"
|
||||
:properties {:logseq.property/parent :logseq.class/Page
|
||||
:properties {:logseq.property.class/extends :logseq.class/Page
|
||||
:logseq.property.journal/title-format "MMM do, yyyy"}}
|
||||
|
||||
:logseq.class/Whiteboard
|
||||
{:title "Whiteboard"
|
||||
:properties {:logseq.property/parent :logseq.class/Page}}
|
||||
:properties {:logseq.property.class/extends :logseq.class/Page}}
|
||||
|
||||
:logseq.class/Task
|
||||
{:title "Task"
|
||||
@@ -50,7 +50,7 @@
|
||||
:logseq.class/Cards
|
||||
{:title "Cards"
|
||||
:properties {:logseq.property/icon {:type :tabler-icon :id "search"}
|
||||
:logseq.property/parent :logseq.class/Query}}
|
||||
:logseq.property.class/extends :logseq.class/Query}}
|
||||
|
||||
:logseq.class/Asset
|
||||
{:title "Asset"
|
||||
@@ -95,7 +95,7 @@
|
||||
"Children of :logseq.class/Page"
|
||||
(set
|
||||
(keep (fn [[class-ident m]]
|
||||
(when (= (get-in m [:properties :logseq.property/parent]) :logseq.class/Page) class-ident))
|
||||
(when (= (get-in m [:properties :logseq.property.class/extends]) :logseq.class/Page) class-ident))
|
||||
built-in-classes)))
|
||||
|
||||
(def page-classes
|
||||
@@ -121,13 +121,13 @@
|
||||
(defn get-structured-children
|
||||
[db eid]
|
||||
(->>
|
||||
(d/q '[:find [?children ...]
|
||||
:in $ ?parent %
|
||||
(d/q '[:find [?c ...]
|
||||
:in $ ?p %
|
||||
:where
|
||||
(parent ?parent ?children)]
|
||||
(class-extends ?p ?c)]
|
||||
db
|
||||
eid
|
||||
(:parent rules/rules))
|
||||
(:class-extends rules/rules))
|
||||
(remove #{eid})))
|
||||
|
||||
;; Helper fns
|
||||
|
||||
51
deps/db/src/logseq/db/frontend/db.cljs
vendored
51
deps/db/src/logseq/db/frontend/db.cljs
vendored
@@ -3,6 +3,7 @@
|
||||
(:require [clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util.namespace :as ns-util]
|
||||
[logseq.common.util.page-ref :as page-ref]
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
@@ -42,34 +43,60 @@
|
||||
(->> (d/datoms db :avet :block/tags :logseq.class/Property)
|
||||
(map (fn [d] (d/entity db (:e d))))))
|
||||
|
||||
(defn get-page-parents
|
||||
[node & {:keys [node-class?]}]
|
||||
(when-let [parent (:logseq.property/parent node)]
|
||||
(defn get-class-extends
|
||||
"Returns all parents of a class"
|
||||
[node]
|
||||
(when-let [parent (:logseq.property.class/extends node)]
|
||||
(loop [current-parent parent
|
||||
parents' []]
|
||||
(if (and
|
||||
current-parent
|
||||
(if node-class? (entity-util/class? current-parent) true)
|
||||
(not (contains? parents' current-parent)))
|
||||
(recur (:logseq.property/parent current-parent)
|
||||
(if (and current-parent
|
||||
(not (contains? parents' current-parent)))
|
||||
(recur (:logseq.property.class/extends current-parent)
|
||||
(conj parents' current-parent))
|
||||
(vec (reverse parents'))))))
|
||||
|
||||
(defn get-page-parents
|
||||
[node]
|
||||
(when-let [parent (:block/parent node)]
|
||||
(loop [current-parent parent
|
||||
parents' []]
|
||||
(if (and current-parent
|
||||
(not (contains? parents' current-parent)))
|
||||
(recur (:block/parent current-parent)
|
||||
(conj parents' current-parent))
|
||||
(vec (reverse parents'))))))
|
||||
|
||||
(defn- get-class-title-with-extends
|
||||
[entity]
|
||||
(let [parents' (->> (get-class-extends entity)
|
||||
(remove (fn [e] (= :logseq.class/Root (:db/ident e))))
|
||||
vec)]
|
||||
(string/join
|
||||
ns-util/parent-char
|
||||
(map :block/title (conj (vec parents') entity)))))
|
||||
|
||||
(defn get-title-with-parents
|
||||
[entity]
|
||||
(if (or (entity-util/class? entity) (entity-util/internal-page? entity))
|
||||
(cond
|
||||
(entity-util/class? entity)
|
||||
(get-class-title-with-extends entity)
|
||||
|
||||
(entity-util/page? entity)
|
||||
(let [parents' (->> (get-page-parents entity)
|
||||
(remove (fn [e] (= :logseq.class/Root (:db/ident e))))
|
||||
vec)]
|
||||
(remove (fn [e]
|
||||
(and (:logseq.property/built-in? e) (= common-config/library-page-name (:block/title e))))))]
|
||||
(string/join
|
||||
ns-util/parent-char
|
||||
(map :block/title (conj (vec parents') entity))))
|
||||
|
||||
:else
|
||||
(:block/title entity)))
|
||||
|
||||
(defn get-classes-parents
|
||||
"Returns all parents of all classes. Like get-class-extends but for multiple classes"
|
||||
[tags]
|
||||
(let [tags' (filter entity-util/class? tags)
|
||||
result (mapcat #(get-page-parents % {:node-class? true}) tags')]
|
||||
result (mapcat get-class-extends tags')]
|
||||
(set result)))
|
||||
|
||||
(defn class-instance?
|
||||
|
||||
@@ -282,7 +282,9 @@
|
||||
(concat
|
||||
[:map
|
||||
;; journal-day is only set for journal pages
|
||||
[:block/journal-day {:optional true} :int]]
|
||||
[:block/journal-day {:optional true} :int]
|
||||
[:block/parent {:optional true} :int]
|
||||
[:block/order {:optional true} block-order]]
|
||||
page-attrs
|
||||
page-or-block-attrs)))
|
||||
|
||||
@@ -290,7 +292,8 @@
|
||||
(vec
|
||||
(concat
|
||||
[:map
|
||||
[:db/ident class-ident]]
|
||||
[:db/ident class-ident]
|
||||
[:logseq.property.class/extends {:optional true} :int]]
|
||||
page-attrs
|
||||
page-or-block-attrs)))
|
||||
|
||||
|
||||
15
deps/db/src/logseq/db/frontend/property.cljs
vendored
15
deps/db/src/logseq/db/frontend/property.cljs
vendored
@@ -148,13 +148,6 @@
|
||||
:hide? true
|
||||
:view-context :block}
|
||||
:queryable? true}
|
||||
:logseq.property/parent {:title "Parent"
|
||||
:schema {:type :node
|
||||
:public? true
|
||||
:view-context :page}
|
||||
:queryable? true
|
||||
:properties
|
||||
{:logseq.property/description "Provides parent-child relationships between nodes. For tags this enables inheritance and for pages this enables namespaces."}}
|
||||
:logseq.property/default-value {:title "Default value"
|
||||
:schema {:type :entity
|
||||
:public? false
|
||||
@@ -165,6 +158,14 @@
|
||||
:public? false
|
||||
:hide? true
|
||||
:view-context :property}}
|
||||
;; TODO: support cardinality many for extends
|
||||
:logseq.property.class/extends {:title "Extends"
|
||||
:schema {:type :class
|
||||
:public? true
|
||||
:view-context :class}
|
||||
:queryable? true
|
||||
:properties
|
||||
{:logseq.property/description "This enables tags to inherit properties from other tags"}}
|
||||
:logseq.property.class/properties {:title "Tag Properties"
|
||||
:schema {:type :property
|
||||
:cardinality :many
|
||||
|
||||
15
deps/db/src/logseq/db/frontend/rules.cljc
vendored
15
deps/db/src/logseq/db/frontend/rules.cljc
vendored
@@ -8,11 +8,18 @@
|
||||
;; rule "parent" is optimized for parent node -> child node nesting queries
|
||||
{:parent
|
||||
'[[(parent ?p ?c)
|
||||
[?c :logseq.property/parent ?p]]
|
||||
[?c :block/parent ?p]]
|
||||
[(parent ?p ?c)
|
||||
[?t :logseq.property/parent ?p]
|
||||
[?t :block/parent ?p]
|
||||
(parent ?t ?c)]]
|
||||
|
||||
:class-extends
|
||||
'[[(class-extends ?p ?c)
|
||||
[?c :logseq.property.class/extends ?p]]
|
||||
[(class-extends ?p ?c)
|
||||
[?t :logseq.property.class/extends ?p]
|
||||
(class-extends ?t ?c)]]
|
||||
|
||||
:alias
|
||||
'[[(alias ?e2 ?e1)
|
||||
[?e2 :block/alias ?e1]]
|
||||
@@ -136,7 +143,7 @@
|
||||
[?b :block/tags ?tc]
|
||||
(or
|
||||
[(= ?t ?tc)]
|
||||
(parent ?t ?tc))]
|
||||
(class-extends ?t ?tc))]
|
||||
|
||||
:has-property-or-object-property
|
||||
'[(has-property-or-object-property? ?b ?prop)
|
||||
@@ -236,7 +243,7 @@
|
||||
:priority #{:simple-query-property}
|
||||
:property-missing-value #{:object-has-class-property}
|
||||
:has-property-or-object-property #{:object-has-class-property}
|
||||
:object-has-class-property #{:parent}
|
||||
:object-has-class-property #{:class-extends}
|
||||
:has-simple-query-property #{:has-property-or-object-property}
|
||||
:has-private-simple-query-property #{:has-property-or-object-property}
|
||||
:property-default-value #{:existing-property-value :property-missing-value}
|
||||
|
||||
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
@@ -37,7 +37,7 @@
|
||||
(map (juxt :major :minor)
|
||||
[(parse-schema-version x) (parse-schema-version y)])))
|
||||
|
||||
(def version (parse-schema-version "65.0"))
|
||||
(def version (parse-schema-version "65.1"))
|
||||
|
||||
(defn major-version
|
||||
"Return a number.
|
||||
|
||||
2
deps/db/src/logseq/db/frontend/validate.cljs
vendored
2
deps/db/src/logseq/db/frontend/validate.cljs
vendored
@@ -118,7 +118,7 @@
|
||||
{:entities (count entities)
|
||||
:pages (count (filter :block/name entities))
|
||||
;; Nodes that aren't pages
|
||||
:blocks (count (filter :block/parent entities))
|
||||
:blocks (count (filter :block/page entities))
|
||||
:classes classes-count
|
||||
:properties properties-count
|
||||
;; Objects that aren't classes or properties
|
||||
|
||||
2
deps/db/src/logseq/db/sqlite/build.cljs
vendored
2
deps/db/src/logseq/db/sqlite/build.cljs
vendored
@@ -282,7 +282,7 @@
|
||||
(->block-properties (merge props (db-property-build/build-properties-with-ref-values pvalue-tx-m))
|
||||
uuid-maps all-idents options))
|
||||
(when class-parent
|
||||
{:logseq.property/parent
|
||||
{:logseq.property.class/extends
|
||||
(or (class-db-ids class-parent)
|
||||
(if (db-malli-schema/class? class-parent)
|
||||
class-parent
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[logseq.db.frontend.schema :as db-schema]
|
||||
[logseq.db.sqlite.util :as sqlite-util]))
|
||||
|
||||
(defn- mark-block-as-built-in [block]
|
||||
(defn mark-block-as-built-in [block]
|
||||
(assoc block :logseq.property/built-in? true))
|
||||
|
||||
(defn- schema->qualified-property-keyword
|
||||
@@ -128,7 +128,8 @@
|
||||
:properties (filter entity-util/property? properties-tx)}))
|
||||
|
||||
(def built-in-pages-names
|
||||
#{"Contents"})
|
||||
#{common-config/library-page-name
|
||||
"Contents"})
|
||||
|
||||
(defn- validate-tx-for-duplicate-idents [tx]
|
||||
(when-let [conflicting-idents
|
||||
@@ -232,7 +233,7 @@
|
||||
(map mark-block-as-built-in))
|
||||
hidden-pages (concat (build-initial-views) (build-favorites-page))
|
||||
;; These classes bootstrap our tags and properties as they depend on each other e.g.
|
||||
;; Root <-> Tag, classes-tx depends on logseq.property/parent, properties-tx depends on Property
|
||||
;; Root <-> Tag, classes-tx depends on logseq.property.class/extends, properties-tx depends on Property
|
||||
bootstrap-class? (fn [c] (contains? #{:logseq.class/Root :logseq.class/Property :logseq.class/Tag :logseq.class/Template} (:db/ident c)))
|
||||
bootstrap-classes (filter bootstrap-class? default-classes)
|
||||
bootstrap-class-ids (map #(select-keys % [:db/ident :block/uuid]) bootstrap-classes)
|
||||
|
||||
18
deps/db/src/logseq/db/sqlite/export.cljs
vendored
18
deps/db/src/logseq/db/sqlite/export.cljs
vendored
@@ -7,16 +7,16 @@
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.common.entity-plus :as entity-plus]
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
[logseq.db.frontend.content :as db-content]
|
||||
[logseq.db.frontend.db :as db-db]
|
||||
[logseq.db.common.entity-plus :as entity-plus]
|
||||
[logseq.db.frontend.entity-util :as entity-util]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.sqlite.build :as sqlite-build]
|
||||
[medley.core :as medley]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.db.frontend.schema :as db-schema]))
|
||||
[logseq.db.frontend.schema :as db-schema]
|
||||
[logseq.db.sqlite.build :as sqlite-build]
|
||||
[medley.core :as medley]))
|
||||
|
||||
;; Export fns
|
||||
;; ==========
|
||||
@@ -166,7 +166,7 @@
|
||||
(map (fn [[ent build-property]]
|
||||
(let [ent-properties (apply dissoc (db-property/properties ent)
|
||||
;; For overlapping class properties, these would be built in :classes
|
||||
:logseq.property/parent :logseq.property.class/properties
|
||||
:logseq.property.class/extends :logseq.property.class/properties
|
||||
(into db-property/schema-properties db-property/public-db-attribute-properties))]
|
||||
[(:db/ident ent)
|
||||
(cond-> build-property
|
||||
@@ -191,10 +191,10 @@
|
||||
(assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias class-ent))))
|
||||
;; It's caller's responsibility to ensure parent is included in final export
|
||||
(and (not shallow-copy?)
|
||||
(:logseq.property/parent class-ent)
|
||||
(not= :logseq.class/Root (:db/ident (:logseq.property/parent class-ent))))
|
||||
(:logseq.property.class/extends class-ent)
|
||||
(not= :logseq.class/Root (:db/ident (:logseq.property.class/extends class-ent))))
|
||||
(assoc :build/class-parent
|
||||
(:db/ident (:logseq.property/parent class-ent)))))
|
||||
(:db/ident (:logseq.property.class/extends class-ent)))))
|
||||
|
||||
(defn- build-node-classes
|
||||
[db build-block block-tags properties]
|
||||
@@ -589,7 +589,7 @@
|
||||
classes
|
||||
(->> class-ents
|
||||
(map (fn [ent]
|
||||
(let [ent-properties (apply dissoc (db-property/properties ent) :logseq.property/parent db-property/public-db-attribute-properties)]
|
||||
(let [ent-properties (apply dissoc (db-property/properties ent) :logseq.property.class/extends db-property/public-db-attribute-properties)]
|
||||
(vector (:db/ident ent)
|
||||
(cond-> (build-export-class ent options)
|
||||
(seq ent-properties)
|
||||
|
||||
12
deps/db/src/logseq/db/sqlite/util.cljs
vendored
12
deps/db/src/logseq/db/sqlite/util.cljs
vendored
@@ -98,16 +98,16 @@
|
||||
(cond-> (merge block
|
||||
{:block/tags (set (conj (:block/tags block) :logseq.class/Tag))})
|
||||
(and (not= (:db/ident block) :logseq.class/Root)
|
||||
(nil? (:logseq.property/parent block)))
|
||||
(assoc :logseq.property/parent :logseq.class/Root))))
|
||||
(nil? (:logseq.property.class/extends block)))
|
||||
(assoc :logseq.property.class/extends :logseq.class/Root))))
|
||||
|
||||
(defn build-new-page
|
||||
"Builds a basic page to be transacted. A minimal version of gp-block/page-name->map"
|
||||
[page-name]
|
||||
[title]
|
||||
(block-with-timestamps
|
||||
{:block/name (common-util/page-name-sanity-lc page-name)
|
||||
:block/title page-name
|
||||
:block/uuid (common-uuid/gen-uuid :builtin-block-uuid page-name)
|
||||
{:block/name (common-util/page-name-sanity-lc title)
|
||||
:block/title title
|
||||
:block/uuid (common-uuid/gen-uuid :builtin-block-uuid title)
|
||||
:block/tags #{:logseq.class/Page}}))
|
||||
|
||||
(defn kv
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
(deftest get-full-deps
|
||||
(let [default-value-deps #{:property-default-value :property-missing-value :existing-property-value
|
||||
:object-has-class-property :parent}
|
||||
:object-has-class-property :class-extends}
|
||||
property-value-deps (conj default-value-deps :property-value :property-scalar-default-value)
|
||||
property-deps (conj property-value-deps :simple-query-property)
|
||||
task-deps (conj property-deps :task)
|
||||
|
||||
@@ -84,9 +84,9 @@
|
||||
(is (= (count (dissoc db-class/built-in-classes :logseq.class/Root))
|
||||
(count (->> (d/datoms @conn :avet :block/tags :logseq.class/Tag)
|
||||
(map #(d/entity @conn (:e %)))
|
||||
(mapcat :logseq.property/_parent)
|
||||
(mapcat :logseq.property.class/_extends)
|
||||
set)))
|
||||
"Reverse lookup of :logseq.property/parent correctly fetches number of child classes")))
|
||||
"Reverse lookup of :logseq.property.class/extends correctly fetches number of child classes")))
|
||||
|
||||
(deftest new-graph-initializes-default-properties-correctly
|
||||
(let [conn (db-test/create-conn)]
|
||||
|
||||
@@ -695,6 +695,8 @@
|
||||
{:page {:block/uuid property-uuid}
|
||||
:blocks [{:block/title "property block1"}]}
|
||||
;; built-in pages
|
||||
{:page {:block/title "Library" :build/properties {:logseq.property/built-in? true}}
|
||||
:blocks []}
|
||||
{:page {:block/title "Contents" :build/properties {:logseq.property/built-in? true}}
|
||||
:blocks [{:block/title "right sidebar"}]}
|
||||
{:page {:block/title common-config/favorites-page-name
|
||||
@@ -882,9 +884,9 @@
|
||||
(deftest build-import-can-import-existing-page-with-different-uuid
|
||||
(testing "By default any properties passed to an existing page are upserted"
|
||||
(test-import-existing-page {}
|
||||
{:logseq.property/description "second description"
|
||||
:logseq.property/exclude-from-graph-view true}))
|
||||
{:logseq.property/description "second description"
|
||||
:logseq.property/exclude-from-graph-view true}))
|
||||
(testing "With ::existing-pages-keep-properties?, existing properties on existing pages are not overwritten by imported data"
|
||||
(test-import-existing-page {:existing-pages-keep-properties? true}
|
||||
{:logseq.property/description "first description"
|
||||
:logseq.property/exclude-from-graph-view true})))
|
||||
:logseq.property/exclude-from-graph-view true})))
|
||||
|
||||
8
deps/db/test/logseq/db_test.cljs
vendored
8
deps/db/test/logseq/db_test.cljs
vendored
@@ -30,18 +30,18 @@
|
||||
:block/title "y"
|
||||
:block/name "y"
|
||||
:block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99"
|
||||
:logseq.property/parent [:block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"]}
|
||||
:logseq.property.class/extends [:block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"]}
|
||||
{:block/tags :logseq.class/Tag
|
||||
:block/title "z"
|
||||
:block/name "z"
|
||||
:block/uuid #uuid "d95f2912-a7af-41b9-8ed5-28861f7fc0be"
|
||||
:logseq.property/parent [:block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99"]}])
|
||||
:logseq.property.class/extends [:block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99"]}])
|
||||
|
||||
(deftest get-page-parents
|
||||
(deftest get-class-extends
|
||||
(let [conn (db-test/create-conn)]
|
||||
(d/transact! conn class-parents-data)
|
||||
(is (= #{"x" "y"}
|
||||
(->> (ldb/get-page-parents (ldb/get-page @conn "z") {:node-class? true})
|
||||
(->> (ldb/get-class-extends (ldb/get-page @conn "z"))
|
||||
(map :block/title)
|
||||
set)))))
|
||||
|
||||
|
||||
6
deps/graph-parser/script/db_import.cljs
vendored
6
deps/graph-parser/script/db_import.cljs
vendored
@@ -10,6 +10,7 @@
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.graph :as common-graph]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.frontend.asset :as db-asset]
|
||||
@@ -18,8 +19,7 @@
|
||||
[logseq.outliner.pipeline :as outliner-pipeline]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]
|
||||
[promesa.core :as p]
|
||||
[logseq.common.config :as common-config]))
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def tx-queue (atom cljs.core/PersistentQueue.EMPTY))
|
||||
(def original-transact! d/transact!)
|
||||
@@ -209,4 +209,4 @@
|
||||
(println "Created graph" (str db-name "!")))))
|
||||
|
||||
(when (= nbb/*file* (nbb/invoked-file))
|
||||
(-main *command-line-args*))
|
||||
(-main *command-line-args*))
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
(merge ex-data' {:page-name page-name
|
||||
:page-names (sort (keys @page-names-to-uuids))})))))
|
||||
|
||||
(defn- replace-namespace-with-parent [block page-names-to-uuids]
|
||||
(defn- replace-namespace-with-parent [block page-names-to-uuids parent-k]
|
||||
(if (:block/namespace block)
|
||||
(-> (dissoc block :block/namespace)
|
||||
(assoc :logseq.property/parent
|
||||
(assoc parent-k
|
||||
{:block/uuid (get-page-uuid page-names-to-uuids
|
||||
(get-in block [:block/namespace :block/name])
|
||||
{:block block :block/namespace (:block/namespace block)})}))
|
||||
@@ -130,7 +130,7 @@
|
||||
db
|
||||
(ns-util/get-last-part full-name))
|
||||
(map #(d/entity db %))
|
||||
(some #(let [parents (->> (ldb/get-page-parents %)
|
||||
(some #(let [parents (->> (ldb/get-class-extends %)
|
||||
(remove (fn [e] (= :logseq.class/Root (:db/ident e))))
|
||||
vec)]
|
||||
(when (= full-name (string/join ns-util/namespace-char (map :block/name (conj parents %))))
|
||||
@@ -175,7 +175,7 @@
|
||||
(dissoc :block/created-at :block/updated-at)
|
||||
(merge (add-missing-timestamps
|
||||
(select-keys tag-block [:block/created-at :block/updated-at])))
|
||||
(replace-namespace-with-parent page-names-to-uuids))]
|
||||
(replace-namespace-with-parent page-names-to-uuids :logseq.property.class/extends))]
|
||||
(when (:new-class? (meta class-m)) (swap! classes-tx conj class-m'))
|
||||
(assert (:block/uuid class-m') "Class must have a :block/uuid")
|
||||
[:block/uuid (:block/uuid class-m')]))))))
|
||||
@@ -427,9 +427,12 @@
|
||||
(def all-built-in-names
|
||||
"All built-in properties and classes as a set of keywords"
|
||||
(set/union all-built-in-property-file-ids
|
||||
;; This should list all new pages introduced with db graph
|
||||
(set (->> db-class/built-in-classes
|
||||
vals
|
||||
(map #(-> % :title string/lower-case keyword))))))
|
||||
(map :title)
|
||||
(concat [common-config/library-page-name])
|
||||
(map #(-> % string/lower-case keyword))))))
|
||||
|
||||
(def file-built-in-property-names
|
||||
"File-graph built-in property names that are supported. Expressed as set of keywords"
|
||||
@@ -739,7 +742,7 @@
|
||||
(update :block dissoc :block/properties :block/properties-text-values :block/properties-order :block/invalid-properties)))
|
||||
|
||||
(defn- handle-page-properties
|
||||
"Adds page properties including special handling for :logseq.property/parent"
|
||||
"Adds page properties including special handling for :logseq.property.class/extends or :block/parent"
|
||||
[{:block/keys [properties] :as block*} db {:keys [page-names-to-uuids classes-tx]} refs
|
||||
{:keys [user-options log-fn import-state] :as options}]
|
||||
(let [{:keys [block properties-tx]} (handle-page-and-block-properties block* db page-names-to-uuids refs options)
|
||||
@@ -752,7 +755,8 @@
|
||||
class-m (find-or-create-class db ((some-fn ::original-title :block/title) block) (:all-idents import-state) block)
|
||||
class-m' (-> block
|
||||
(merge class-m)
|
||||
(assoc :logseq.property/parent
|
||||
(dissoc :block/namespace)
|
||||
(assoc :logseq.property.class/extends
|
||||
(let [new-class (first parent-classes-from-properties)
|
||||
class-m (find-or-create-class db new-class (:all-idents import-state))
|
||||
class-m' (merge class-m
|
||||
@@ -762,9 +766,8 @@
|
||||
(when (:new-class? (meta class-m)) (swap! classes-tx conj class-m'))
|
||||
[:block/uuid (:block/uuid class-m')])))]
|
||||
class-m')
|
||||
block)
|
||||
block'' (replace-namespace-with-parent block' page-names-to-uuids)]
|
||||
{:block block'' :properties-tx properties-tx}))
|
||||
(replace-namespace-with-parent block page-names-to-uuids :block/parent))]
|
||||
{:block block' :properties-tx properties-tx}))
|
||||
|
||||
(defn- pretty-print-dissoc
|
||||
"Remove list of keys from a given map string while preserving whitespace"
|
||||
@@ -918,10 +921,10 @@
|
||||
"\\)(\\{[^}]*\\})?"))
|
||||
(page-ref/->page-ref asset-uuid))]
|
||||
(when (string/includes? new-title asset-name)
|
||||
(swap! ignored-assets conj
|
||||
{:reason "Some asset links were not updated to block references"
|
||||
:path asset-name
|
||||
:location {:block new-title}}))
|
||||
(swap! ignored-assets conj
|
||||
{:reason "Some asset links were not updated to block references"
|
||||
:path asset-name
|
||||
:location {:block new-title}}))
|
||||
new-title))
|
||||
block-title
|
||||
asset-name-to-uuids))
|
||||
@@ -1025,10 +1028,11 @@
|
||||
"Like ldb/get-page-parents but using all-existing-page-uuids"
|
||||
[node all-existing-page-uuids]
|
||||
(let [get-parent (fn get-parent [n]
|
||||
(when (:block/uuid (:logseq.property/parent n))
|
||||
(or (get all-existing-page-uuids (:block/uuid (:logseq.property/parent n)))
|
||||
(throw (ex-info (str "No parent page found for " (pr-str (:block/uuid (:logseq.property/parent n))))
|
||||
{:node n})))))]
|
||||
(let [parent (or (:logseq.property.class/extends n) (:block/parent n))]
|
||||
(when-let [parent-id (:block/uuid parent)]
|
||||
(or (get all-existing-page-uuids parent-id)
|
||||
(throw (ex-info (str "No parent page found for " (pr-str (:block/uuid parent)))
|
||||
{:node n}))))))]
|
||||
(when-let [parent (get-parent node)]
|
||||
(loop [current-parent parent
|
||||
parents' []]
|
||||
@@ -1063,7 +1067,7 @@
|
||||
(let [;; These attributes are not allowed to be transacted because they must not change across files
|
||||
disallowed-attributes [:block/name :block/uuid :block/format :block/title :block/journal-day
|
||||
:block/created-at :block/updated-at]
|
||||
allowed-attributes (into [:block/tags :block/alias :logseq.property/parent :db/ident]
|
||||
allowed-attributes (into [:block/tags :block/alias :block/parent :logseq.property.class/extends :db/ident]
|
||||
(keep #(when (db-malli-schema/user-property? (key %)) (key %))
|
||||
m))
|
||||
block-changes (select-keys m allowed-attributes)]
|
||||
@@ -1267,6 +1271,15 @@
|
||||
(set (map (comp keyword string/lower-case) (:property-parent-classes user-options)))
|
||||
file-built-in-property-names)})}))
|
||||
|
||||
(defn- retract-parent-and-page-tag
|
||||
[col]
|
||||
(vec
|
||||
(mapcat (fn [b]
|
||||
(let [eid [:block/uuid (:block/uuid b)]]
|
||||
[[:db/retract eid :block/parent]
|
||||
[:db/retract eid :block/tags :logseq.class/Page]]))
|
||||
col)))
|
||||
|
||||
(defn- split-pages-and-properties-tx
|
||||
"Separates new pages from new properties tx in preparation for properties to
|
||||
be transacted separately. Also builds property pages tx and converts existing
|
||||
@@ -1295,8 +1308,7 @@
|
||||
{:block/uuid existing-page-uuid})))
|
||||
(set/intersection new-properties (set (map keyword (keys existing-pages)))))
|
||||
;; Could do this only for existing pages but the added complexity isn't worth reducing the tx noise
|
||||
retract-page-tag-from-properties-tx (map #(vector :db/retract [:block/uuid (:block/uuid %)] :block/tags :logseq.class/Page)
|
||||
(concat property-pages-tx converted-property-pages-tx))
|
||||
retract-page-tag-from-properties-tx (retract-parent-and-page-tag (concat property-pages-tx converted-property-pages-tx))
|
||||
;; Save properties on new property pages separately as they can contain new properties and thus need to be
|
||||
;; transacted separately the property pages
|
||||
property-page-properties-tx (keep (fn [b]
|
||||
@@ -1420,17 +1432,18 @@
|
||||
(->> pages-tx'
|
||||
;; Existing pages that have converted to property or class
|
||||
(filter #(and (:db/ident %) (get existing-pages' (:block/uuid %))))
|
||||
(mapv #(vector :db/retract [:block/uuid (:block/uuid %)] :block/tags :logseq.class/Page)))]
|
||||
retract-parent-and-page-tag)]
|
||||
{:pages-tx
|
||||
(mapv (fn [page]
|
||||
(if (or (contains? classes (:block/uuid page))
|
||||
(contains? existing-properties (:block/uuid page)))
|
||||
(update page :block/tags (fn [tags] (vec (remove #(= % :logseq.class/Page) tags))))
|
||||
(-> page
|
||||
(update :block/tags (fn [tags] (vec (remove #(= % :logseq.class/Page) tags))))
|
||||
(dissoc :block/parent))
|
||||
page))
|
||||
pages-tx')
|
||||
:retract-page-tags-tx
|
||||
(into (mapv #(vector :db/retract [:block/uuid (:block/uuid %)] :block/tags :logseq.class/Page)
|
||||
classes-tx)
|
||||
(into (retract-parent-and-page-tag classes-tx)
|
||||
retract-page-tag-from-existing-pages)}))
|
||||
|
||||
(defn- save-from-tx
|
||||
@@ -1730,6 +1743,26 @@
|
||||
:macros (or (:macros options) (:macros config))}
|
||||
(merge (select-keys options [:set-ui-state :export-file :notify-user]))))
|
||||
|
||||
(defn- move-top-parent-pages-to-library
|
||||
[conn repo-or-conn]
|
||||
(let [db @conn
|
||||
library-page (ldb/get-built-in-page db common-config/library-page-name)
|
||||
library-id (:block/uuid library-page)
|
||||
top-parent-pages (->> (d/datoms db :avet :block/parent)
|
||||
(keep (fn [d]
|
||||
(let [child (d/entity db (:e d))
|
||||
parent (d/entity db (:v d))]
|
||||
(when (and (nil? (:block/parent parent)) (ldb/page? child) (ldb/page? parent))
|
||||
parent))))
|
||||
(common-util/distinct-by :block/uuid))
|
||||
tx-data (map
|
||||
(fn [parent]
|
||||
{:db/id (:db/id parent)
|
||||
:block/parent [:block/uuid library-id]
|
||||
:block/order (db-order/gen-key)})
|
||||
top-parent-pages)]
|
||||
(ldb/transact! repo-or-conn tx-data)))
|
||||
|
||||
(defn export-file-graph
|
||||
"Main fn which exports a file graph given its files and imports them
|
||||
into a DB graph. Files is expected to be a seq of maps with a :path key.
|
||||
@@ -1781,6 +1814,7 @@
|
||||
(select-keys options [:notify-user :set-ui-state]))
|
||||
(export-favorites-from-config-edn conn repo-or-conn config {})
|
||||
(export-class-properties conn repo-or-conn)
|
||||
(move-top-parent-pages-to-library conn repo-or-conn)
|
||||
{:import-state (-> (:import-state doc-options)
|
||||
;; don't leak full asset content (which could be large) out of this ns
|
||||
(dissoc :assets))
|
||||
|
||||
@@ -364,7 +364,7 @@
|
||||
(:block/alias (db-test/readable-properties (db-test/find-page-by-title @conn "chat-gpt"))))
|
||||
"alias set correctly")
|
||||
(is (= ["y"]
|
||||
(->> (d/q '[:find [?b ...] :where [?b :block/title "y"] [?b :logseq.property/parent]]
|
||||
(->> (d/q '[:find [?b ...] :where [?b :block/title "y"] [?b :block/parent]]
|
||||
@conn)
|
||||
first
|
||||
(d/entity @conn)
|
||||
@@ -440,14 +440,15 @@
|
||||
|
||||
(testing "namespaces"
|
||||
(let [expand-children (fn expand-children [ent parent]
|
||||
(if-let [children (:logseq.property/_parent ent)]
|
||||
(if-let [children (:block/_parent ent)]
|
||||
(cons {:parent (:block/title parent) :child (:block/title ent)}
|
||||
(mapcat #(expand-children % ent) children))
|
||||
[{:parent (:block/title parent) :child (:block/title ent)}]))]
|
||||
;; check pages only
|
||||
(is (= [{:parent "n1" :child "x"}
|
||||
{:parent "x" :child "z"}
|
||||
{:parent "x" :child "y"}]
|
||||
(rest (expand-children (db-test/find-page-by-title @conn "n1") nil)))
|
||||
(take 3 (rest (expand-children (db-test/find-page-by-title @conn "n1") nil))))
|
||||
"First namespace tests duplicate parent page name")
|
||||
(is (= [{:parent "n2" :child "x"}
|
||||
{:parent "x" :child "z"}
|
||||
@@ -640,7 +641,8 @@
|
||||
(p/let [file-graph-dir "test/resources/exporter-test-graph"
|
||||
files (mapv #(node-path/join file-graph-dir %)
|
||||
["journals/2024_02_23.md" "pages/url.md" "pages/Whiteboard___Tool.md"
|
||||
"pages/Whiteboard___Arrow_head_toggle.md"])
|
||||
"pages/Whiteboard___Arrow_head_toggle.md"
|
||||
"pages/Library.md"])
|
||||
conn (db-test/create-conn)
|
||||
_ (import-files-to-db files conn {:property-classes ["type"]})
|
||||
_ (@#'gp-exporter/export-class-properties conn conn)]
|
||||
@@ -725,9 +727,9 @@
|
||||
set))
|
||||
"All classes are correctly defined by :type")
|
||||
|
||||
(is (= "CreativeWork" (get-in (d/entity @conn :user.class/Movie) [:logseq.property/parent :block/title]))
|
||||
(is (= "CreativeWork" (get-in (d/entity @conn :user.class/Movie) [:logseq.property.class/extends :block/title]))
|
||||
"Existing page correctly set as class parent")
|
||||
(is (= "Thing" (get-in (d/entity @conn :user.class/CreativeWork) [:logseq.property/parent :block/title]))
|
||||
(is (= "Thing" (get-in (d/entity @conn :user.class/CreativeWork) [:logseq.property.class/extends :block/title]))
|
||||
"New page correctly set as class parent")))
|
||||
|
||||
(deftest-async export-files-with-property-pages-disabled
|
||||
|
||||
1
deps/graph-parser/test/resources/exporter-test-graph/pages/Library.md
vendored
Normal file
1
deps/graph-parser/test/resources/exporter-test-graph/pages/Library.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
type:: [[Class]]
|
||||
2
deps/outliner/script/transact.cljs
vendored
2
deps/outliner/script/transact.cljs
vendored
@@ -2,8 +2,8 @@
|
||||
"This script generically runs transactions against the queried blocks"
|
||||
(:require [clojure.edn :as edn]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[logseq.outliner.db-pipeline :as db-pipeline]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
|
||||
2
deps/outliner/src/logseq/outliner/cli.cljs
vendored
2
deps/outliner/src/logseq/outliner/cli.cljs
vendored
@@ -73,4 +73,4 @@
|
||||
|
||||
(def build-blocks-tx
|
||||
"An alias for build-blocks-tx to specify default options for this ns"
|
||||
sqlite-build/build-blocks-tx)
|
||||
sqlite-build/build-blocks-tx)
|
||||
|
||||
51
deps/outliner/src/logseq/outliner/core.cljs
vendored
51
deps/outliner/src/logseq/outliner/core.cljs
vendored
@@ -261,7 +261,7 @@
|
||||
(not= block-title (:block/title block-entity)))
|
||||
_ (when (and db-based? page? block-title)
|
||||
(outliner-validate/validate-page-title-characters block-title {:node m*}))
|
||||
m* (if (and db-based? page-title-changed?)
|
||||
m* (if (and db-based? page-title-changed? (not (and page? (:block/parent block-entity))))
|
||||
(let [_ (outliner-validate/validate-page-title (:block/title m*) {:node m*})
|
||||
page-name (common-util/page-name-sanity-lc (:block/title m*))]
|
||||
(assoc m* :block/name page-name))
|
||||
@@ -334,7 +334,7 @@
|
||||
txs (map (fn [id] [:db.fn/retractEntity [:block/uuid id]]) ids)
|
||||
page-tx (let [block (d/entity db [:block/uuid block-id])]
|
||||
(when (:block/pre-block? block)
|
||||
(let [id (:db/id (:block/page block))]
|
||||
(when-let [id (:db/id (:block/page block))]
|
||||
[[:db/retract id :block/properties]
|
||||
[:db/retract id :block/properties-order]
|
||||
[:db/retract id :block/properties-text-values]
|
||||
@@ -503,12 +503,21 @@
|
||||
{}
|
||||
block)))
|
||||
|
||||
(defn- get-target-block-page
|
||||
[target-block]
|
||||
(or
|
||||
(:db/id (:block/page target-block))
|
||||
;; target parent is a page
|
||||
(when-let [parent (:block/parent target-block)]
|
||||
(when (ldb/page? parent)
|
||||
(:db/id parent)))
|
||||
;; target-block is a page itself
|
||||
(:db/id target-block)))
|
||||
|
||||
(defn- build-insert-blocks-tx
|
||||
[db target-block blocks uuids get-new-id {:keys [sibling? outliner-op replace-empty-target? insert-template? keep-block-order?]}]
|
||||
(let [block-ids (set (map :block/uuid blocks))
|
||||
target-page (or (:db/id (:block/page target-block))
|
||||
;; target block is a page itself
|
||||
(:db/id target-block))
|
||||
target-page (get-target-block-page target-block)
|
||||
orders (get-block-orders blocks target-block sibling? keep-block-order?)]
|
||||
(map-indexed (fn [idx {:block/keys [parent] :as block}]
|
||||
(when-let [uuid' (get uuids (:block/uuid block))]
|
||||
@@ -522,11 +531,13 @@
|
||||
(let [ref-ids (set (map :block/uuid (:block/refs block)))]
|
||||
(->> (set/intersection block-ids ref-ids)
|
||||
(remove #{(:block/uuid block)})))))
|
||||
m {:db/id (:db/id block)
|
||||
:block/uuid uuid'
|
||||
:block/page target-page
|
||||
:block/parent parent
|
||||
:block/order order}
|
||||
m (cond->
|
||||
{:db/id (:db/id block)
|
||||
:block/uuid uuid'
|
||||
:block/parent parent
|
||||
:block/order order}
|
||||
(not (ldb/page? block))
|
||||
(assoc :block/page target-page))
|
||||
result (->
|
||||
(if (de/entity? block)
|
||||
(assoc m :block/level (:block/level block))
|
||||
@@ -610,7 +621,6 @@
|
||||
|
||||
:else
|
||||
[block sibling?])
|
||||
sibling? (if (ldb/page? block) false sibling?)
|
||||
block (if (de/entity? block) block (d/entity db (:db/id block)))]
|
||||
[block sibling?])))
|
||||
|
||||
@@ -684,7 +694,6 @@
|
||||
blocks)
|
||||
[target-block sibling?] (get-target-block db blocks target-block opts)
|
||||
_ (assert (some? target-block) (str "Invalid target: " target-block))
|
||||
sibling? (if (ldb/page? target-block) false sibling?)
|
||||
replace-empty-target? (if (and (some? replace-empty-target?)
|
||||
(:block/title target-block)
|
||||
(string/blank? (:block/title target-block)))
|
||||
@@ -773,8 +782,7 @@
|
||||
[db blocks]
|
||||
(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?)
|
||||
(remove ldb/page?))
|
||||
top-level-blocks* (get-top-level-blocks top-level-blocks non-consecutive?)
|
||||
top-level-blocks (remove :logseq.property/built-in? top-level-blocks*)
|
||||
txs-state (ds/new-outliner-txs-state)
|
||||
block-ids (map (fn [b] [:block/uuid (:block/uuid b)]) top-level-blocks)
|
||||
@@ -824,10 +832,8 @@
|
||||
(let [target-block (d/entity db (:db/id target-block))
|
||||
block (d/entity db (:db/id block))
|
||||
first-block-page (:db/id (:block/page block))
|
||||
target-page (or (:db/id (:block/page target-block))
|
||||
(:db/id target-block))
|
||||
target-page (get-target-block-page target-block)
|
||||
not-same-page? (not= first-block-page target-page)
|
||||
|
||||
block-order (if sibling?
|
||||
(db-order/gen-key (:block/order target-block)
|
||||
(:block/order (ldb/get-right-sibling target-block)))
|
||||
@@ -840,12 +846,15 @@
|
||||
(:db/id (:block/parent target-block))
|
||||
(:db/id target-block))
|
||||
:block/order block-order}
|
||||
not-same-page?
|
||||
(not (ldb/page? block))
|
||||
(assoc :block/page target-page))]
|
||||
children-page-tx (when not-same-page?
|
||||
(let [children-ids (ldb/get-block-children-ids db (:block/uuid block))]
|
||||
(map (fn [id] {:block/uuid id
|
||||
:block/page target-page}) children-ids)))
|
||||
(keep (fn [id]
|
||||
(let [child (d/entity db [:block/uuid id])]
|
||||
(when-not (ldb/page? child)
|
||||
{:block/uuid id
|
||||
:block/page target-page}))) children-ids)))
|
||||
target-from-property (:logseq.property/created-from-property target-block)
|
||||
block-from-property (:logseq.property/created-from-property block)
|
||||
property-tx (let [retract-property-tx (when block-from-property
|
||||
@@ -985,7 +994,7 @@
|
||||
:sibling? true
|
||||
:indent? false})))
|
||||
|
||||
(when (and parent (not (ldb/page? (d/entity db (:db/id parent)))))
|
||||
(when parent
|
||||
(let [blocks' (take-while (fn [b]
|
||||
(not= (:db/id (:block/parent b))
|
||||
(:db/id (:block/parent parent))))
|
||||
|
||||
12
deps/outliner/src/logseq/outliner/property.cljs
vendored
12
deps/outliner/src/logseq/outliner/property.cljs
vendored
@@ -314,8 +314,8 @@
|
||||
_ (when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn block-eids v))
|
||||
property (d/entity @conn property-id)
|
||||
_ (when (= (:db/ident property) :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property
|
||||
_ (when (= (:db/ident property) :logseq.property.class/extends)
|
||||
(outliner-validate/validate-extends-property
|
||||
(if (number? v) (d/entity @conn v) v)
|
||||
(map #(d/entity @conn %) block-eids)))
|
||||
_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
|
||||
@@ -359,9 +359,9 @@
|
||||
property-id :logseq.property/empty-placeholder}]
|
||||
{:outliner-op :save-block})
|
||||
|
||||
(and (ldb/class? block) (= property-id :logseq.property/parent))
|
||||
(and (ldb/class? block) (= property-id :logseq.property.class/extends))
|
||||
(ldb/transact! conn
|
||||
[[:db/add (:db/id block) :logseq.property/parent :logseq.class/Root]]
|
||||
[[:db/add (:db/id block) :logseq.property.class/extends :logseq.class/Root]]
|
||||
{:outliner-op :save-block})
|
||||
|
||||
(contains? db-property/db-attribute-properties property-id)
|
||||
@@ -387,8 +387,8 @@
|
||||
db-attribute? (some? (db-schema/schema property-id))]
|
||||
(when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn [block-eid] v))
|
||||
(when (= property-id :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property v [block]))
|
||||
(when (= property-id :logseq.property.class/extends)
|
||||
(outliner-validate/validate-extends-property v [block]))
|
||||
(cond
|
||||
db-attribute?
|
||||
(when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself
|
||||
|
||||
116
deps/outliner/src/logseq/outliner/validate.cljs
vendored
116
deps/outliner/src/logseq/outliner/validate.cljs
vendored
@@ -46,59 +46,74 @@
|
||||
:payload {:message "Built-in pages can't be edited"
|
||||
:type :warning}}))))
|
||||
|
||||
(defn- validate-unique-by-parent-and-name [db entity new-title]
|
||||
(defn- validate-unique-by-extends-and-name [db entity new-title]
|
||||
(when-let [_res (seq (d/q '[:find [?b ...]
|
||||
:in $ ?eid ?type ?title
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :logseq.property/parent ?type]
|
||||
[?b :logseq.property.class/extends ?type]
|
||||
[(not= ?b ?eid)]]
|
||||
db
|
||||
(:db/id entity)
|
||||
(:db/id (:logseq.property/parent entity))
|
||||
(:db/id (:logseq.property.class/extends entity))
|
||||
new-title))]
|
||||
(throw (ex-info "Duplicate page by parent"
|
||||
{:type :notification
|
||||
:payload {:message (str "Another page named " (pr-str new-title) " already exists for parents "
|
||||
(pr-str (->> (ldb/get-page-parents entity)
|
||||
(pr-str (->> (ldb/get-class-extends entity)
|
||||
(map :block/title)
|
||||
(string/join ns-util/parent-char))))
|
||||
:type :warning}}))))
|
||||
|
||||
(defn- another-id-q
|
||||
[entity]
|
||||
(cond
|
||||
(ldb/property? entity)
|
||||
;; Property names are unique in that they can
|
||||
;; have the same names as built-in property names
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(missing? $ ?b :logseq.property/built-in?)]
|
||||
[(not= ?b ?eid)]]
|
||||
(:logseq.property.class/extends entity)
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(not= ?b ?eid)]
|
||||
;; same extends
|
||||
[?b :logseq.property.class/extends ?bp]
|
||||
[?eid :logseq.property.class/extends ?ep]
|
||||
[(= ?bp ?ep)]]
|
||||
(:block/parent entity)
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(not= ?b ?eid)]
|
||||
;; same parent
|
||||
[?b :block/parent ?bp]
|
||||
[?eid :block/parent ?ep]
|
||||
[(= ?bp ?ep)]]
|
||||
:else
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(not= ?b ?eid)]]))
|
||||
|
||||
(defn- validate-unique-for-page
|
||||
[db new-title {:block/keys [tags] :as entity}]
|
||||
(cond
|
||||
(seq tags)
|
||||
(when-let [another-id (first
|
||||
(d/q (cond
|
||||
(ldb/property? entity)
|
||||
;; Property names are unique in that they can
|
||||
;; have the same names as built-in property names
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(missing? $ ?b :logseq.property/built-in?)]
|
||||
[(not= ?b ?eid)]]
|
||||
(:logseq.property/parent entity)
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(not= ?b ?eid)]
|
||||
;; same parent
|
||||
[?b :logseq.property/parent ?bp]
|
||||
[?eid :logseq.property/parent ?ep]
|
||||
[(= ?bp ?ep)]]
|
||||
:else
|
||||
'[:find [?b ...]
|
||||
:in $ ?eid ?title [?tag-id ...]
|
||||
:where
|
||||
[?b :block/title ?title]
|
||||
[?b :block/tags ?tag-id]
|
||||
[(not= ?b ?eid)]])
|
||||
(d/q (another-id-q entity)
|
||||
db
|
||||
(:db/id entity)
|
||||
new-title
|
||||
@@ -117,13 +132,13 @@
|
||||
(map (fn [id] (str "#" (:block/title (d/entity db id)))) common-tag-ids)))
|
||||
:type :warning}})))))
|
||||
|
||||
(:logseq.property/parent entity)
|
||||
(validate-unique-by-parent-and-name db entity new-title)))
|
||||
(:logseq.property.class/extends entity)
|
||||
(validate-unique-by-extends-and-name db entity new-title)))
|
||||
|
||||
(defn ^:api validate-unique-by-name-tag-and-block-type
|
||||
"Validates uniqueness of nodes for the following cases:
|
||||
- Page names are unique for a tag e.g. their can be Apple #Company and Apple #Fruit
|
||||
- Page names are unique for a :logseq.property/parent"
|
||||
- Page names are unique for a :logseq.property.class/extends"
|
||||
[db new-title entity]
|
||||
(when (entity-util/page? entity)
|
||||
(validate-unique-for-page db new-title entity)))
|
||||
@@ -154,32 +169,29 @@
|
||||
:payload {:message "This is an invalid property name. A property name cannot start with page reference characters '#' or '[['."
|
||||
:type :error}}))))
|
||||
|
||||
(defn- validate-parent-property-have-same-type
|
||||
"Validates whether given parent and children are valid. Allows 'class' and
|
||||
'page' types to have a relationship with their own type. May consider allowing more
|
||||
page types if they don't cause systemic bugs"
|
||||
(defn- validate-extends-property-have-correct-type
|
||||
"Validates whether given parent and children are classes"
|
||||
[parent-ent child-ents]
|
||||
(when (or (and (ldb/class? parent-ent) (not (every? ldb/class? child-ents)))
|
||||
(and (ldb/internal-page? parent-ent) (not (every? ldb/internal-page? child-ents)))
|
||||
(not ((some-fn ldb/class? ldb/internal-page?) parent-ent)))
|
||||
(throw (ex-info "Can't set this page as a parent because the child page is a different type"
|
||||
(when (or (not (ldb/class? parent-ent))
|
||||
(not (every? ldb/class? child-ents)))
|
||||
(throw (ex-info "Can't extend this page since either it is not a tag or is extending from a page that is not a tag"
|
||||
{:type :notification
|
||||
:payload {:message "Can't set this page as a parent because the child page is a different type"
|
||||
:type :warning}
|
||||
:payload {:message "Can't extend this page since either it is not a tag or is extending from a page that is not a tag"
|
||||
:type :error}
|
||||
:blocks (map #(select-keys % [:db/id :block/title]) (remove ldb/class? child-ents))}))))
|
||||
|
||||
(defn- disallow-built-in-class-parent-change
|
||||
(defn- disallow-built-in-class-extends-change
|
||||
[_parent-ent child-ents]
|
||||
(when (some #(get db-class/built-in-classes (:db/ident %)) child-ents)
|
||||
(throw (ex-info "Can't change the parent of a built-in tag"
|
||||
{:type :notification
|
||||
:payload {:message "Can't change the parent of a built-in tag"
|
||||
:type :warning}}))))
|
||||
:type :error}}))))
|
||||
|
||||
(defn validate-parent-property
|
||||
(defn validate-extends-property
|
||||
[parent-ent child-ents]
|
||||
(disallow-built-in-class-parent-change parent-ent child-ents)
|
||||
(validate-parent-property-have-same-type parent-ent child-ents))
|
||||
(disallow-built-in-class-extends-change parent-ent child-ents)
|
||||
(validate-extends-property-have-correct-type parent-ent child-ents))
|
||||
|
||||
(defn- disallow-node-cant-tag-with-built-in-non-tags
|
||||
[db _block-eids v]
|
||||
@@ -230,4 +242,4 @@
|
||||
"Validates deleting a property value from :block/tags for given blocks"
|
||||
[db block-eids v]
|
||||
(disallow-tagging-a-built-in-entity db block-eids {:delete? true})
|
||||
(disallow-node-cant-tag-with-private-tags db block-eids v {:delete? true}))
|
||||
(disallow-node-cant-tag-with-private-tags db block-eids v {:delete? true}))
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
(db-test/find-page-by-title @conn "Fruit")))
|
||||
"Allow class to have same name as a page")))
|
||||
|
||||
(deftest validate-parent-property
|
||||
(deftest validate-extends-property
|
||||
(let [conn (db-test/create-conn-with-blocks
|
||||
{:properties {:prop1 {:logseq.property/type :default}}
|
||||
:classes {:Class1 {} :Class2 {}}
|
||||
@@ -73,34 +73,30 @@
|
||||
[{:page {:block/title "page1"}}
|
||||
{:page {:block/title "page2"}}]})
|
||||
page1 (db-test/find-page-by-title @conn "page1")
|
||||
page2 (db-test/find-page-by-title @conn "page2")
|
||||
class1 (db-test/find-page-by-title @conn "Class1")
|
||||
class2 (db-test/find-page-by-title @conn "Class2")
|
||||
property (db-test/find-page-by-title @conn "prop1")]
|
||||
|
||||
(testing "valid parent and child combinations"
|
||||
(is (nil? (outliner-validate/validate-parent-property page1 [page2]))
|
||||
"parent page to child page is valid")
|
||||
(is (nil? (outliner-validate/validate-parent-property class1 [class2]))
|
||||
(is (nil? (outliner-validate/validate-extends-property class1 [class2]))
|
||||
"parent class to child class is valid"))
|
||||
|
||||
(testing "invalid parent and child combinations"
|
||||
(are [parent child]
|
||||
(thrown-with-msg?
|
||||
js/Error
|
||||
#"Can't set"
|
||||
(outliner-validate/validate-parent-property parent [child]))
|
||||
#"Can't extend"
|
||||
(outliner-validate/validate-extends-property parent [child]))
|
||||
|
||||
class1 page1
|
||||
page1 class1
|
||||
property page1
|
||||
property class1))
|
||||
|
||||
(testing "built-in tag can't have parent changed"
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"Can't change.*built-in"
|
||||
(outliner-validate/validate-parent-property (entity-plus/entity-memoized @conn :logseq.class/Task)
|
||||
(outliner-validate/validate-extends-property (entity-plus/entity-memoized @conn :logseq.class/Task)
|
||||
[(entity-plus/entity-memoized @conn :logseq.class/Cards)]))))))
|
||||
|
||||
(deftest validate-tags-property
|
||||
@@ -168,9 +164,9 @@
|
||||
|
||||
(testing "Validate property relationships"
|
||||
(let [parent-child-pairs (d/q '[:find ?parent ?child
|
||||
:where [?child :logseq.property/parent ?parent]] @conn)]
|
||||
:where [?child :logseq.property.class/extends ?parent]] @conn)]
|
||||
(doseq [[parent-id child-id] parent-child-pairs]
|
||||
(let [parent (d/entity @conn parent-id)
|
||||
child (d/entity @conn child-id)]
|
||||
(is (nil? (#'outliner-validate/validate-parent-property-have-same-type parent [child]))
|
||||
(is (nil? (#'outliner-validate/validate-extends-property-have-correct-type parent [child]))
|
||||
(str "Parent and child page is valid: " (pr-str (:block/title parent)) " " (pr-str (:block/title child))))))))))
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"This frontend only ns builds the publishing html including doing all the
|
||||
necessary db filtering"
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[datascript.transit :as dt]
|
||||
[goog.string :as gstring]
|
||||
[goog.string.format]
|
||||
[datascript.transit :as dt]
|
||||
[datascript.core :as d]
|
||||
[logseq.publishing.db :as db]))
|
||||
|
||||
;; Copied from hiccup but tweaked for publish usage
|
||||
|
||||
7
deps/shui/src/logseq/shui/base/core.cljs
vendored
7
deps/shui/src/logseq/shui/base/core.cljs
vendored
@@ -44,13 +44,12 @@
|
||||
(apply button-base props' children)))
|
||||
|
||||
(defn button-icon
|
||||
[variant icon-name {:keys [icon-props size] :as props} child]
|
||||
|
||||
[variant icon-name {:keys [icon-props button-size] :as props} child]
|
||||
(button
|
||||
(merge (dissoc props :icon-props :size)
|
||||
(merge (dissoc props :icon-props :button-size)
|
||||
{:variant variant
|
||||
:data-button :icon
|
||||
:style (when size {:width size :height size})})
|
||||
:style (when button-size {:width button-size :height button-size})})
|
||||
(tabler-icon/root (name icon-name) (merge {:size 20} icon-props))
|
||||
child))
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.common.util.date-time :as date-time-util]
|
||||
[logseq.common.util.page-ref :as page-ref]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.outliner.cli :as outliner-cli]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]))
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn- date-journal-title [date]
|
||||
(date-time-util/int->journal-title (date-time-util/date->int date) "MMM do, yyyy"))
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
[clojure.string :as string]
|
||||
[clojure.walk :as w]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.outliner.cli :as outliner-cli]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]
|
||||
[logseq.db.common.sqlite-cli :as sqlite-cli]))
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn- get-comment-string
|
||||
[rdfs-comment renamed-pages]
|
||||
@@ -364,14 +364,14 @@
|
||||
(d/q '[:find [(pull ?b [*
|
||||
{:logseq.property.class/properties [:block/title]}
|
||||
{:logseq.property/classes [:block/title]}
|
||||
{:logseq.property/parent [:block/title]}
|
||||
{:logseq.property.class/extends [:block/title]}
|
||||
{:block/tags [:block/title]}
|
||||
{:block/refs [:block/title]}]) ...]
|
||||
:in $
|
||||
:where [?b :db/ident ?ident]]
|
||||
db))
|
||||
top-level-properties [:logseq.property/type :logseq.property.class/properties :logseq.property/classes
|
||||
:logseq.property/parent :block/tags]
|
||||
:logseq.property.class/extends :block/tags]
|
||||
debug-attributes (into [:block/name :block/title :db/cardinality :db/ident :block/refs]
|
||||
top-level-properties)]
|
||||
(fs/writeFileSync "schema-org.edn"
|
||||
@@ -388,8 +388,8 @@
|
||||
v)))))
|
||||
(seq (:logseq.property.class/properties m))
|
||||
(update :logseq.property.class/properties #(set (map :block/title %)))
|
||||
(some? (:logseq.property/parent m))
|
||||
(update :logseq.property/parent :block/title)
|
||||
(some? (:logseq.property.class/extends m))
|
||||
(update :logseq.property.class/extends :block/title)
|
||||
(seq (:logseq.property/classes m))
|
||||
(update :logseq.property/classes #(set (map :block/title %)))
|
||||
(seq (:block/tags m))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns logseq.tasks.dev.lint
|
||||
(:require [clojure.string :as string]
|
||||
[babashka.process :refer [shell]]))
|
||||
(:require [babashka.process :refer [shell]]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(defn dev
|
||||
"Run all lint tasks
|
||||
@@ -34,14 +34,14 @@
|
||||
;; remove files that aren't in a kondo dir
|
||||
((fn [x] (dissoc x nil))))]
|
||||
(if (seq dir-to-files)
|
||||
(doseq [[dir* files*] dir-to-files]
|
||||
(let [dir (if (= dir* "src") "." dir*)
|
||||
files (mapv #(string/replace-first % (str dir "/") "") files*)
|
||||
cmd (str "cd " dir " && clj-kondo --lint " (string/join " " files))
|
||||
_ (println cmd)
|
||||
res (apply shell {:dir dir :continue :true} "clj-kondo --lint" files)]
|
||||
(when (pos? (:exit res)) (System/exit (:exit res)))))
|
||||
(println "No clj* files have changed to lint."))))
|
||||
(doseq [[dir* files*] dir-to-files]
|
||||
(let [dir (if (= dir* "src") "." dir*)
|
||||
files (mapv #(string/replace-first % (str dir "/") "") files*)
|
||||
cmd (str "cd " dir " && clj-kondo --lint " (string/join " " files))
|
||||
_ (println cmd)
|
||||
res (apply shell {:dir dir :continue :true} "clj-kondo --lint" files)]
|
||||
(when (pos? (:exit res)) (System/exit (:exit res)))))
|
||||
(println "No clj* files have changed to lint."))))
|
||||
|
||||
(defn- validate-frontend-not-in-worker
|
||||
[]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
(ns logseq.tasks.lang
|
||||
"Tasks related to language translations"
|
||||
(:require [clojure.set :as set]
|
||||
(:require [babashka.cli :as cli]
|
||||
[babashka.fs :as fs]
|
||||
[babashka.process :refer [shell]]
|
||||
[borkdude.rewrite-edn :as rewrite]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[frontend.dicts :as dicts]
|
||||
[logseq.tasks.util :as task-util]
|
||||
[babashka.cli :as cli]
|
||||
[babashka.process :refer [shell]]
|
||||
[babashka.fs :as fs]
|
||||
[borkdude.rewrite-edn :as rewrite]))
|
||||
[logseq.tasks.util :as task-util]))
|
||||
|
||||
(defn- get-dicts
|
||||
[]
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
:kv/value {:doc "Used to store key-value, the value could be anything,
|
||||
e.g. {:db/ident :logseq.kv/xxx :kv/value value}"}
|
||||
|
||||
|
||||
;; :block.temp/xxx keywords
|
||||
;; :block.temp/xxx keywords
|
||||
:block.temp/load-status {:doc "`:full` means the block and its children have been loaded,
|
||||
`:self` means the block itself has been loaded."})
|
||||
|
||||
@@ -663,8 +663,9 @@
|
||||
All page-names are sanitized except page-name-in-block"
|
||||
[state
|
||||
{:keys [contents-page? whiteboard-page? other-position? show-unique-title? stop-click-event?
|
||||
on-context-menu]
|
||||
:or {stop-click-event? true}
|
||||
on-context-menu with-parent?]
|
||||
:or {stop-click-event? true
|
||||
with-parent? true}
|
||||
:as config}
|
||||
page-entity children label]
|
||||
(let [*hover? (::hover? state)
|
||||
@@ -672,9 +673,10 @@
|
||||
tag? (:tag? config)
|
||||
page-name (when (:block/title page-entity)
|
||||
(util/page-name-sanity-lc (:block/title page-entity)))
|
||||
breadcrumb? (:breadcrumb? config)
|
||||
config (assoc config :whiteboard-page? whiteboard-page?)
|
||||
untitled? (when page-name (model/untitled-page? (:block/title page-entity)))
|
||||
untitled? (when page-name
|
||||
(or (model/untitled-page? (:block/title page-entity))
|
||||
(and (ldb/page? page-entity) (string/blank? (:block/title page-entity)))))
|
||||
show-icon? (:show-icon? config)]
|
||||
[:a.relative
|
||||
(cond->
|
||||
@@ -706,9 +708,6 @@
|
||||
(and other-position? (not (util/shift-key? e)))
|
||||
(some-> (.-target e) (.closest ".jtrigger") (.click))
|
||||
|
||||
breadcrumb?
|
||||
(.preventDefault e)
|
||||
|
||||
:else
|
||||
(do
|
||||
(.preventDefault e)
|
||||
@@ -734,6 +733,12 @@
|
||||
:own-icon? true})]
|
||||
[:span {:class (str "icon-emoji-wrap " (when emoji? "as-emoji"))}
|
||||
icon])))
|
||||
|
||||
(when (and (ldb/page? page-entity) with-parent?)
|
||||
(when-let [parent (:block/parent page-entity)]
|
||||
(when-not (ldb/library? parent)
|
||||
[:span.select-none (str (:block/title parent) "/")])))
|
||||
|
||||
[:span
|
||||
(if (and (coll? children) (seq children))
|
||||
(for [child children]
|
||||
@@ -793,25 +798,27 @@
|
||||
(let [*el-trigger (hooks/use-ref nil)]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(when (true? visible?)
|
||||
(shui/popup-show!
|
||||
(hooks/deref *el-trigger) render
|
||||
{:root-props {:onOpenChange (fn [v] (set-visible! v))
|
||||
:modal false}
|
||||
:content-props {:class "ls-preview-popup"
|
||||
:onInteractOutside (fn [^js e] (.preventDefault e))
|
||||
:onEscapeKeyDown (fn [^js e]
|
||||
(when (state/editing?)
|
||||
(.preventDefault e)
|
||||
(some-> (hooks/deref *el-popup) (.focus))))}
|
||||
:as-dropdown? false}))
|
||||
(when-not (state/editing?)
|
||||
(when (true? visible?)
|
||||
(shui/popup-show!
|
||||
(hooks/deref *el-trigger) render
|
||||
{:root-props {:onOpenChange (fn [v] (set-visible! v))
|
||||
:modal false}
|
||||
:content-props {:class "ls-preview-popup"
|
||||
:onInteractOutside (fn [^js e] (.preventDefault e))
|
||||
:onEscapeKeyDown (fn [^js e]
|
||||
(when (state/editing?)
|
||||
(.preventDefault e)
|
||||
(some-> (hooks/deref *el-popup) (.focus))))}
|
||||
:as-dropdown? false}))
|
||||
|
||||
(when (false? visible?)
|
||||
(shui/popup-hide!)
|
||||
(when (state/get-edit-block)
|
||||
(state/clear-edit!)))
|
||||
(hooks/set-ref! *timer nil)
|
||||
(hooks/set-ref! *timer1 nil))
|
||||
|
||||
(when (false? visible?)
|
||||
(shui/popup-hide!)
|
||||
(when (state/get-edit-block)
|
||||
(state/clear-edit!)))
|
||||
(hooks/set-ref! *timer nil)
|
||||
(hooks/set-ref! *timer1 nil)
|
||||
;; teardown
|
||||
(fn []
|
||||
(when visible?
|
||||
@@ -2119,12 +2126,15 @@
|
||||
[config block children collapsed?]
|
||||
(let [ref? (:ref? config)
|
||||
query? (:custom-query? config)
|
||||
library? (:library? config)
|
||||
children (when (coll? children)
|
||||
(let [ref-matched-children-ids (:ref-matched-children-ids config)]
|
||||
(cond->> (remove nil? children)
|
||||
ref-matched-children-ids
|
||||
;; Block children will not be rendered if the filters do not match them
|
||||
(filter (fn [b] (ref-matched-children-ids (:db/id b)))))))]
|
||||
(filter (fn [b] (ref-matched-children-ids (:db/id b))))
|
||||
library?
|
||||
(filter (fn [b] (and (ldb/page? b) (not (or (ldb/class? b) (ldb/property? b)))))))))]
|
||||
(when (and (coll? children)
|
||||
(seq children)
|
||||
(not collapsed?))
|
||||
@@ -2163,9 +2173,10 @@
|
||||
:ignore-children? page-title?})
|
||||
link? (boolean (:original-block config))
|
||||
icon-size (if collapsed? 12 14)
|
||||
icon (icon-component/get-node-icon-cp block {:size icon-size :color? true})
|
||||
icon (icon-component/get-node-icon-cp block {:size icon-size :color? true :link? link?})
|
||||
with-icon? (and (some? icon)
|
||||
(or (db/page? block)
|
||||
(or (and (db/page? block)
|
||||
(not (:library? config)))
|
||||
(:logseq.property/icon block)
|
||||
link?
|
||||
(some :logseq.property/icon (:block/tags block))
|
||||
@@ -2449,6 +2460,9 @@
|
||||
db (db/get-db)
|
||||
query? (ldb/class-instance? (entity-plus/entity-memoized db :logseq.class/Query) block')]
|
||||
(cond
|
||||
(and (:page-title? config) (ldb/page? block) (string/blank? (:block/title block)))
|
||||
[:div.opacity-75 "Untitled"]
|
||||
|
||||
(:raw-title? config)
|
||||
(text-block-title (dissoc config :raw-title?) block)
|
||||
|
||||
@@ -3209,42 +3223,30 @@
|
||||
|
||||
(rum/defc breadcrumb-separator
|
||||
[]
|
||||
(ui/icon "chevron-right" {:style {:font-size 20}
|
||||
:class "opacity-50 mx-1"}))
|
||||
[:span.opacity-50.px-1
|
||||
"/"])
|
||||
|
||||
;; "block-id - uuid of the target block of breadcrumb. page uuid is also acceptable"
|
||||
(rum/defc breadcrumb-aux < rum/reactive
|
||||
[config repo block-id {:keys [show-page? indent? end-separator? level-limit _navigating-block]
|
||||
:or {show-page? true
|
||||
level-limit 3}
|
||||
[config repo block-id {:keys [show-page? indent? end-separator? _navigating-block]
|
||||
:or {show-page? true}
|
||||
:as opts}]
|
||||
(let [from-property (when (and block-id (config/db-based-graph? repo))
|
||||
(:logseq.property/created-from-property (db/entity [:block/uuid block-id])))
|
||||
parents (db/get-block-parents repo block-id {:depth (inc level-limit)})
|
||||
parents (remove nil? (concat parents [from-property]))
|
||||
page (or (db/get-block-page repo block-id) ;; only return for block uuid
|
||||
(model/query-block-by-uuid block-id)) ;; return page entity when received page uuid
|
||||
page-name (:block/name page)
|
||||
page-title (:block/title page)
|
||||
show? (or (seq parents) show-page? page-name)
|
||||
parents (if (= page-name (:block/name (first parents)))
|
||||
(rest parents)
|
||||
parents)
|
||||
more? (> (count parents) level-limit)
|
||||
parents (if more? (take-last level-limit parents) parents)
|
||||
parents (db/get-block-parents repo block-id {:depth 1000})
|
||||
parents (cond-> (remove nil? (concat parents [from-property]))
|
||||
(not show-page?)
|
||||
rest)
|
||||
config (assoc config
|
||||
:breadcrumb? true
|
||||
:disable-preview? true
|
||||
:stop-click-event? false)]
|
||||
(when show?
|
||||
(let [page-name-props (when show-page?
|
||||
[page
|
||||
(page-cp (dissoc config :breadcrumb? true) page)
|
||||
{:block/name (or page-title page-name)}])
|
||||
parents-props (doall
|
||||
(when (seq parents)
|
||||
(let [parents-props (doall
|
||||
(for [{:block/keys [uuid name title] :as block} parents]
|
||||
(if name
|
||||
[block (page-cp {} block)]
|
||||
[block (page-cp {:disable-preview? true
|
||||
:with-parent? false} block) true]
|
||||
(let [result (block/parse-title-and-body
|
||||
uuid
|
||||
(get block :block/format :markdown)
|
||||
@@ -3257,16 +3259,15 @@
|
||||
(when ast-title
|
||||
(if (seq ast-title)
|
||||
(->elem :span (map-inline config ast-title))
|
||||
(->elem :div (markup-elements-cp config ast-body))))]))))
|
||||
breadcrumbs (->> (into [] parents-props)
|
||||
(concat [page-name-props] (when more? [:more]))
|
||||
(filterv identity)
|
||||
(->elem :div (markup-elements-cp config ast-body))))
|
||||
false]))))
|
||||
breadcrumbs (->> parents-props
|
||||
(map (fn [x]
|
||||
(if (and (vector? x) (second x))
|
||||
(let [[block label] x]
|
||||
(rum/with-key (breadcrumb-fragment config block label opts)
|
||||
(str (:block/uuid block))))
|
||||
[:span.opacity-70 {:key "dots"} "⋯"])))
|
||||
(let [[block label page?] x
|
||||
label' (if page?
|
||||
label
|
||||
(breadcrumb-fragment config block label opts))]
|
||||
(rum/with-key label' (str (:block/uuid block))))))
|
||||
(interpose (breadcrumb-separator)))]
|
||||
(when (seq breadcrumbs)
|
||||
[:div.breadcrumb.block-parents
|
||||
@@ -3282,10 +3283,9 @@
|
||||
(when end-separator? (breadcrumb-separator))])))))
|
||||
|
||||
(rum/defc breadcrumb
|
||||
[config repo block-id {:keys [_show-page? _indent? _end-separator? level-limit _navigating-block]
|
||||
:or {level-limit 3}
|
||||
[config repo block-id {:keys [_show-page? _indent? _end-separator? _navigating-block]
|
||||
:as opts}]
|
||||
(let [[block set-block!] (hooks/use-state (db/entity [:block/uuid block-id]))]
|
||||
(let [[block set-block!] (hooks/use-state nil)]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(p/let [block (db-async/<get-block (state/get-current-repo)
|
||||
@@ -3293,7 +3293,7 @@
|
||||
{:children? false
|
||||
:skip-refresh? true})
|
||||
_ (when-let [id (:db/id block)]
|
||||
(db-async/<get-block-parents (state/get-current-repo) id level-limit))]
|
||||
(db-async/<get-block-parents (state/get-current-repo) id 9))]
|
||||
(set-block! block)))
|
||||
[])
|
||||
(when block
|
||||
@@ -3513,9 +3513,7 @@
|
||||
show-query? (rum/react *show-query?)
|
||||
*refs-count (get state ::refs-count)
|
||||
hide-block-refs? (rum/react *hide-block-refs?)
|
||||
refs-count (if (seq (:block/_refs block))
|
||||
(count (remove :logseq.property/view-for (:block/_refs block)))
|
||||
(rum/react *refs-count))
|
||||
refs-count (rum/react *refs-count)
|
||||
[original-block block] (build-block config* block {:navigating-block navigating-block :navigated? navigated?})
|
||||
config* (if original-block
|
||||
(assoc config* :original-block original-block)
|
||||
@@ -3702,6 +3700,7 @@
|
||||
(block-right-menu config block editing?))])
|
||||
|
||||
(when (and db-based?
|
||||
(not (:library? config))
|
||||
(or (:tag-dialog? config)
|
||||
(and
|
||||
(not collapsed?)
|
||||
|
||||
@@ -378,7 +378,7 @@
|
||||
@apply rounded transition-[background];
|
||||
|
||||
.bracket {
|
||||
@apply opacity-30;
|
||||
@apply opacity-30 inline-flex;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
(defn class-children-aux
|
||||
[class {:keys [default-collapsed?] :as opts}]
|
||||
(let [children (->> (:logseq.property/_parent class)
|
||||
(let [children (->> (:logseq.property.class/_extends class)
|
||||
;; Disallow parent cycles
|
||||
(remove #(= (:db/id class) (:db/id %))))]
|
||||
(when (seq children)
|
||||
[:ul
|
||||
(for [child (sort-by :block/title children)]
|
||||
(let [title [:li.ml-2 (block/page-reference {:show-brackets? false} (:block/uuid child) nil)]]
|
||||
(if (seq (:logseq.property/_parent child))
|
||||
(if (seq (:logseq.property.class/_extends child))
|
||||
(ui/foldable
|
||||
title
|
||||
(class-children-aux child opts)
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
(rum/defc class-children
|
||||
[class]
|
||||
(when (seq (:logseq.property/_parent class))
|
||||
(when (seq (:logseq.property.class/_extends class))
|
||||
(let [children-pages (set (model/get-structured-children (state/get-current-repo) (:db/id class)))
|
||||
;; Expand children if there are about a pageful of total blocks to display
|
||||
default-collapsed? (> (count children-pages) 30)]
|
||||
|
||||
@@ -127,14 +127,15 @@
|
||||
(or (let [page (some-> (text/get-namespace-last-part input)
|
||||
string/trim
|
||||
db/get-page)
|
||||
parent-title (:block/title (:logseq.property/parent page))
|
||||
parent-title (:block/title (:block/parent page))
|
||||
namespace? (string/includes? input "/")]
|
||||
(and page
|
||||
(or (not namespace?)
|
||||
(and
|
||||
parent-title
|
||||
(= (util/page-name-sanity-lc parent-title)
|
||||
(util/page-name-sanity-lc (nth (reverse (string/split input "/")) 1)))))))
|
||||
(some-> (util/nth-safe (reverse (string/split input "/")) 1)
|
||||
util/page-name-sanity-lc))))))
|
||||
(some (fn [block]
|
||||
(and
|
||||
(:block/tags block)
|
||||
@@ -294,13 +295,16 @@
|
||||
source-page (or (model/get-alias-source-page repo (:db/id entity))
|
||||
(:alias page))
|
||||
icon (get-page-icon entity)
|
||||
title (block-handler/block-unique-title page)
|
||||
title (block-handler/block-unique-title (or entity page))
|
||||
title' (if source-page (str title " -> alias: " (:block/title source-page)) title)]
|
||||
(hash-map :icon icon
|
||||
:icon-theme :gray
|
||||
:text title'
|
||||
:source-page (or source-page page)
|
||||
:alias (:alias page))))
|
||||
:header (when (:block/parent entity)
|
||||
(block/breadcrumb {:disable-preview? true
|
||||
:search? true} repo (:block/uuid page) {}))
|
||||
:alias (:alias page)
|
||||
:source-page (or source-page page))))
|
||||
|
||||
(defn- block-item
|
||||
[repo block current-page !input]
|
||||
@@ -310,7 +314,8 @@
|
||||
{:icon icon
|
||||
:icon-theme :gray
|
||||
:text (highlight-content-query text @!input)
|
||||
:header (when-not (db/page? block) (block/breadcrumb {:search? true} repo id {}))
|
||||
:header (block/breadcrumb {:disable-preview? true
|
||||
:search? true} repo id {})
|
||||
:current-page? (when-let [page-id (:block/page block)]
|
||||
(= page-id (:block/uuid current-page)))
|
||||
:source-block block}))
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
highlighted-text (string/replace normal-text query-re "<:hlmarker>$1<:hlmarker>")
|
||||
segs (string/split highlighted-text #"<:hlmarker>")]
|
||||
(if (seq segs)
|
||||
(into [:span {:data-testid text-string}]
|
||||
(into [:span {"data-testid" text-string}]
|
||||
(map-indexed (fn [i seg]
|
||||
(if (even? i)
|
||||
[:span seg]
|
||||
@@ -82,7 +82,7 @@
|
||||
:style {:color "var(--lx-gray-11)"}}
|
||||
(highlight-query header)])
|
||||
;; main row
|
||||
[:div.flex.items-center.gap-3
|
||||
[:div.flex.items-start.gap-3
|
||||
[:div.w-5.h-5.rounded.flex.items-center.justify-center
|
||||
{:style {:background (when (#{:gradient} icon-theme) "linear-gradient(-65deg, #8AE8FF, #5373E7, #369EFF, #00B1CC)")
|
||||
:box-shadow (when (#{:gradient} icon-theme) "inset 0 0 0 1px rgba(255,255,255,0.3) ")}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
[frontend.version :refer [version]]
|
||||
[goog.dom :as gdom]
|
||||
[goog.object :as gobj]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.path :as path]
|
||||
[logseq.common.util.namespace :as ns-util]
|
||||
[logseq.db :as ldb]
|
||||
@@ -155,8 +156,12 @@
|
||||
(block/inline-text :markdown (:block/title page))
|
||||
untitled? (t :untitled)
|
||||
:else (let [title' (pdf-utils/fix-local-asset-pagename title)
|
||||
parent (:logseq.property/parent page)]
|
||||
(if (and parent (not (ldb/class? page)))
|
||||
parent (:block/parent page)]
|
||||
(if (and parent
|
||||
(not (or (ldb/class? page)
|
||||
(and (:logseq.property/built-in? parent)
|
||||
(= (:block/title parent)
|
||||
common-config/library-page-name)))))
|
||||
(str (:block/title parent) ns-util/parent-char title')
|
||||
title')))]
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@
|
||||
(state/set-edit-content! (.-id input) value')
|
||||
(state/clear-editor-action!)
|
||||
(p/let [page (db/get-page chosen-item)
|
||||
_ (when-not page (page-handler/<create! chosen-item {:redirect? false}))
|
||||
_ (when-not page (page-handler/<create! chosen-item {:redirect? false
|
||||
:reference? true}))
|
||||
page' (db/get-page chosen-item)
|
||||
current-block (state/get-edit-block)]
|
||||
(editor-handler/api-insert-new-block! chosen-item
|
||||
@@ -204,14 +205,13 @@
|
||||
block)
|
||||
block)]
|
||||
[:div.flex.flex-col
|
||||
(when (and (not (or db-tag? (:page? block) (ldb/page? block)))
|
||||
(:block/uuid block'))
|
||||
(when (and (:block/uuid block') (or (:block/parent block') (not (:page? block))))
|
||||
(when-let [breadcrumb (state/get-component :block/breadcrumb)]
|
||||
[:div.text-xs.opacity-70.mb-1 {:style {:margin-left 3}}
|
||||
(breadcrumb {:search? true} (state/get-current-repo) (:block/uuid block') {})]))
|
||||
[:div.flex.flex-row.items-center.gap-1
|
||||
[:div.flex.flex-row.items-start
|
||||
(when-not (or db-tag? (not db-based?))
|
||||
[:div.flex.items-center
|
||||
[:div.flex.items-center.h-5.mr-1.opacity-50
|
||||
(cond
|
||||
(:nlp-date? block')
|
||||
(ui/icon "calendar" {:size 14})
|
||||
@@ -225,7 +225,7 @@
|
||||
(db-model/whiteboard-page? block')
|
||||
(ui/icon "writing" {:size 14})
|
||||
|
||||
(or (ldb/page? block') (:page? block'))
|
||||
(or (ldb/page? block') (:page? block))
|
||||
(ui/icon "file" {:size 14})
|
||||
|
||||
(or (string/starts-with? (str (:block/title block')) (t :new-tag))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[clojure.string :as string]
|
||||
[dommy.core :as d]
|
||||
[frontend.common.missionary :as c.m]
|
||||
[frontend.components.block :as component-block]
|
||||
[frontend.components.export :as export]
|
||||
[frontend.components.file-sync :as fs-sync]
|
||||
[frontend.components.page-menu :as page-menu]
|
||||
@@ -29,6 +30,7 @@
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.version :refer [version]]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.shui.hooks :as hooks]
|
||||
[logseq.shui.ui :as shui]
|
||||
@@ -66,7 +68,7 @@
|
||||
(let [rtc-graph-id (ldb/get-graph-rtc-uuid (db/get-db))
|
||||
online-users @(::online-users state)]
|
||||
(when rtc-graph-id
|
||||
[:div.rtc-collaborators.flex.gap-1.text-sm.py-2.bg-gray-01.items-center
|
||||
[:div.rtc-collaborators.flex.gap-1.text-sm.bg-gray-01.items-center
|
||||
(shui/button-ghost-icon :user-plus
|
||||
{:on-click #(shui/dialog-open!
|
||||
(fn []
|
||||
@@ -333,6 +335,18 @@
|
||||
(when (state/sub :ui/toggle-highlight-recent-blocks?)
|
||||
(recent-slider-inner)))
|
||||
|
||||
(rum/defc block-breadcrumb
|
||||
[page-name]
|
||||
[:div.ls-block-breadcrumb
|
||||
(when-let [page (when (and page-name (common-util/uuid-string? page-name))
|
||||
(db/entity [:block/uuid (uuid page-name)]))]
|
||||
(when (:block/parent page)
|
||||
[:div.text-sm
|
||||
(component-block/breadcrumb {}
|
||||
(state/get-current-repo)
|
||||
(:block/uuid page)
|
||||
{:header? true})]))])
|
||||
|
||||
(rum/defc ^:large-vars/cleanup-todo header-aux < rum/reactive
|
||||
[{:keys [current-repo default-home new-block-mode]}]
|
||||
(let [electron-mac? (and util/mac? (util/electron?))
|
||||
@@ -358,7 +372,7 @@
|
||||
[:div.l.flex.items-center.drag-region
|
||||
[left-menu
|
||||
(if (mobile-util/native-platform?)
|
||||
;; back button for mobile
|
||||
;; back button for mobile
|
||||
(when-not (or (state/home?) custom-home-page? (state/whiteboard-dashboard?))
|
||||
(ui/with-shortcut :go/backward "bottom"
|
||||
[:button.it.navigation.nav-left.button.icon.opacity-70
|
||||
@@ -375,58 +389,61 @@
|
||||
(state/pub-event! [:go/search]))}
|
||||
(ui/icon "search" {:size ui/icon-size})])))]]
|
||||
|
||||
[:div.r.flex.drag-region
|
||||
(when (and current-repo
|
||||
(ldb/get-graph-rtc-uuid (db/get-db))
|
||||
(user-handler/logged-in?)
|
||||
(config/db-based-graph? current-repo)
|
||||
(user-handler/team-member?))
|
||||
[:<>
|
||||
(recent-slider)
|
||||
(rum/with-key (rtc-collaborators)
|
||||
(str "collab-" current-repo))
|
||||
(rtc-indicator/indicator)])
|
||||
[:div.r.flex.drag-region.justify-between.items-center.gap-2.overflow-x-hidden.w-full
|
||||
[:div.flex.flex-1
|
||||
(block-breadcrumb (state/get-current-page))]
|
||||
[:div.flex
|
||||
(when (and current-repo
|
||||
(ldb/get-graph-rtc-uuid (db/get-db))
|
||||
(user-handler/logged-in?)
|
||||
(config/db-based-graph? current-repo)
|
||||
(user-handler/team-member?))
|
||||
[:<>
|
||||
(recent-slider)
|
||||
(rum/with-key (rtc-collaborators)
|
||||
(str "collab-" current-repo))
|
||||
(rtc-indicator/indicator)])
|
||||
|
||||
(when (user-handler/logged-in?)
|
||||
(rtc-indicator/downloading-detail))
|
||||
(when (user-handler/logged-in?)
|
||||
(rtc-indicator/uploading-detail))
|
||||
(when (user-handler/logged-in?)
|
||||
(rtc-indicator/downloading-detail))
|
||||
(when (user-handler/logged-in?)
|
||||
(rtc-indicator/uploading-detail))
|
||||
|
||||
(when (and current-repo
|
||||
(not (config/demo-graph? current-repo))
|
||||
(not (config/db-based-graph? current-repo))
|
||||
(user-handler/alpha-or-beta-user?))
|
||||
(fs-sync/indicator))
|
||||
(when (and current-repo
|
||||
(not (config/demo-graph? current-repo))
|
||||
(not (config/db-based-graph? current-repo))
|
||||
(user-handler/alpha-or-beta-user?))
|
||||
(fs-sync/indicator))
|
||||
|
||||
(when (and (not= (state/get-current-route) :home)
|
||||
(not custom-home-page?))
|
||||
(home-button))
|
||||
(when (and (not= (state/get-current-route) :home)
|
||||
(not custom-home-page?))
|
||||
(home-button))
|
||||
|
||||
(when config/lsp-enabled?
|
||||
[:<>
|
||||
(plugins/hook-ui-items :toolbar)
|
||||
(plugins/updates-notifications)])
|
||||
(when config/lsp-enabled?
|
||||
[:<>
|
||||
(plugins/hook-ui-items :toolbar)
|
||||
(plugins/updates-notifications)])
|
||||
|
||||
(when (state/feature-http-server-enabled?)
|
||||
(server/server-indicator (state/sub :electron/server)))
|
||||
(when (state/feature-http-server-enabled?)
|
||||
(server/server-indicator (state/sub :electron/server)))
|
||||
|
||||
(when (util/electron?)
|
||||
(back-and-forward))
|
||||
(when (util/electron?)
|
||||
(back-and-forward))
|
||||
|
||||
(when-not (mobile-util/native-platform?)
|
||||
(new-block-mode))
|
||||
(when-not (mobile-util/native-platform?)
|
||||
(new-block-mode))
|
||||
|
||||
(when config/publishing?
|
||||
[:a.text-sm.font-medium.button {:href (rfe/href :graph)}
|
||||
(t :graph)])
|
||||
(when config/publishing?
|
||||
[:a.text-sm.font-medium.button {:href (rfe/href :graph)}
|
||||
(t :graph)])
|
||||
|
||||
(toolbar-dots-menu {:t t
|
||||
:current-repo current-repo
|
||||
:default-home default-home})
|
||||
(toolbar-dots-menu {:t t
|
||||
:current-repo current-repo
|
||||
:default-home default-home})
|
||||
|
||||
(sidebar/toggle)
|
||||
(sidebar/toggle)
|
||||
|
||||
(updater-tips-new-version t)]]))
|
||||
(updater-tips-new-version t)]]]))
|
||||
|
||||
(def ^:private header-related-flow
|
||||
(m/latest
|
||||
|
||||
@@ -314,3 +314,31 @@ html.is-zoomed-native-ios {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cp__header {
|
||||
> .r > div:not(.ui__dropdown-trigger) a, button {
|
||||
@apply opacity-70 text-[inherit];
|
||||
|
||||
&:hover {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp__header {
|
||||
.ls-block-breadcrumb {
|
||||
@apply text-sm pl-4;
|
||||
}
|
||||
.breadcrumb {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply !opacity-75 hover:!opacity-100;
|
||||
span {
|
||||
@apply whitespace-nowrap text-ellipsis inline-block overflow-x-hidden align-middle;
|
||||
max-width: 34ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,12 @@
|
||||
(defn get-node-icon-cp
|
||||
[node-entity opts]
|
||||
(let [opts' (merge {:size 14} opts)
|
||||
node-icon (if (:own-icon? opts)
|
||||
node-icon (cond
|
||||
(:own-icon? opts)
|
||||
(get node-entity (pu/get-pid :logseq.property/icon))
|
||||
(:link? opts)
|
||||
"arrow-narrow-right"
|
||||
:else
|
||||
(get-node-icon node-entity))]
|
||||
(when-not (or (string/blank? node-icon) (and (contains? #{"letter-n" "file"} node-icon) (:not-text-or-page? opts)))
|
||||
[:div.icon-cp-container.flex.items-center
|
||||
|
||||
75
src/main/frontend/components/library.cljs
Normal file
75
src/main/frontend/components/library.cljs
Normal file
@@ -0,0 +1,75 @@
|
||||
(ns frontend.components.library
|
||||
"Library page"
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.components.select :as components-select]
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.search :as search]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.shui.hooks :as hooks]
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc select-pages
|
||||
[library-page]
|
||||
(let [[result set-result!] (hooks/use-state nil)
|
||||
[input set-input!] (hooks/use-state "")
|
||||
[selected-choices set-selected-choices!] (hooks/use-state #{})
|
||||
items (map (fn [block]
|
||||
{:value (:db/id block)
|
||||
:label (:block/title block)})
|
||||
result)]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(if (string/blank? input)
|
||||
(set-result! nil)
|
||||
(p/let [result (search/block-search (state/get-current-repo) input {:enable-snippet? false
|
||||
:built-in? false
|
||||
:page-only? true
|
||||
:library-page-search? true})]
|
||||
(set-result! result))))
|
||||
[(hooks/use-debounced-value input 200)])
|
||||
(components-select/select
|
||||
{:items items
|
||||
:extract-fn :label
|
||||
:extract-chosen-fn :value
|
||||
:selected-choices selected-choices
|
||||
:on-chosen (fn [chosen selected?]
|
||||
(if selected?
|
||||
(let [last-child (->> (:block/_parent (db/entity (:db/id library-page)))
|
||||
ldb/sort-by-order
|
||||
last)
|
||||
target (or last-child library-page)
|
||||
sibling? (some? last-child)]
|
||||
(editor-handler/move-blocks! [{:db/id chosen}] target sibling?)
|
||||
(set-selected-choices! (conj selected-choices chosen)))
|
||||
(do
|
||||
(db/transact! (state/get-current-repo)
|
||||
[[:db/retract chosen :block/parent]]
|
||||
{:outliner-op :save-block})
|
||||
(set-selected-choices! (disj selected-choices chosen)))))
|
||||
:multiple-choices? true
|
||||
:input-default-placeholder "Add pages"
|
||||
:show-new-when-not-exact-match? false
|
||||
:on-input set-input!
|
||||
:input-opts {:class "!p-1 !text-sm"}})))
|
||||
|
||||
(rum/defc add-pages
|
||||
[library-page]
|
||||
[:div.ls-add-pages.px-1
|
||||
(shui/button
|
||||
{:variant :secondary
|
||||
:size :sm
|
||||
:class "text-muted-foreground hover:text-foreground"
|
||||
:on-click (fn [e]
|
||||
(shui/popup-show!
|
||||
(.-target e)
|
||||
(fn []
|
||||
[:div {:style {:min-height 120}}
|
||||
(select-pages library-page)])
|
||||
{:align :start}))}
|
||||
(ui/icon "plus" {:size 16})
|
||||
"Add pages to Library")])
|
||||
@@ -8,6 +8,7 @@
|
||||
[frontend.components.db-based.page :as db-page]
|
||||
[frontend.components.editor :as editor]
|
||||
[frontend.components.file-based.hierarchy :as hierarchy]
|
||||
[frontend.components.library :as library]
|
||||
[frontend.components.objects :as objects]
|
||||
[frontend.components.plugins :as plugins]
|
||||
[frontend.components.property.config :as property-config]
|
||||
@@ -100,11 +101,12 @@
|
||||
(declare page-cp)
|
||||
|
||||
(rum/defc add-button
|
||||
[block container-id]
|
||||
[block {:keys [container-id] :as config*}]
|
||||
(let [*ref (rum/use-ref nil)
|
||||
has-children? (:block/_parent block)
|
||||
page? (ldb/page? block)
|
||||
opacity-class (if has-children? "opacity-0" "opacity-50")]
|
||||
opacity-class (if has-children? "opacity-0" "opacity-50")
|
||||
config (dissoc config* :page)]
|
||||
(when page?
|
||||
[:div.ls-block.block-add-button.flex-1.flex-col.rounded-sm.cursor-text.transition-opacity.ease-in.duration-100.!py-0
|
||||
{:class opacity-class
|
||||
@@ -113,8 +115,8 @@
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(state/set-state! :editor/container-id container-id)
|
||||
(editor-handler/api-insert-new-block! ""
|
||||
{:block-uuid (:block/uuid block)}))
|
||||
(editor-handler/api-insert-new-block! "" (merge config
|
||||
{:block-uuid (:block/uuid block)})))
|
||||
:on-mouse-over (fn []
|
||||
(let [ref (rum/deref *ref)
|
||||
prev-block (util/get-prev-block-non-collapsed (rum/deref *ref) {:up-down? true})]
|
||||
@@ -132,7 +134,7 @@
|
||||
(util/stop e)
|
||||
(when (= "Enter" (util/ekey e))
|
||||
(state/set-state! :editor/container-id container-id)
|
||||
(editor-handler/api-insert-new-block! "" block)))
|
||||
(editor-handler/api-insert-new-block! "" (merge config block))))
|
||||
:tab-index 0}
|
||||
[:div.flex.flex-row
|
||||
[:div.flex.items-center {:style {:height 28
|
||||
@@ -165,13 +167,14 @@
|
||||
(remove (fn [b] (some? (get b (:db/ident block)))) children)
|
||||
|
||||
:else
|
||||
children)]
|
||||
children)
|
||||
config (assoc config :library? (ldb/library? block))]
|
||||
(cond
|
||||
(and
|
||||
(not block?)
|
||||
(not config/publishing?)
|
||||
(empty? children) block)
|
||||
(add-button block (:container-id config))
|
||||
(add-button block config)
|
||||
|
||||
:else
|
||||
(let [document-mode? (state/sub :document/mode?)
|
||||
@@ -186,7 +189,7 @@
|
||||
blocks (if block? [block] (db/sort-by-order children block))]
|
||||
[:div.relative
|
||||
(page-blocks-inner block blocks config sidebar? whiteboard? block-id)
|
||||
(add-button block (:container-id config))])))))
|
||||
(add-button block config)])))))
|
||||
|
||||
(rum/defc today-queries < rum/reactive
|
||||
[repo today? sidebar?]
|
||||
@@ -407,7 +410,7 @@
|
||||
(let [with-actions? (not config/publishing?)]
|
||||
[:div.ls-page-title.flex.flex-1.w-full.content.items-start.title
|
||||
{:class (when-not whiteboard-page? "title")
|
||||
:data-testid "page title"
|
||||
"data-testid" "page title"
|
||||
:on-pointer-down (fn [e]
|
||||
(when (util/right-click? e)
|
||||
(state/set-state! :page-title/context {:page (:block/title page)
|
||||
@@ -572,7 +575,6 @@
|
||||
(let [current-repo (state/sub :git/current-repo)
|
||||
*tabs-rendered? (::tabs-rendered? state)
|
||||
repo (or repo current-repo)
|
||||
block-id (:block/uuid page)
|
||||
block? (some? (:block/page page))
|
||||
class-page? (ldb/class? page)
|
||||
property-page? (ldb/property? page)
|
||||
@@ -629,22 +631,21 @@
|
||||
:preview? preview?})))
|
||||
(lsp-pagebar-slot)])
|
||||
|
||||
(when (and db-based? (ldb/library? page))
|
||||
(library/add-pages page))
|
||||
|
||||
(when (and db-based? sidebar? (ldb/page? page))
|
||||
[:div.-mb-8
|
||||
(sidebar-page-properties config page)])
|
||||
|
||||
(when (and block? (not sidebar?) (not whiteboard?))
|
||||
(let [config (merge config {:id "block-parent"
|
||||
:block? true})]
|
||||
[:div.mb-4
|
||||
(component-block/breadcrumb config repo block-id {:level-limit 3})]))
|
||||
|
||||
(when show-tabs?
|
||||
(tabs page {:current-page? option :sidebar? sidebar? :*tabs-rendered? *tabs-rendered?}))
|
||||
|
||||
(when (and (or (not show-tabs?) tabs-rendered?) (not tag-dialog?))
|
||||
[:div.ls-page-blocks
|
||||
{:style {:margin-left (if whiteboard? 0 -20)}}
|
||||
{:style {:margin-left (if whiteboard? 0 -20)}
|
||||
:class (when-not sidebar?
|
||||
"mt-4")}
|
||||
(page-blocks-cp page (merge option {:sidebar? sidebar?
|
||||
:container-id (:container-id state)
|
||||
:whiteboard? whiteboard?}))])])
|
||||
@@ -660,7 +661,7 @@
|
||||
(when (and (not block?) (not db-based?))
|
||||
(tagged-pages repo page title))
|
||||
|
||||
(when (and (ldb/page? page) (:logseq.property/_parent page))
|
||||
(when (and (ldb/page? page) (:logseq.property.class/_extends page))
|
||||
(class-component/class-children page))
|
||||
|
||||
;; referenced blocks
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
(when (and db-based? (ldb/internal-page? page))
|
||||
{:title (t :page/convert-to-tag)
|
||||
:options {:on-click (fn []
|
||||
(db-page-handler/convert-to-tag! page))}})
|
||||
(db-page-handler/convert-page-to-tag! page))}})
|
||||
|
||||
(when (and db-based? (ldb/class? page) (not (:logseq.property/built-in? page)))
|
||||
{:title (t :page/convert-tag-to-page)
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
(shui/popup-hide!)))}]
|
||||
(property-select (merge (:select-opts opts) {:on-chosen on-chosen
|
||||
:input-opts input-opts
|
||||
:block block
|
||||
:class-schema? class-schema?}))))]))
|
||||
|
||||
(rum/defcs new-property < rum/reactive
|
||||
|
||||
@@ -615,7 +615,7 @@
|
||||
(property-type-sub-pane property ops))}))
|
||||
|
||||
(when (and (= property-type :node)
|
||||
(not (contains? #{:logseq.property/parent} (:db/ident property))))
|
||||
(not (contains? #{:logseq.property.class/extends} (:db/ident property))))
|
||||
(dropdown-editor-menuitem {:icon :hash
|
||||
:disabled? disabled?
|
||||
:title "Specify node tags"
|
||||
@@ -666,7 +666,7 @@
|
||||
(when (and (not= :logseq.property/enable-history? (:db/ident property))
|
||||
(not special-built-in-prop?))
|
||||
(let [property-type (:logseq.property/type property)
|
||||
group' (->> [(when (and (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
|
||||
group' (->> [(when (and (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
(contains? #{:default :number :date :checkbox :node} property-type)
|
||||
(not
|
||||
(and (= :default property-type)
|
||||
@@ -678,13 +678,13 @@
|
||||
:disabled? config/publishing?
|
||||
:submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :ui-position position)))})))
|
||||
|
||||
(when (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
(dropdown-editor-menuitem {:icon :eye-off :title "Hide by default" :toggle-checked? (boolean (:logseq.property/hide? property))
|
||||
:disabled? config/publishing?
|
||||
:on-toggle-checked-change #(db-property-handler/set-block-property! (:db/id property)
|
||||
:logseq.property/hide?
|
||||
%)}))
|
||||
(when (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
(dropdown-editor-menuitem
|
||||
{:icon :eye-off :title "Hide empty value"
|
||||
:toggle-checked? (boolean (:logseq.property/hide-empty-value property))
|
||||
@@ -731,7 +731,7 @@
|
||||
;; Any property should be removable from Tag Properties
|
||||
(if class-schema?
|
||||
(contains? (set (map :db/id (:logseq.property.class/properties owner-block))) (:db/id property))
|
||||
(not (contains? #{:logseq.property/parent} (:db/ident property)))))
|
||||
(not (contains? #{:logseq.property.class/extends} (:db/ident property)))))
|
||||
|
||||
(dropdown-editor-menuitem
|
||||
{:id :delete-property :icon :x
|
||||
|
||||
@@ -561,7 +561,7 @@
|
||||
page-entity (ldb/get-case-page (db/get-db) page)
|
||||
id (:db/id page-entity)
|
||||
class? (or (= :block/tags (:db/ident property))
|
||||
(and (= :logseq.property/parent (:db/ident property))
|
||||
(and (= :logseq.property.class/extends (:db/ident property))
|
||||
(ldb/class? block)))
|
||||
;; Note: property and other types shouldn't be converted to class
|
||||
page? (ldb/internal-page? page-entity)]
|
||||
@@ -585,7 +585,7 @@
|
||||
(:db/id page)))
|
||||
|
||||
(and class? page? id)
|
||||
(p/let [_ (db-page-handler/convert-to-tag! page-entity)]
|
||||
(p/let [_ (db-page-handler/convert-page-to-tag! page-entity)]
|
||||
id)
|
||||
|
||||
:else
|
||||
@@ -614,7 +614,7 @@
|
||||
items' (->>
|
||||
(if (and (seq selected-choices)
|
||||
(not multiple-choices?)
|
||||
(not (and (ldb/class? block) (= (:db/ident property) :logseq.property/parent)))
|
||||
(not (and (ldb/class? block) (= (:db/ident property) :logseq.property.class/extends)))
|
||||
(not= (:db/ident property) :logseq.property.view/type))
|
||||
(concat sorted-items
|
||||
[{:value clear-value
|
||||
@@ -674,11 +674,11 @@
|
||||
(if (every? entity-map? v)
|
||||
(map :db/id v)
|
||||
[(:db/id v)])))
|
||||
parent-property? (= (:db/ident property) :logseq.property/parent)
|
||||
children-pages (when parent-property? (model/get-structured-children repo (:db/id block)))
|
||||
extends-property? (= (:db/ident property) :logseq.property.class/extends)
|
||||
children-pages (when extends-property? (model/get-structured-children repo (:db/id block)))
|
||||
property-type (:logseq.property/type property)
|
||||
nodes (cond
|
||||
parent-property?
|
||||
extends-property?
|
||||
(let [;; Disallows cyclic hierarchies
|
||||
exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
|
||||
(conj (:block/uuid block))) ; break cycle
|
||||
@@ -779,7 +779,7 @@
|
||||
"Set alias"
|
||||
:else
|
||||
(str "Set " (:block/title property)))
|
||||
:show-new-when-not-exact-match? (if (or (and parent-property? (contains? (set children-pages) (:db/id block)))
|
||||
:show-new-when-not-exact-match? (if (or (and extends-property? (contains? (set children-pages) (:db/id block)))
|
||||
;; Don't allow creating private tags
|
||||
(and (= :block/tags (:db/ident property))
|
||||
(seq (set/intersection (set (map :db/ident classes'))
|
||||
@@ -869,20 +869,20 @@
|
||||
non-root-classes (cond-> (remove (fn [c] (= (:db/ident c) :logseq.class/Root)) classes)
|
||||
class?
|
||||
(conj (frontend.db/entity :logseq.class/Tag)))
|
||||
parent-property? (= (:db/ident property) :logseq.property/parent)]
|
||||
extends-property? (= (:db/ident property) :logseq.property.class/extends)]
|
||||
|
||||
;; effect runs once
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(cond
|
||||
(and parent-property? (not (ldb/class? block))
|
||||
(and extends-property? (not (ldb/class? block))
|
||||
(ldb/internal-page? block))
|
||||
(p/let [result (db-async/<get-tag-pages repo (:db/id (db/entity :logseq.class/Page)))
|
||||
result' (->> result
|
||||
(remove ldb/built-in?))]
|
||||
(set-result-and-initial-choices! result'))
|
||||
|
||||
parent-property?
|
||||
extends-property?
|
||||
nil
|
||||
|
||||
(seq non-root-classes)
|
||||
@@ -1499,15 +1499,15 @@
|
||||
(multiple-values block property opts)
|
||||
|
||||
:else
|
||||
(let [parent? (= property-ident :logseq.property/parent)
|
||||
(let [extends? (= property-ident :logseq.property.class/extends)
|
||||
value-cp (property-scalar-value block property v
|
||||
(merge
|
||||
opts
|
||||
{:editor-id editor-id
|
||||
:dom-id dom-id}))
|
||||
page-ancestors (when parent?
|
||||
page-ancestors (when extends?
|
||||
(let [ancestor-pages (loop [parents [block]]
|
||||
(if-let [parent (:logseq.property/parent (last parents))]
|
||||
(if-let [parent (:logseq.property.class/extends (last parents))]
|
||||
(when-not (contains? (set parents) parent)
|
||||
(recur (conj parents parent)))
|
||||
parents))]
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
:let [only-cloud? (and remote? (nil? root))
|
||||
db-based? (config/db-based-graph? url)]]
|
||||
[:div.flex.justify-between.mb-4.items-center.group {:key (or url GraphUUID)
|
||||
:data-testid url}
|
||||
"data-testid" url}
|
||||
[:div
|
||||
[:span.flex.items-center.gap-1
|
||||
(normalized-graph-label repo
|
||||
|
||||
@@ -21,16 +21,6 @@ html[data-theme=light] {
|
||||
height: calc(100vh - 48px);
|
||||
}
|
||||
|
||||
.cp__header {
|
||||
> .r > div:not(.ui__dropdown-trigger) a, button {
|
||||
@apply opacity-70 text-[inherit];
|
||||
|
||||
&:hover {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp__right-sidebar-topbar {
|
||||
@apply px-1 h-12;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"RTC state indicator"
|
||||
(:require [clojure.pprint :as pprint]
|
||||
[frontend.common.missionary :as c.m]
|
||||
[frontend.config :as config]
|
||||
[frontend.db :as db]
|
||||
[frontend.flows :as flows]
|
||||
[frontend.handler.db-based.rtc-flows :as rtc-flows]
|
||||
@@ -11,8 +12,7 @@
|
||||
[logseq.shui.hooks :as hooks]
|
||||
[logseq.shui.ui :as shui]
|
||||
[missionary.core :as m]
|
||||
[rum.core :as rum]
|
||||
[frontend.config :as config]))
|
||||
[rum.core :as rum]))
|
||||
|
||||
(comment
|
||||
(def rtc-state-schema
|
||||
@@ -147,7 +147,7 @@
|
||||
unpushed-block-update-count (:pending-local-ops detail-info)
|
||||
{:keys [local-tx remote-tx]} detail-info]
|
||||
[:div.cp__rtc-sync
|
||||
[:div.hidden {:data-testid "rtc-tx"} (pr-str {:local-tx local-tx :remote-tx remote-tx})]
|
||||
[:div.hidden {"data-testid" "rtc-tx"} (pr-str {:local-tx local-tx :remote-tx remote-tx})]
|
||||
[:div.cp__rtc-sync-indicator.flex.flex-row.items-center.gap-1
|
||||
(shui/button-ghost-icon :cloud
|
||||
{:on-click #(shui/popup-show! (.-target %)
|
||||
|
||||
@@ -529,10 +529,10 @@ independent of format as format specific heading characters are stripped"
|
||||
view-context (get m :logseq.property/view-context :all)]
|
||||
(or (contains? #{:logseq.property/query} (:db/ident m))
|
||||
(and (not block-page?) (contains? #{:block/alias} (:db/ident m)))
|
||||
;; Filters out properties from being in wrong :view-context and :never view-contexts
|
||||
;; Filters out properties from being in wrong :view-context and :never view-contexts
|
||||
(and (not= view-context :all) (not (contains? block-types view-context)))
|
||||
(and (ldb/built-in? block) (contains? #{:logseq.property/parent} (:db/ident m)))
|
||||
;; Filters out adding buggy class properties e.g. Alias and Parent
|
||||
(and (ldb/built-in? block) (contains? #{:logseq.property.class/extends} (:db/ident m)))
|
||||
;; Filters out adding buggy class properties e.g. Alias and Parent
|
||||
(and class-schema? (ldb/public-built-in-property? m) (:logseq.property/view-context m))))))
|
||||
|
||||
(defn get-all-properties
|
||||
@@ -577,7 +577,7 @@ independent of format as format specific heading characters are stripped"
|
||||
[repo class-id]
|
||||
(when-let [class (db-utils/entity repo class-id)]
|
||||
(->>
|
||||
(if (first (:logseq.property/_parent class)) ; has children classes
|
||||
(if (first (:logseq.property.class/_extends class)) ; has children classes
|
||||
(let [all-classes (conj (->> (get-structured-children repo class-id)
|
||||
(map #(db-utils/entity repo %)))
|
||||
class)]
|
||||
|
||||
@@ -176,8 +176,8 @@
|
||||
(if-not id
|
||||
;; add highlight
|
||||
(let [highlight (merge highlight
|
||||
{:id (pdf-utils/gen-uuid)
|
||||
:properties properties})]
|
||||
{:id (pdf-utils/gen-uuid)
|
||||
:properties properties})]
|
||||
(p/let [highlight' (add-hl! highlight)]
|
||||
(pdf-utils/clear-all-selection owner-win)
|
||||
(pdf-assets/copy-hl-ref! highlight' viewer)))
|
||||
@@ -569,15 +569,15 @@
|
||||
(set-highlights! highlights')
|
||||
|
||||
(if-let [vw-pos (and (pdf-assets/area-highlight? hl)
|
||||
(pdf-utils/scaled-to-vw-pos viewer (:position hl)))]
|
||||
(pdf-utils/scaled-to-vw-pos viewer (:position hl)))]
|
||||
(-> (p/let [result (pdf-assets/persist-hl-area-image$ viewer (:pdf/current @state/state)
|
||||
hl nil (:bounding vw-pos))]
|
||||
hl nil (:bounding vw-pos))]
|
||||
(if (de/entity? result)
|
||||
(let [hl' (assoc-in hl [:content :image] (:db/id result))]
|
||||
(set-highlights! (map (fn [hl] (if (= (:id hl) (:id hl')) hl' hl)) highlights'))
|
||||
hl')
|
||||
hl))
|
||||
(p/catch (fn [e] (js/console.error e))))
|
||||
(p/catch (fn [e] (js/console.error e))))
|
||||
hl))))
|
||||
upd-hl! (fn [hl]
|
||||
(let [highlights (pdf-utils/fix-nested-js highlights)]
|
||||
|
||||
@@ -53,8 +53,7 @@
|
||||
(block/breadcrumb {:preview? true}
|
||||
(state/get-current-repo)
|
||||
(uuid (gobj/get props "blockId"))
|
||||
{:end-separator? (gobj/get props "endSeparator")
|
||||
:level-limit (gobj/get props "levelLimit" 3)}))
|
||||
{:end-separator? (gobj/get props "endSeparator")})) -
|
||||
|
||||
(rum/defc tweet
|
||||
[props]
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.path :as path]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db.frontend.asset :as db-asset]
|
||||
[medley.core :as medley]
|
||||
[missionary.core :as m]
|
||||
[promesa.core :as p]
|
||||
[logseq.db.frontend.asset :as db-asset])
|
||||
[promesa.core :as p])
|
||||
(:import [missionary Cancelled]))
|
||||
|
||||
(defn alias-enabled?
|
||||
@@ -50,15 +50,6 @@
|
||||
(medley/find-first #(= name (:name (second %1)))
|
||||
(medley/indexed alias-dirs))))
|
||||
|
||||
(defn- convert-platform-protocol
|
||||
[full-path]
|
||||
|
||||
(cond-> full-path
|
||||
(and (string? full-path)
|
||||
(mobile-util/native-platform?))
|
||||
(string/replace-first
|
||||
#"^(file://|assets://)" common-config/capacitor-protocol-with-prefix)))
|
||||
|
||||
(defn resolve-asset-real-path-url
|
||||
[repo rpath]
|
||||
(when-let [rpath (and (string? rpath)
|
||||
@@ -87,7 +78,7 @@
|
||||
(if has-schema?
|
||||
(path/path-join graph-root rpath)
|
||||
(path/prepend-protocol "file:" (path/path-join graph-root rpath)))))]
|
||||
(convert-platform-protocol ret)))))
|
||||
ret))))
|
||||
|
||||
(defn normalize-asset-resource-url
|
||||
"try to convert resource file to url asset link"
|
||||
|
||||
@@ -77,14 +77,13 @@
|
||||
(= type order-list-type)))
|
||||
prev-block-fn #(some-> (db/entity (:db/id %)) ldb/get-left-sibling)
|
||||
prev-block (prev-block-fn block)]
|
||||
(letfn [(page-fn? [b] (some-> b :block/name some?))
|
||||
(order-sibling-list [b]
|
||||
(letfn [(order-sibling-list [b]
|
||||
(lazy-seq
|
||||
(when (and (not (page-fn? b)) (order-block-fn? b))
|
||||
(when (order-block-fn? b)
|
||||
(cons b (order-sibling-list (prev-block-fn b))))))
|
||||
(order-parent-list [b]
|
||||
(lazy-seq
|
||||
(when (and (not (page-fn? b)) (order-block-fn? b))
|
||||
(when (order-block-fn? b)
|
||||
(cons b (order-parent-list (db-model/get-block-parent (:block/uuid b)))))))]
|
||||
(let [idx (if prev-block
|
||||
(count (order-sibling-list block)) 1)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(ns frontend.handler.common.config-edn
|
||||
"Common fns related to config.edn - global and repo"
|
||||
(:require [malli.error :as me]
|
||||
[malli.core :as m]
|
||||
(:require [clojure.edn :as edn]
|
||||
[clojure.string :as string]
|
||||
[clojure.edn :as edn]
|
||||
[lambdaisland.glogi :as log]
|
||||
[frontend.handler.notification :as notification]
|
||||
[goog.string :as gstring]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[logseq.common.config :as common-config]))
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.common.config :as common-config]
|
||||
[malli.core :as m]
|
||||
[malli.error :as me]
|
||||
[reitit.frontend.easy :as rfe]))
|
||||
|
||||
(defn- humanize-more
|
||||
"Make error maps from me/humanize more readable for users. Doesn't try to handle
|
||||
@@ -77,7 +77,7 @@ nested keys or positional errors e.g. tuples"
|
||||
(do
|
||||
(config-notification-show! [:<> "Failed to read file " (file-link path)]
|
||||
"Make sure your config is wrapped in {}. Also make sure that the characters '( { [' have their corresponding closing character ') } ]'.")
|
||||
false)
|
||||
false)
|
||||
;; Custom error message is better than malli's "invalid type" error
|
||||
(not (map? parsed-body))
|
||||
(do
|
||||
@@ -91,7 +91,7 @@ nested keys or positional errors e.g. tuples"
|
||||
"Detects config keys that will or have been deprecated"
|
||||
[path content {:keys [db-graph?]}]
|
||||
(let [body (try (edn/read-string content)
|
||||
(catch :default _ ::failed-to-detect))
|
||||
(catch :default _ ::failed-to-detect))
|
||||
warnings (cond->
|
||||
{:editor/command-trigger
|
||||
"is no longer supported. Please use '/' and report bugs on it."
|
||||
|
||||
@@ -42,43 +42,50 @@
|
||||
(when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity)
|
||||
(db-property-handler/set-block-property! block-id :block/tags (:db/id tag-entity)))))
|
||||
|
||||
(defn convert-to-tag!
|
||||
(defn convert-page-to-tag!
|
||||
"Converts a Page to a Tag"
|
||||
[page-entity]
|
||||
(if (db/page-exists? (:block/title page-entity) #{:logseq.class/Tag})
|
||||
(notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false)
|
||||
(let [txs [(db-class/build-new-class (db/get-db)
|
||||
{:db/id (:db/id page-entity)
|
||||
:block/title (:block/title page-entity)
|
||||
:block/created-at (:block/created-at page-entity)})
|
||||
[:db/retract (:db/id page-entity) :block/tags :logseq.class/Page]]]
|
||||
(cond (db/page-exists? (:block/title page-entity) #{:logseq.class/Tag})
|
||||
(notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false)
|
||||
(:block/parent page-entity)
|
||||
(notification/show! "Namespaced pages can't be tags" :error false)
|
||||
:else
|
||||
(let [txs [(db-class/build-new-class (db/get-db)
|
||||
{:db/id (:db/id page-entity)
|
||||
:block/title (:block/title page-entity)
|
||||
:block/created-at (:block/created-at page-entity)})
|
||||
[:db/retract (:db/id page-entity) :block/tags :logseq.class/Page]]]
|
||||
|
||||
(db/transact! (state/get-current-repo) txs {:outliner-op :save-block}))))
|
||||
(db/transact! (state/get-current-repo) txs {:outliner-op :save-block}))))
|
||||
|
||||
(defn convert-tag-to-page!
|
||||
[page-entity]
|
||||
(if (db/page-exists? (:block/title page-entity) #{:logseq.class/Page})
|
||||
(notification/show! (str "A page with the name \"" (:block/title page-entity) "\" already exists.") :warning false)
|
||||
(when-not (:logseq.property/built-in? page-entity)
|
||||
(p/let [objects (db-async/<get-tag-objects (state/get-current-repo) (:db/id page-entity))]
|
||||
(let [convert-fn
|
||||
(fn convert-fn []
|
||||
(let [page-txs [[:db/retract (:db/id page-entity) :db/ident]
|
||||
[:db/retract (:db/id page-entity) :block/tags :logseq.class/Tag]
|
||||
[:db/add (:db/id page-entity) :block/tags :logseq.class/Page]]
|
||||
obj-txs (mapcat (fn [obj]
|
||||
(let [tags (map #(db/entity (state/get-current-repo) (:db/id %)) (:block/tags obj))]
|
||||
[{:db/id (:db/id obj)
|
||||
:block/title (db-content/replace-tag-refs-with-page-refs (:block/title obj) tags)}
|
||||
[:db/retract (:db/id obj) :block/tags (:db/id page-entity)]]))
|
||||
objects)
|
||||
txs (concat page-txs obj-txs)]
|
||||
(db/transact! (state/get-current-repo) txs {:outliner-op :save-block})))]
|
||||
(-> (shui/dialog-confirm!
|
||||
"Converting a tag to page also removes tags from any nodes that have that tag. Are you ok with that?"
|
||||
{:id :convert-tag-to-page
|
||||
:data-reminder :ok})
|
||||
(p/then convert-fn)))))))
|
||||
[entity]
|
||||
(if (db/page-exists? (:block/title entity) #{:logseq.class/Page})
|
||||
(notification/show! (str "A page with the name \"" (:block/title entity) "\" already exists.") :warning false)
|
||||
(when-not (:logseq.property/built-in? entity)
|
||||
(if (seq (:logseq.property.class/_extends entity))
|
||||
(notification/show! "This tag cannot be converted because it has tag children. All tag children must be removed or converted before converting this tag." :error false)
|
||||
(p/let [objects (db-async/<get-tag-objects (state/get-current-repo) (:db/id entity))]
|
||||
(let [convert-fn
|
||||
(fn convert-fn []
|
||||
(let [page-txs [[:db/retract (:db/id entity) :db/ident]
|
||||
[:db/retract (:db/id entity) :block/tags :logseq.class/Tag]
|
||||
[:db/retract (:db/id entity) :logseq.property.class/extends]
|
||||
[:db/retract (:db/id entity) :logseq.property.class/properties]
|
||||
[:db/add (:db/id entity) :block/tags :logseq.class/Page]]
|
||||
obj-txs (mapcat (fn [obj]
|
||||
(let [tags (map #(db/entity (state/get-current-repo) (:db/id %)) (:block/tags obj))]
|
||||
[{:db/id (:db/id obj)
|
||||
:block/title (db-content/replace-tag-refs-with-page-refs (:block/title obj) tags)}
|
||||
[:db/retract (:db/id obj) :block/tags (:db/id entity)]]))
|
||||
objects)
|
||||
txs (concat page-txs obj-txs)]
|
||||
(db/transact! (state/get-current-repo) txs {:outliner-op :save-block})))]
|
||||
(-> (shui/dialog-confirm!
|
||||
"Converting a tag to page also removes its tag properties and its tag from all nodes tagged with it. Are you ok with that?"
|
||||
{:id :convert-tag-to-page
|
||||
:data-reminder :ok})
|
||||
(p/then convert-fn))))))))
|
||||
|
||||
(defn <create-class!
|
||||
"Creates a class page and provides class-specific error handling"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns frontend.handler.db-based.recent
|
||||
"Fns related to recent pages feature"
|
||||
(:require [frontend.db :as db]
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.db :as db]
|
||||
[frontend.state :as state]
|
||||
[logseq.db :as ldb]))
|
||||
|
||||
@@ -9,11 +10,12 @@
|
||||
(assert db-id (number? db-id))
|
||||
(when-not (:db/restoring? @state/state)
|
||||
(when-let [page (db/entity db-id)]
|
||||
(let [pages (state/get-recent-pages)]
|
||||
(when-not (or (ldb/hidden? page)
|
||||
((set pages) db-id))
|
||||
(let [new-pages (vec (take 15 (distinct (cons db-id pages))))]
|
||||
(state/set-recent-pages! new-pages)))))))
|
||||
(when-not (string/blank? (:block/title page))
|
||||
(let [pages (state/get-recent-pages)]
|
||||
(when-not (or (ldb/hidden? page)
|
||||
((set pages) db-id))
|
||||
(let [new-pages (vec (take 15 (distinct (cons db-id pages))))]
|
||||
(state/set-recent-pages! new-pages))))))))
|
||||
|
||||
(defn get-recent-pages
|
||||
[]
|
||||
@@ -24,5 +26,6 @@
|
||||
(filter db/page?)
|
||||
(remove ldb/hidden?)
|
||||
(remove (fn [e]
|
||||
(and (ldb/property? e)
|
||||
(true? (:logseq.property/hide? e)))))))
|
||||
(or (and (ldb/property? e)
|
||||
(true? (:logseq.property/hide? e)))
|
||||
(string/blank? (:block/title e)))))))
|
||||
|
||||
@@ -338,14 +338,21 @@
|
||||
true
|
||||
|
||||
:else
|
||||
(not has-children?))]
|
||||
(not has-children?))
|
||||
library? (:library? config)
|
||||
new-block' (if library?
|
||||
(-> new-block
|
||||
(-> (assoc :block/tags #{:logseq.class/Page}
|
||||
:block/name (util/page-name-sanity-lc (:block/title new-block)))
|
||||
(dissoc :block/page)))
|
||||
new-block)]
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :insert-blocks}
|
||||
(save-current-block! {:current-block current-block})
|
||||
(outliner-op/insert-blocks! [new-block] current-block {:sibling? sibling?
|
||||
:keep-uuid? keep-uuid?
|
||||
:ordered-list? ordered-list?
|
||||
:replace-empty-target? replace-empty-target?}))))
|
||||
(outliner-op/insert-blocks! [new-block'] current-block {:sibling? sibling?
|
||||
:keep-uuid? keep-uuid?
|
||||
:ordered-list? ordered-list?
|
||||
:replace-empty-target? replace-empty-target?}))))
|
||||
|
||||
(defn- block-self-alone-when-insert?
|
||||
[config uuid]
|
||||
@@ -528,7 +535,8 @@
|
||||
custom-uuid replace-empty-target? edit-block? ordered-list? other-attrs]
|
||||
:or {sibling? false
|
||||
before? false
|
||||
edit-block? true}}]
|
||||
edit-block? true}
|
||||
:as config}]
|
||||
(when (or page block-uuid)
|
||||
(let [repo (state/get-current-repo)
|
||||
db-based? (config/db-based-graph? repo)
|
||||
@@ -589,10 +597,11 @@
|
||||
(p/do!
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :insert-blocks}
|
||||
(outliner-insert-block! {} block-m new-block {:sibling? sibling?
|
||||
:keep-uuid? true
|
||||
:ordered-list? ordered-list?
|
||||
:replace-empty-target? replace-empty-target?})
|
||||
(outliner-insert-block! config block-m new-block
|
||||
{:sibling? sibling?
|
||||
:keep-uuid? true
|
||||
:ordered-list? ordered-list?
|
||||
:replace-empty-target? replace-empty-target?})
|
||||
(when (and db-based? (seq properties))
|
||||
(property-handler/set-block-properties! repo (:block/uuid new-block) properties)))
|
||||
(when edit-block?
|
||||
@@ -835,6 +844,13 @@
|
||||
(delete-block-aux! block)
|
||||
(when edit-block-f (edit-block-f))))))))))))
|
||||
|
||||
(defn move-blocks!
|
||||
[blocks target sibling?]
|
||||
(when (seq blocks)
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :move-blocks}
|
||||
(outliner-op/move-blocks! blocks target sibling?))))
|
||||
|
||||
(defn delete-block!
|
||||
[repo]
|
||||
(delete-block-inner! repo (get-state)))
|
||||
@@ -1048,14 +1064,24 @@
|
||||
(let [repo (state/get-current-repo)
|
||||
block-uuids (distinct (map #(uuid (dom/attr % "blockid")) dom-blocks))
|
||||
lookup-refs (map (fn [id] [:block/uuid id]) block-uuids)
|
||||
blocks (->> (map db/entity lookup-refs)
|
||||
(remove ldb/page?))
|
||||
top-level-blocks (when (seq blocks) (block-handler/get-top-level-blocks blocks))
|
||||
sorted-blocks (mapcat (fn [block]
|
||||
(tree/get-sorted-block-and-children repo (:db/id block)))
|
||||
top-level-blocks)]
|
||||
(when (seq sorted-blocks)
|
||||
(delete-blocks! repo (map :block/uuid sorted-blocks) sorted-blocks dom-blocks)))))))
|
||||
blocks (map db/entity lookup-refs)
|
||||
pages (filter ldb/page? blocks)
|
||||
pages-with-parent (filter (fn [page] (and (:block/parent page) (not (string/blank? (:block/title page))))) pages)]
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :delete-blocks}
|
||||
(doseq [page pages-with-parent]
|
||||
(outliner-op/remove-block-property! (:db/id page) :block/parent))
|
||||
(let [blocks' (if (seq pages-with-parent)
|
||||
(let [ids (set (map :db/id pages-with-parent))]
|
||||
(remove (fn [b] (ids (:db/id b))) blocks))
|
||||
blocks)]
|
||||
(when (seq blocks')
|
||||
(let [top-level-blocks (block-handler/get-top-level-blocks blocks')
|
||||
sorted-blocks (mapcat (fn [block]
|
||||
(tree/get-sorted-block-and-children repo (:db/id block)))
|
||||
top-level-blocks)]
|
||||
(when (seq sorted-blocks)
|
||||
(delete-blocks! repo (map :block/uuid sorted-blocks) sorted-blocks dom-blocks)))))))))))
|
||||
|
||||
(def url-regex
|
||||
"Didn't use link/plain-link as it is incorrectly detects words as urls."
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
(p/let [_ (when (:convert-page-to-tag? chosen-result)
|
||||
(let [entity (db/entity (:db/id chosen-result))]
|
||||
(when (and (ldb/page? entity) (not (ldb/class? entity)))
|
||||
(db-page-handler/convert-to-tag! entity))))
|
||||
(db-page-handler/convert-page-to-tag! entity))))
|
||||
chosen-result (if (:block/uuid chosen-result)
|
||||
(db/entity [:block/uuid (:block/uuid chosen-result)])
|
||||
chosen-result)
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
:logseq.property/icon :block/alias :logseq.property/enable-history?
|
||||
:logseq.property/exclude-from-graph-view :logseq.property/template-applied-to
|
||||
:logseq.property/hide-empty-value :logseq.property.class/hide-from-node
|
||||
:logseq.property/page-tags :logseq.property/parent
|
||||
:logseq.property/page-tags :logseq.property.class/extends
|
||||
:logseq.property/publishing-public? :logseq.property.user/avatar
|
||||
:logseq.property.user/email :logseq.property.user/name})
|
||||
|
||||
|
||||
@@ -354,6 +354,9 @@
|
||||
(gobj/get range "endContainer")
|
||||
(gobj/get range "endOffset"))
|
||||
(let [contents (.cloneContents pre-caret-range)
|
||||
;; Remove all `.select-none` nodes
|
||||
_ (doseq [el (.querySelectorAll contents ".select-none")]
|
||||
(.remove el))
|
||||
html (some-> (first (.-childNodes contents))
|
||||
(gobj/get "innerHTML")
|
||||
str)
|
||||
@@ -365,7 +368,7 @@
|
||||
(string/ends-with? html "<div class=\"is-paragraph\"></div></div></span></div></div></div>")
|
||||
;; multiple lines with a new line
|
||||
(string/ends-with? html "<br></div></div></span></div></div></div>")))
|
||||
value (.toString pre-caret-range)]
|
||||
value (.-textContent contents)]
|
||||
(if br-ended?
|
||||
(str value "\n")
|
||||
value)))))
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
(ns frontend.worker.db.migrate
|
||||
"Handles SQLite and datascript migrations for DB graphs"
|
||||
(:require [clojure.walk :as walk]
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.walk :as walk]
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
[frontend.worker.util :as worker-util]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.common.order :as db-order]
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.frontend.schema :as db-schema]
|
||||
@@ -15,121 +19,121 @@
|
||||
;; Frontend migrations
|
||||
;; ===================
|
||||
|
||||
(comment
|
||||
(defn- rename-properties-aux
|
||||
[db props-to-rename]
|
||||
(let [property-tx (map
|
||||
(fn [[old new]]
|
||||
(let [e-new (d/entity db new)
|
||||
e-old (d/entity db old)]
|
||||
(if e-new
|
||||
(when e-old
|
||||
[:db/retractEntity (:db/id e-old)])
|
||||
(merge {:db/id (:db/id (d/entity db old))
|
||||
:db/ident new}
|
||||
(when-let [new-title (get-in db-property/built-in-properties [new :title])]
|
||||
{:block/title new-title
|
||||
:block/name (common-util/page-name-sanity-lc new-title)})))))
|
||||
props-to-rename)
|
||||
titles-tx (->> (d/datoms db :avet :block/title)
|
||||
(keep (fn [d]
|
||||
(let [title (:v d)]
|
||||
(if (string? title)
|
||||
(when-let [props (seq (filter (fn [[old _new]] (string/includes? (:v d) (str old))) props-to-rename))]
|
||||
(let [title' (reduce (fn [title [old new]]
|
||||
(string/replace title (str old) (str new))) title props)]
|
||||
[:db/add (:e d) :block/title title']))
|
||||
[:db/retract (:e d) :block/title])))))
|
||||
sorting-tx (->> (d/datoms db :avet :logseq.property.table/sorting)
|
||||
(keep (fn [d]
|
||||
(when (coll? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]]
|
||||
(some (fn [item] (= old (:id item))) (:v d))) props-to-rename))]
|
||||
(let [value (reduce
|
||||
(fn [sorting [old new]]
|
||||
(mapv
|
||||
(fn [item]
|
||||
(if (= old (:id item))
|
||||
(assoc item :id new)
|
||||
item))
|
||||
sorting))
|
||||
(:v d)
|
||||
props)]
|
||||
[:db/add (:e d) :logseq.property.table/sorting value]))))))
|
||||
sized-columns-tx (->> (d/datoms db :avet :logseq.property.table/sized-columns)
|
||||
(defn- rename-properties-aux
|
||||
[db props-to-rename]
|
||||
(let [property-tx (map
|
||||
(fn [[old new]]
|
||||
(let [e-new (d/entity db new)
|
||||
e-old (d/entity db old)]
|
||||
(if e-new
|
||||
(when e-old
|
||||
[:db/retractEntity (:db/id e-old)])
|
||||
(merge {:db/id (:db/id (d/entity db old))
|
||||
:db/ident new}
|
||||
(when-let [new-title (get-in db-property/built-in-properties [new :title])]
|
||||
{:block/title new-title
|
||||
:block/name (common-util/page-name-sanity-lc new-title)})))))
|
||||
props-to-rename)
|
||||
titles-tx (->> (d/datoms db :avet :block/title)
|
||||
(keep (fn [d]
|
||||
(let [title (:v d)]
|
||||
(if (string? title)
|
||||
(when-let [props (seq (filter (fn [[old _new]] (string/includes? (:v d) (str old))) props-to-rename))]
|
||||
(let [title' (reduce (fn [title [old new]]
|
||||
(string/replace title (str old) (str new))) title props)]
|
||||
[:db/add (:e d) :block/title title']))
|
||||
[:db/retract (:e d) :block/title])))))
|
||||
sorting-tx (->> (d/datoms db :avet :logseq.property.table/sorting)
|
||||
(keep (fn [d]
|
||||
(when (coll? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]]
|
||||
(some (fn [item] (= old (:id item))) (:v d))) props-to-rename))]
|
||||
(let [value (reduce
|
||||
(fn [sorting [old new]]
|
||||
(mapv
|
||||
(fn [item]
|
||||
(if (= old (:id item))
|
||||
(assoc item :id new)
|
||||
item))
|
||||
sorting))
|
||||
(:v d)
|
||||
props)]
|
||||
[:db/add (:e d) :logseq.property.table/sorting value]))))))
|
||||
sized-columns-tx (->> (d/datoms db :avet :logseq.property.table/sized-columns)
|
||||
(keep (fn [d]
|
||||
(when (map? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]] (get (:v d) old)) props-to-rename))]
|
||||
(let [value (reduce
|
||||
(fn [sizes [old new]]
|
||||
(if-let [size (get sizes old)]
|
||||
(-> sizes
|
||||
(dissoc old)
|
||||
(assoc new size))
|
||||
sizes))
|
||||
(:v d)
|
||||
props)]
|
||||
[:db/add (:e d) :logseq.property.table/sized-columns value]))))))
|
||||
hidden-columns-tx (mapcat
|
||||
(fn [[old new]]
|
||||
(->> (d/datoms db :avet :logseq.property.table/hidden-columns old)
|
||||
(mapcat (fn [d]
|
||||
[[:db/retract (:e d) :logseq.property.table/hidden-columns old]
|
||||
[:db/add (:e d) :logseq.property.table/hidden-columns new]]))))
|
||||
props-to-rename)
|
||||
ordered-columns-tx (->> (d/datoms db :avet :logseq.property.table/ordered-columns)
|
||||
(keep (fn [d]
|
||||
(when (map? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]] (get (:v d) old)) props-to-rename))]
|
||||
(when (coll? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]] ((set (:v d)) old)) props-to-rename))]
|
||||
(let [value (reduce
|
||||
(fn [sizes [old new]]
|
||||
(if-let [size (get sizes old)]
|
||||
(-> sizes
|
||||
(dissoc old)
|
||||
(assoc new size))
|
||||
sizes))
|
||||
(fn [col [old new]]
|
||||
(mapv (fn [v] (if (= old v) new v)) col))
|
||||
(:v d)
|
||||
props)]
|
||||
[:db/add (:e d) :logseq.property.table/sized-columns value]))))))
|
||||
hidden-columns-tx (mapcat
|
||||
(fn [[old new]]
|
||||
(->> (d/datoms db :avet :logseq.property.table/hidden-columns old)
|
||||
(mapcat (fn [d]
|
||||
[[:db/retract (:e d) :logseq.property.table/hidden-columns old]
|
||||
[:db/add (:e d) :logseq.property.table/hidden-columns new]]))))
|
||||
props-to-rename)
|
||||
ordered-columns-tx (->> (d/datoms db :avet :logseq.property.table/ordered-columns)
|
||||
(keep (fn [d]
|
||||
(when (coll? (:v d))
|
||||
(when-let [props (seq (filter (fn [[old _new]] ((set (:v d)) old)) props-to-rename))]
|
||||
(let [value (reduce
|
||||
(fn [col [old new]]
|
||||
(mapv (fn [v] (if (= old v) new v)) col))
|
||||
(:v d)
|
||||
props)]
|
||||
[:db/add (:e d) :logseq.property.table/ordered-columns value]))))))
|
||||
filters-tx (->> (d/datoms db :avet :logseq.property.table/filters)
|
||||
(keep (fn [d]
|
||||
(let [filters (:filters (:v d))]
|
||||
(when (coll? filters)
|
||||
(when-let [props (seq (filter (fn [[old _new]]
|
||||
(some (fn [item] (and (vector? item)
|
||||
(= old (first item)))) filters)) props-to-rename))]
|
||||
(let [value (update (:v d) :filters
|
||||
(fn [col]
|
||||
(reduce
|
||||
(fn [col [old new]]
|
||||
(mapv (fn [item]
|
||||
(if (and (vector? item) (= old (first item)))
|
||||
(vec (cons new (rest item)))
|
||||
item))
|
||||
col))
|
||||
col
|
||||
props)))]
|
||||
[:db/add (:e d) :logseq.property.table/filters value])))))))]
|
||||
(concat property-tx
|
||||
titles-tx
|
||||
sorting-tx
|
||||
sized-columns-tx
|
||||
hidden-columns-tx
|
||||
ordered-columns-tx
|
||||
filters-tx)))
|
||||
[:db/add (:e d) :logseq.property.table/ordered-columns value]))))))
|
||||
filters-tx (->> (d/datoms db :avet :logseq.property.table/filters)
|
||||
(keep (fn [d]
|
||||
(let [filters (:filters (:v d))]
|
||||
(when (coll? filters)
|
||||
(when-let [props (seq (filter (fn [[old _new]]
|
||||
(some (fn [item] (and (vector? item)
|
||||
(= old (first item)))) filters)) props-to-rename))]
|
||||
(let [value (update (:v d) :filters
|
||||
(fn [col]
|
||||
(reduce
|
||||
(fn [col [old new]]
|
||||
(mapv (fn [item]
|
||||
(if (and (vector? item) (= old (first item)))
|
||||
(vec (cons new (rest item)))
|
||||
item))
|
||||
col))
|
||||
col
|
||||
props)))]
|
||||
[:db/add (:e d) :logseq.property.table/filters value])))))))]
|
||||
(concat property-tx
|
||||
titles-tx
|
||||
sorting-tx
|
||||
sized-columns-tx
|
||||
hidden-columns-tx
|
||||
ordered-columns-tx
|
||||
filters-tx)))
|
||||
|
||||
(defn- rename-properties
|
||||
[props-to-rename]
|
||||
(fn [conn _search-db]
|
||||
(when (ldb/db-based-graph? @conn)
|
||||
(let [props-tx (rename-properties-aux @conn props-to-rename)]
|
||||
;; Property changes need to be in their own tx for subsequent uses of properties to take effect
|
||||
(ldb/transact! conn props-tx {:db-migrate? true})
|
||||
|
||||
(mapcat (fn [[old new]]
|
||||
;; can't use datoms b/c user properties aren't indexed
|
||||
(->> (d/q '[:find ?b ?prop-v :in $ ?prop :where [?b ?prop ?prop-v]] @conn old)
|
||||
(mapcat (fn [[id prop-value]]
|
||||
[[:db/retract id old]
|
||||
[:db/add id new prop-value]]))))
|
||||
props-to-rename)))))
|
||||
(defn rename-properties
|
||||
[props-to-rename & {:keys [replace-fn]}]
|
||||
(fn [conn]
|
||||
(when (ldb/db-based-graph? @conn)
|
||||
(let [props-tx (rename-properties-aux @conn props-to-rename)
|
||||
fix-tx (mapcat (fn [[old new]]
|
||||
;; can't use datoms b/c user properties aren't indexed
|
||||
(->> (d/q '[:find ?b ?prop-v :in $ ?prop :where [?b ?prop ?prop-v]] @conn old)
|
||||
(mapcat (fn [[id prop-value]]
|
||||
(if (fn? replace-fn)
|
||||
(replace-fn id prop-value)
|
||||
[[:db/retract id old]
|
||||
[:db/add id new prop-value]])))))
|
||||
props-to-rename)]
|
||||
(concat props-tx fix-tx)))))
|
||||
|
||||
(comment
|
||||
(defn- rename-classes
|
||||
[classes-to-rename]
|
||||
(fn [conn _search-db]
|
||||
@@ -142,6 +146,53 @@
|
||||
:block/name (common-util/page-name-sanity-lc new-title)})))
|
||||
classes-to-rename)))))
|
||||
|
||||
(defn fix-rename-parent-to-extends
|
||||
[conn _search-db]
|
||||
(let [db @conn
|
||||
parent-entity (d/entity db :logseq.property/parent)]
|
||||
(when parent-entity
|
||||
(let [old-p :logseq.property/parent
|
||||
new-p :logseq.property.class/extends
|
||||
f (rename-properties
|
||||
{old-p new-p}
|
||||
{:replace-fn (fn [id prop-value]
|
||||
(let [page (d/entity db id)
|
||||
new-p' (if (ldb/class? page) new-p :block/parent)]
|
||||
[[:db/retract id old-p]
|
||||
[:db/add id new-p' prop-value]]))})
|
||||
rename-property-tx (f conn)
|
||||
library-page (if-let [page (ldb/get-built-in-page db common-config/library-page-name)]
|
||||
page
|
||||
(-> (sqlite-util/build-new-page common-config/library-page-name)
|
||||
sqlite-create-graph/mark-block-as-built-in))
|
||||
library-id (:block/uuid library-page)
|
||||
library-page-tx (when-not (de/entity? library-page)
|
||||
[library-page])
|
||||
pages-with-parent (->> (d/datoms db :avet :logseq.property/parent)
|
||||
(keep (fn [d]
|
||||
(let [e (d/entity db (:e d))]
|
||||
(when-not (ldb/class? e)
|
||||
e)))))
|
||||
parents (->> pages-with-parent
|
||||
(map :logseq.property/parent)
|
||||
(common-util/distinct-by :db/id))
|
||||
top-parents (remove :logseq.property/parent parents)
|
||||
top-parent-ids (set (map :db/id top-parents))
|
||||
move-top-parents-to-library (map (fn [parent]
|
||||
{:db/id (:db/id parent)
|
||||
:block/parent [:block/uuid library-id]
|
||||
:block/order (db-order/gen-key)}) top-parents)
|
||||
update-children-parent-and-order (->> pages-with-parent
|
||||
(remove (fn [page] (top-parent-ids (:db/id page))))
|
||||
(map (fn [page]
|
||||
{:db/id (:db/id page)
|
||||
:block/order (db-order/gen-key)})))]
|
||||
(concat
|
||||
rename-property-tx
|
||||
library-page-tx
|
||||
move-top-parents-to-library
|
||||
update-children-parent-and-order)))))
|
||||
|
||||
(defn separate-classes-and-properties
|
||||
[conn _sqlite-db]
|
||||
;; find all properties that're classes, create new properties to separate them
|
||||
@@ -198,7 +249,8 @@
|
||||
"A vec of tuples defining datascript migrations. Each tuple consists of the
|
||||
schema version integer and a migration map. A migration map can have keys of :properties, :classes
|
||||
and :fix."
|
||||
[["65.0" {:fix separate-classes-and-properties}]])
|
||||
[["65.0" {:fix separate-classes-and-properties}]
|
||||
["65.1" {:fix fix-rename-parent-to-extends}]])
|
||||
|
||||
(let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
|
||||
schema-version->updates)))]
|
||||
@@ -302,7 +354,6 @@
|
||||
"Migrate 'frontend' datascript schema and data. To add a new migration,
|
||||
add an entry to schema-version->updates and bump db-schema/version"
|
||||
[conn search-db]
|
||||
(prn :debug :migrate)
|
||||
(when (ldb/db-based-graph? @conn)
|
||||
(let [db @conn
|
||||
version-in-db (db-schema/parse-schema-version (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0))
|
||||
|
||||
@@ -94,10 +94,10 @@
|
||||
delete-property-tx
|
||||
[[:db.fn/retractEntity (:db/id page)]])
|
||||
restore-class-parent-tx (when db-based?
|
||||
(->> (filter (fn [p] (ldb/class? p)) (:logseq.property/_parent page))
|
||||
(->> (filter ldb/class? (:logseq.property.class/_extends page))
|
||||
(map (fn [p]
|
||||
{:db/id (:db/id p)
|
||||
:logseq.property/parent :logseq.class/Root}))))
|
||||
:logseq.property.class/extends :logseq.class/Root}))))
|
||||
tx-data (concat truncate-blocks-tx-data
|
||||
restore-class-parent-tx
|
||||
delete-page-tx
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[datascript.impl.entity :as de]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.common.util.namespace :as ns-util]
|
||||
[logseq.db :as ldb]
|
||||
[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.entity-util :as entity-util]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]
|
||||
@@ -74,23 +76,34 @@
|
||||
title))
|
||||
|
||||
(defn- get-page-by-parent-name
|
||||
[db parent-title child-title]
|
||||
[db parent-title child-title class?]
|
||||
(some->>
|
||||
(d/q
|
||||
'[:find [?b ...]
|
||||
:in $ ?parent-name ?child-name
|
||||
:in $ ?attribute ?parent-name ?child-name
|
||||
:where
|
||||
[?b :logseq.property/parent ?p]
|
||||
[?b ?attribute ?p]
|
||||
[?b :block/name ?child-name]
|
||||
[?p :block/name ?parent-name]]
|
||||
db
|
||||
(if class? :logseq.property.class/extends :block/parent)
|
||||
(common-util/page-name-sanity-lc parent-title)
|
||||
(common-util/page-name-sanity-lc child-title))
|
||||
first
|
||||
(d/entity db)))
|
||||
|
||||
(defn- page-with-parent-and-order
|
||||
"Apply to namespace pages"
|
||||
[db page & {:keys [parent]}]
|
||||
(let [library (ldb/get-built-in-page db common-config/library-page-name)]
|
||||
(when (nil? library)
|
||||
(throw (ex-info "Library page doesn't exist" {})))
|
||||
(assoc page
|
||||
:block/parent (or parent (:db/id library))
|
||||
:block/order (db-order/gen-key))))
|
||||
|
||||
(defn- split-namespace-pages
|
||||
[db page date-formatter]
|
||||
[db page date-formatter create-class?]
|
||||
(let [{:block/keys [title] block-uuid :block/uuid} page]
|
||||
(->>
|
||||
(if (and (or (entity-util/class? page)
|
||||
@@ -100,20 +113,19 @@
|
||||
parts (->> (string/split title ns-util/parent-re)
|
||||
(map string/trim)
|
||||
(remove string/blank?))
|
||||
pages (doall
|
||||
(map-indexed
|
||||
(fn [idx part]
|
||||
(let [last-part? (= idx (dec (count parts)))
|
||||
page (if (zero? idx)
|
||||
(ldb/get-page db part)
|
||||
(get-page-by-parent-name db (nth parts (dec idx)) part))
|
||||
result (or page
|
||||
(gp-block/page-name->map part db true date-formatter
|
||||
{:page-uuid (when last-part? block-uuid)
|
||||
:skip-existing-page-check? true
|
||||
:class? class?}))]
|
||||
result))
|
||||
parts))]
|
||||
pages (map-indexed
|
||||
(fn [idx part]
|
||||
(let [last-part? (= idx (dec (count parts)))
|
||||
page (if (zero? idx)
|
||||
(ldb/get-page db part)
|
||||
(get-page-by-parent-name db (nth parts (dec idx)) part create-class?))
|
||||
result (or page
|
||||
(gp-block/page-name->map part db true date-formatter
|
||||
{:page-uuid (when last-part? block-uuid)
|
||||
:skip-existing-page-check? true
|
||||
:class? class?}))]
|
||||
result))
|
||||
parts)]
|
||||
(cond
|
||||
(and (not class?) (not (every? ldb/internal-page? pages)))
|
||||
(throw (ex-info "Cannot create this page unless all parents are pages"
|
||||
@@ -136,7 +148,7 @@
|
||||
(if class?
|
||||
(cond
|
||||
(and (de/entity? page) (ldb/class? page))
|
||||
(assoc page :logseq.property/parent parent-eid)
|
||||
(assoc page :logseq.property.class/extends parent-eid)
|
||||
|
||||
(de/entity? page) ; page exists but not a class, avoid converting here because this could be troublesome.
|
||||
nil
|
||||
@@ -145,10 +157,10 @@
|
||||
(db-class/build-new-class db page)
|
||||
|
||||
:else
|
||||
(db-class/build-new-class db (assoc page :logseq.property/parent parent-eid)))
|
||||
(if (or (de/entity? page) (zero? idx))
|
||||
(db-class/build-new-class db (assoc page :logseq.property.class/extends parent-eid)))
|
||||
(if (de/entity? page)
|
||||
page
|
||||
(assoc page :logseq.property/parent parent-eid)))))
|
||||
(page-with-parent-and-order db page {:parent parent-eid})))))
|
||||
pages)))
|
||||
[page])
|
||||
(remove nil?))))
|
||||
@@ -188,12 +200,12 @@
|
||||
[:db/retract [:block/uuid (:block/uuid existing-page)] :block/tags :logseq.class/Page]]]
|
||||
{:tx-meta tx-meta
|
||||
:tx-data tx-data})))
|
||||
(let [page (gp-block/page-name->map title db true date-formatter
|
||||
{:class? class?
|
||||
:page-uuid (when (uuid? uuid) uuid)
|
||||
:skip-existing-page-check? true})
|
||||
(let [page (gp-block/page-name->map title db true date-formatter
|
||||
{:class? class?
|
||||
:page-uuid (when (uuid? uuid) uuid)
|
||||
:skip-existing-page-check? true})
|
||||
[page parents] (if (and (text/namespace-page? title) split-namespace?)
|
||||
(let [pages (split-namespace-pages db page date-formatter)]
|
||||
(let [pages (split-namespace-pages db page date-formatter class?)]
|
||||
[(last pages) (butlast pages)])
|
||||
[page nil])]
|
||||
(when (and page (or (nil? (:db/ident page))
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
(let [object (d/entity db e)
|
||||
template-blocks (->> (mapcat (fn [id]
|
||||
(let [tag (d/entity db id)
|
||||
parents (ldb/get-page-parents tag {:node-class? true})
|
||||
parents (ldb/get-class-extends tag)
|
||||
templates (mapcat :logseq.property/_template-applied-to (conj parents tag))]
|
||||
(cond->> templates
|
||||
journal-page
|
||||
|
||||
@@ -273,7 +273,7 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
* :limit - Number of result to limit search results. Defaults to 100
|
||||
* :dev? - Allow all nodes to be seen for development. Defaults to false
|
||||
* :built-in? - Whether to return public built-in nodes for db graphs. Defaults to false"
|
||||
[repo conn search-db q {:keys [limit page enable-snippet? built-in? dev? page-only?]
|
||||
[repo conn search-db q {:keys [limit page enable-snippet? built-in? dev? page-only? library-page-search?]
|
||||
:as option
|
||||
:or {enable-snippet? true}}]
|
||||
(when-not (string/blank? q)
|
||||
@@ -308,26 +308,29 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
(let [{:keys [id page title snippet]} result
|
||||
block-id (uuid id)]
|
||||
(when-let [block (d/entity @conn [:block/uuid block-id])]
|
||||
(when (if dev?
|
||||
true
|
||||
(if built-in?
|
||||
(or (not (ldb/built-in? block))
|
||||
(not (ldb/private-built-in-page? block))
|
||||
(ldb/class? block))
|
||||
(or (not (ldb/built-in? block))
|
||||
(ldb/class? block))))
|
||||
{:db/id (:db/id block)
|
||||
:block/uuid block-id
|
||||
:block/title (if (ldb/page? block)
|
||||
(ldb/get-title-with-parents block)
|
||||
(or snippet title))
|
||||
:block/page (if (common-util/uuid-string? page)
|
||||
(uuid page)
|
||||
nil)
|
||||
:block/tags (seq (map :db/id (:block/tags block)))
|
||||
:page? (ldb/page? block)
|
||||
:alias (some-> (first (:block/_alias block))
|
||||
(select-keys [:block/uuid :block/title]))}))))))
|
||||
(when-not (and library-page-search?
|
||||
(or (:block/parent block)
|
||||
(not (ldb/internal-page? block)))) ; remove pages that already have parents
|
||||
(when (if dev?
|
||||
true
|
||||
(if built-in?
|
||||
(or (not (ldb/built-in? block))
|
||||
(not (ldb/private-built-in-page? block))
|
||||
(ldb/class? block))
|
||||
(or (not (ldb/built-in? block))
|
||||
(ldb/class? block))))
|
||||
{:db/id (:db/id block)
|
||||
:block/uuid block-id
|
||||
:block/title (if (ldb/page? block)
|
||||
(ldb/get-title-with-parents block)
|
||||
(or snippet title))
|
||||
:block/page (if (common-util/uuid-string? page)
|
||||
(uuid page)
|
||||
nil)
|
||||
:block/tags (seq (map :db/id (:block/tags block)))
|
||||
:page? (ldb/page? block)
|
||||
:alias (some-> (first (:block/_alias block))
|
||||
(select-keys [:block/uuid :block/title]))})))))))
|
||||
page-or-object-result (filter (fn [b] (or (:page? b) (:block/tags result))) result)]
|
||||
(->>
|
||||
(concat page-or-object-result
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
:right-side-bar/switch-theme "Skakel oor na die {1} tema"
|
||||
:right-side-bar/contents "Inhoud"
|
||||
:right-side-bar/block-ref "Blok verwysing"
|
||||
:page/delete-confirmation "Is jy seker jy wil die bladsy uitvee?"
|
||||
:file/name "Lêer naam"
|
||||
:file/last-modified-at "Laas verander op"
|
||||
:file/no-data "Geen data"
|
||||
|
||||
@@ -419,7 +419,6 @@
|
||||
:page/copy-page-url "Copiar URL de la pàgina"
|
||||
:page/created-at "Creada el"
|
||||
:page/delete "Eliminar pàgina"
|
||||
:page/delete-confirmation "Està segur que desitja eliminar aquesta pàgina i el seu arxiu?"
|
||||
:page/illegal-page-name "¡Nom de pàgina no permès!"
|
||||
:page/logseq-is-having-a-problem "Logseq està tenint un problema. Per intentar tornar a un estat de treball, intenti el següent procediment en ordre:"
|
||||
:page/make-private "Fer privada"
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq má problém. Chcete-li se pokusit uvést jej zpět do funkčního stavu, vyzkoušejte prosím následující bezpečné kroky v uvedeném pořadí:"
|
||||
:page/step "Krok {1}"
|
||||
:page/try "Zkusit"
|
||||
:page/delete-confirmation "Jste si jisti, že chcete tuto stránku a její soubor odstranit?"
|
||||
:page/open-in-finder "Otevřít v adresáři"
|
||||
:page/open-with-default-app "Otevřít pomocí výchozí aplikace"
|
||||
:page/make-public "Označit stránku jjako veřejnou"
|
||||
|
||||
@@ -235,7 +235,6 @@
|
||||
:page/copy-page-url "Seiten-URL kopieren"
|
||||
:page/created-at "Erstellt am"
|
||||
:page/delete "Seite löschen"
|
||||
:page/delete-confirmation "Diese Seite und die zugehörige Datei löschen?"
|
||||
:page/illegal-page-name "Seitenname nicht zulässig!"
|
||||
:page/logseq-is-having-a-problem "Logseq hat ein Problem festgestellt. Versuche zurückzukehren ..."
|
||||
:page/make-private "Privat machen"
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
:page/logseq-is-having-a-problem "Logseq is having a problem. To try to get it back to a working state, please try the following safe steps in order:"
|
||||
:page/step "Step {1}"
|
||||
:page/try "Try"
|
||||
:page/delete-confirmation "Are you sure you want to delete this page and its file?"
|
||||
:page/delete-confirmation "Are you sure you want to delete this page?"
|
||||
:page/db-delete-confirmation "Are you sure you want to delete this page?"
|
||||
:page/open-in-finder "Open in directory"
|
||||
:page/open-with-default-app "Open with default app"
|
||||
|
||||
@@ -418,7 +418,6 @@
|
||||
:page/copy-page-url "Copiar URL de la página"
|
||||
:page/created-at "Creada el"
|
||||
:page/delete "Eliminar página"
|
||||
:page/delete-confirmation "¿Está seguro que desea eliminar esta página y su archivo?"
|
||||
:page/illegal-page-name "¡Nombre de página ilegal!"
|
||||
:page/logseq-is-having-a-problem "Logseq está teniendo un problema. Para intentar volver a un estado de trabajo, intenta los siguientes pasos seguros en orden:"
|
||||
:page/make-private "Hacer privada"
|
||||
|
||||
@@ -106,7 +106,6 @@
|
||||
:page/logseq-is-having-a-problem "لاگسیک دچار مشکلی شده است. برای بازگرداندن آن به حالت قابل استفاده لطفا گامهای زیر را به ترتیب انجام دهید:"
|
||||
:page/step "گام {1}"
|
||||
:page/try "امتحان کنید"
|
||||
:page/delete-confirmation "مطمئنید که میخواید این برگه به همراه پروندهاش را پاک کنید؟"
|
||||
:page/open-in-finder "باز کردن در پوشه"
|
||||
:page/open-with-default-app "باز کردن با برنامه پیشفرض"
|
||||
:page/make-public "عمومی ساختن برای انتشار"
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
:right-side-bar/all-pages "Toutes les pages"
|
||||
:right-side-bar/flashcards "Cartes-mémoire"
|
||||
:left-side-bar/journals "Journaux"
|
||||
:page/delete-confirmation "Voulez-vous vraiment supprimer la page ?"
|
||||
:page/make-public "Rendre la page publique"
|
||||
:page/make-private "Rendre la page privée"
|
||||
:page/delete "Supprimer la page (supprime le fichier)"
|
||||
|
||||
@@ -125,7 +125,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq mengalami masalah. Untuk mencoba mengembalikannya ke keadaan yang berfungsi, silakan coba langkah-langkah aman berikut ini secara berurutan:"
|
||||
:page/step "Langkah {1}"
|
||||
:page/try "Coba"
|
||||
:page/delete-confirmation "Apakah Anda yakin ingin menghapus halaman ini beserta berkasnya?"
|
||||
:page/open-in-finder "Buka dalam direktori"
|
||||
:page/open-with-default-app "Buka dengan aplikasi default"
|
||||
:page/make-public "Buat publik untuk dipublikasikan"
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
:left-side-bar/journals "Diario"
|
||||
:left-side-bar/nav-favorites "Preferiti"
|
||||
:left-side-bar/nav-recent-pages "Recenti"
|
||||
:page/delete-confirmation "Sei sicuro di voler eliminare questa pagina e i suoi dati?"
|
||||
:page/open-in-finder "Apri nella cartella"
|
||||
:page/open-with-default-app "Apri con l'app predefinita"
|
||||
:page/make-public "Segna come pubblico per la pubblicazione"
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseqになにか問題があります。きちんと動く状態に戻すため、以下の項目を上から順番に実行してください:"
|
||||
:page/step "ステップ{1}"
|
||||
:page/try "試す"
|
||||
:page/delete-confirmation "このページとページのファイルを削除してもよいですか?"
|
||||
:page/open-in-finder "ディレクトリで開く"
|
||||
:page/open-with-default-app "既定のアプリで開く"
|
||||
:page/make-public "パブリッシュのため公開する"
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
:left-side-bar/journals "일지"
|
||||
:left-side-bar/nav-favorites "즐겨찾기"
|
||||
:left-side-bar/nav-recent-pages "최근 페이지"
|
||||
:page/delete-confirmation "이 페이지와 페이지의 파일들을 삭제하시겠습니까?"
|
||||
:page/open-in-finder "디렉토리에서 열기"
|
||||
:page/open-with-default-app "기본 앱으로 열기"
|
||||
:page/make-public "출판 전 공개 상태로 만들기"
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
:left-side-bar/journals "Dagbøker"
|
||||
:left-side-bar/nav-favorites "Favoritter"
|
||||
:left-side-bar/nav-recent-pages "Nylig"
|
||||
:page/delete-confirmation "Er du sikker på at du vil slette denne siden og filen dens?"
|
||||
:page/open-in-finder "Åpne i mappe"
|
||||
:page/open-with-default-app "Åpne med forhåndsvalgt app"
|
||||
:page/make-public "Gjør den offentlig for publisering"
|
||||
|
||||
@@ -97,7 +97,6 @@
|
||||
:page/copy-page-url "Kopieer pagina URL"
|
||||
:page/created-at "Aangemaakt op"
|
||||
:page/delete "Verwijder pagina"
|
||||
:page/delete-confirmation "Weet u zeker dat u deze pagina wilt verwijderen..."
|
||||
:page/make-private "Maak prive"
|
||||
:page/make-public "Maak publiek"
|
||||
:page/open-backup-directory "Open backups map pagina"
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
:left-side-bar/journals "Dzienniki"
|
||||
:left-side-bar/nav-favorites "Ulubione"
|
||||
:left-side-bar/nav-recent-pages "Ostatnio odwiedzane"
|
||||
:page/delete-confirmation "Czy jesteś pewien że chcesz usunąć tę stronę i jej plik?"
|
||||
:page/open-in-finder "Otwórz w przeglądarce plików"
|
||||
:page/open-with-default-app "Otwórz w domyślnej aplikacji"
|
||||
:page/make-public "Oznacz jako publiczną"
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq está com um problema. Para tentar fazê-lo voltar a funcionar, siga as etapas seguras a seguir:"
|
||||
:page/step "Etapa {1}"
|
||||
:page/try "Tentar"
|
||||
:page/delete-confirmation "Tem certeza de que deseja excluir esta página e seu arquivo?"
|
||||
:page/open-in-finder "Abrir no diretório"
|
||||
:page/open-with-default-app "Abrir com o aplicativo padrão"
|
||||
:page/make-public "Torná-la pública para publicação"
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq está tendo um problema. Para tentar colocá-lo de volta em um estado de funcionamento, por favor tente os seguintes passos seguros em ordem:"
|
||||
:page/step "Passo {1}"
|
||||
:page/try "Tentar"
|
||||
:page/delete-confirmation "Tem a certeza de que quer apagar esta página e o respetivo ficheiro?"
|
||||
:page/open-in-finder "Abrir em pasta"
|
||||
:page/open-with-default-app "Abrir com a aplicação predefinida"
|
||||
:page/make-public "Tornar pública para publicação"
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
:page/logseq-is-having-a-problem "У Logseq возникла проблема. Чтобы попытаться вернуть его в рабочее состояние, пожалуйста, попробуйте выполнить следующие безопасные шаги по порядку:"
|
||||
:page/step "Шаг {1}"
|
||||
:page/try "Попробовать"
|
||||
:page/delete-confirmation "Вы уверены, что хотите удалить эту страницу и её файл(ы)?"
|
||||
:page/open-in-finder "Открыть в каталоге"
|
||||
:page/open-with-default-app "Открыть в приложении по умолчанию"
|
||||
:page/make-public "Сделать доступной для публикации"
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq má problém. Skúste tieto bezpečné kroky, aby ste ho dostali do funkčného stavu:"
|
||||
:page/step "Krok {1}"
|
||||
:page/try "Vyskúšať"
|
||||
:page/delete-confirmation "Naozaj chcete odstrániť túto stránku a jej súbor?"
|
||||
:page/open-in-finder "Otvoriť v adresári"
|
||||
:page/open-with-default-app "Otvoriť v predvolenej aplikácii"
|
||||
:page/make-public "Označiť stránku ako verejnú"
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq'te bir sorun var. Tekrar çalışır duruma getirmek için lütfen aşağıdaki güvenli adımları sırayla deneyin:"
|
||||
:page/step "{1}. Adım"
|
||||
:page/try "Deneyin"
|
||||
:page/delete-confirmation "Bu sayfayı ve dosyasını silmek istediğinizden emin misiniz?"
|
||||
:page/open-in-finder "Dizini aç"
|
||||
:page/open-with-default-app "Varsayılan uygulamayla aç"
|
||||
:page/make-public "Yayımlamak için herkese açık hale getir"
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
:page/logseq-is-having-a-problem "У Logseq виникла проблема. Щоб спробувати повернути його до робочого стану, виконайте такі безпечні кроки по порядку:"
|
||||
:page/step "Крок {1}"
|
||||
:page/try "Спробувати"
|
||||
:page/delete-confirmation "Ви впевнені, що хочете видалити цю сторінку та її файл?"
|
||||
:page/open-in-finder "Відкрити у директорії"
|
||||
:page/open-with-default-app "Відкрити за допомогою програми за умовчанням"
|
||||
:page/make-public "Зробіти загальнодоступним для публікації"
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
:left-side-bar/journals "日志"
|
||||
:left-side-bar/nav-favorites "收藏页面"
|
||||
:left-side-bar/nav-recent-pages "最近使用"
|
||||
:page/delete-confirmation "您确定要删除此页面和文件吗?"
|
||||
:page/open-in-finder "打开文件对应目录"
|
||||
:page/open-with-default-app "用默认应用打开文件"
|
||||
:page/make-public "导出 HTML 时发布本页面"
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
:page/logseq-is-having-a-problem "Logseq 出了些問題。請按照以下安全步驟將其恢復到正常狀態:"
|
||||
:page/step "步驟 {1}"
|
||||
:page/try "嘗試"
|
||||
:page/delete-confirmation "你確定想刪除此頁面檔案嗎?"
|
||||
:page/open-in-finder "開啟資料夾"
|
||||
:page/open-with-default-app "使用預設應用程式開啟"
|
||||
:page/make-public "將其公開讓所有人均可檢視"
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user