Просмотр исходного кода

Move property-edit to file-based.property

Also, util.property -> file-based.property.util
Tienson Qin 2 лет назад
Родитель
Сommit
1671f540fb

+ 3 - 4
src/main/frontend/commands.cljs

@@ -16,7 +16,7 @@
             [frontend.util.cursor :as cursor]
             [frontend.util.marker :as marker]
             [frontend.util.priority :as priority]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [logseq.graph-parser.config :as gp-config]
@@ -612,15 +612,14 @@
     (when-let [current-input (gdom/getElement input-id)]
       (let [format (or (db/get-page-format (state/get-current-page)) (state/get-preferred-format))
             edit-content (gobj/get current-input "value")
-            repo (state/get-current-repo)
-            new-value (property-edit/insert-property-when-file-based repo format edit-content "" "")]
+            new-value (file-property/insert-property format edit-content "" "")]
         (state/set-edit-content! input-id new-value)))))
 
 (defmethod handle-step :editor/move-cursor-to-properties [[_]]
   (when-let [input-id (state/get-edit-input-id)]
     (when-let [current-input (gdom/getElement input-id)]
       (let [format (or (db/get-page-format (state/get-current-page)) (state/get-preferred-format))]
-        (property-edit/goto-properties-end-when-file-based format current-input)
+        (file-property/goto-properties-end-when-file-based format current-input)
         (cursor/move-cursor-backward current-input 3)))))
 
 (defonce markdown-heading-pattern #"^#+\s+")

+ 5 - 5
src/main/frontend/components/block.cljs

@@ -59,8 +59,8 @@
             [frontend.extensions.pdf.utils :as pdf-utils]
             [frontend.util.clock :as clock]
             [frontend.util.drawer :as drawer]
-            [frontend.util.property-edit :as property-edit]
-            [frontend.util.property :as property]
+            [frontend.handler.file-based.property :as file-property]
+            [frontend.handler.file-based.property.util :as property]
             [frontend.util.text :as text-util]
             [goog.dom :as gdom]
             [goog.object :as gobj]
@@ -1725,7 +1725,7 @@
   (and
    (or
     (empty? properties)
-    (property-edit/properties-hidden? properties))
+    (file-property/properties-hidden? properties))
 
    (empty? title)
 
@@ -2178,7 +2178,7 @@
                                 content (if (config/db-based-graph? repo)
                                           content
                                           (->> content
-                                               (property-edit/remove-built-in-properties-when-file-based
+                                               (file-property/remove-built-in-properties-when-file-based
                                                 (state/get-current-repo) format)
                                                (drawer/remove-logbook)))]
                             ;; save current editing block
@@ -2340,7 +2340,7 @@
          (invalid-properties-cp invalid-properties)))
 
       (when (and (seq properties)
-                 (let [hidden? (property-edit/properties-hidden? properties)]
+                 (let [hidden? (file-property/properties-hidden? properties)]
                    (not hidden?))
                  (not (and block-ref? (or (seq title) (seq body))))
                  (not (:slide? config))

+ 2 - 2
src/main/frontend/components/query_table.cljs

@@ -10,7 +10,7 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.clock :as clock]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [logseq.shui.core :as shui]
             [medley.core :as medley]
             [rum.core :as rum]
@@ -94,7 +94,7 @@
   "Get keys for a query table result, which are the columns in a table"
   [result page?]
   (let [keys (->> (distinct (mapcat keys (map :block/properties result)))
-                  (remove (property-edit/built-in-properties))
+                  (remove (file-property/built-in-properties))
                   (remove #{:template}))
         keys (if page? (cons :page keys) (concat '(:block :page) keys))
         keys (if page? (distinct (concat keys [:created-at :updated-at])) keys)]

+ 4 - 4
src/main/frontend/extensions/srs.cljs

@@ -5,7 +5,7 @@
             [frontend.util :as util]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.util.page-ref :as page-ref]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [frontend.util.drawer :as drawer]
             [frontend.util.persist-var :as persist-var]
             [frontend.db :as db]
@@ -109,7 +109,7 @@
   [block props]
   (editor-handler/save-block-if-changed!
    block
-   (property-edit/insert-properties-when-file-based
+   (file-property/insert-properties-when-file-based
     (state/get-current-repo) (:block/format block) (:block/content block) props)
    {:force? true}))
 
@@ -774,7 +774,7 @@
   [block-id]
   (when-let [block (db/entity [:block/uuid block-id])]
     (when-let [content (:block/content block)]
-      (let [content (-> (property-edit/remove-built-in-properties-when-file-based
+      (let [content (-> (file-property/remove-built-in-properties-when-file-based
                          (state/get-current-repo) (:block/format block) content)
                         (drawer/remove-logbook))]
         (editor-handler/save-block!
@@ -786,7 +786,7 @@
   ([] (batch-make-cards! (state/get-selection-block-ids)))
   ([block-ids]
    (let [block-content-fn (fn [block]
-                            [block (-> (property-edit/remove-built-in-properties-when-file-based
+                            [block (-> (file-property/remove-built-in-properties-when-file-based
                                         (state/get-current-repo) (:block/format block) (:block/content block))
                                        (drawer/remove-logbook)
                                        string/trim

+ 2 - 2
src/main/frontend/handler/common.cljs

@@ -5,13 +5,13 @@
             [frontend.date :as date]
             [frontend.state :as state]
             [frontend.util :as util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.property :as property-handler]
             [goog.object :as gobj]
             ["ignore" :as Ignore]))
 
 (defn copy-to-clipboard-without-id-property!
   [repo format raw-text html blocks]
-  (util/copy-to-clipboard! (property-edit/remove-id-property-when-file-based repo format raw-text)
+  (util/copy-to-clipboard! (property-handler/remove-id-property repo format raw-text)
                            :html html
                            :blocks blocks))
 

+ 284 - 0
src/main/frontend/handler/db_based/property.cljs

@@ -0,0 +1,284 @@
+(ns frontend.handler.db-based.property
+  "Block properties handler."
+  (:require [clojure.edn :as edn]
+            [clojure.string :as string]
+            [frontend.db :as db]
+            [frontend.db.model :as model]
+            [frontend.handler.notification :as notification]
+            [frontend.modules.outliner.core :as outliner-core]
+            [frontend.state :as state]
+            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
+            [malli.util :as mu]
+            [malli.error :as me]))
+
+;; TODO:
+;; Validate && list fixes for non-validated values when updating property schema
+
+(defn- date-str?
+  [value]
+  (when-let [d (js/Date. value)]
+    (not= (str d) "Invalid Date")))
+
+(defn- logseq-page?
+  [id]
+  (and (uuid? id)
+       (when-let [e (db/entity [:block/uuid id])]
+         (nil? (:block/page e)))))
+
+(defn- logseq-block?
+  [id]
+  (and (uuid? id)
+       (when-let [e (db/entity [:block/uuid id])]
+         (some? (:block/page e)))))
+
+(defn- logseq-object?
+  [id]
+  (and (uuid? id)
+       (when-let [e (db/entity [:block/uuid id])]
+         (seq (:block/class e)))))
+
+(def builtin-schema-types
+  {:default  string?                     ; refs/tags will not be extracted
+   :number   number?
+   :date     [:fn
+              {:error/message "should be a date"}
+              date-str?]
+   :checkbox boolean?
+   :url      [:fn
+              {:error/message "should be a URL"}
+              gp-util/url?]
+   :page     [:fn
+              {:error/message "should be a page"}
+              logseq-page?]
+   :block    [:fn
+              {:error/message "should be a block"}
+              logseq-block?]
+   :object    [:fn
+               {:error/message "should be an object"}
+               logseq-object?]})
+
+;; schema -> type, cardinality, object's class
+;;           min, max -> string length, number range, cardinality size limit
+
+;; TODO: Enable or delete if unused
+#_(def builtin-schema->type
+    (set/map-invert builtin-schema-types))
+
+(defn- infer-schema-from-input-string
+  [v-str]
+  (try
+    (cond
+      (parse-long v-str) :number
+      (parse-double v-str) :number
+      (util/uuid-string? v-str) :object
+      (gp-util/url? v-str) :url
+      (date-str? v-str) :date
+      (contains? #{"true" "false"} (string/lower-case v-str)) :boolean
+      :else :default)
+    (catch :default _e
+      :default)))
+
+(defn convert-property-input-string
+  [schema-type v-str]
+  (if (and (not (string? v-str)) (not (object? v-str)))
+    v-str
+    (case schema-type
+      :default
+      v-str
+
+      :number
+      (edn/read-string v-str)
+
+      :boolean
+      (edn/read-string (string/lower-case v-str))
+
+      :page
+      (uuid v-str)
+
+      :block
+      (uuid v-str)
+
+      :object
+      (uuid v-str)
+
+      :date
+      (js/Date. v-str)                  ; inst
+
+      :url
+      v-str)))
+
+(defn- upsert-property!
+  [repo property k-name property-uuid property-type]
+  (let [k-name (name k-name)]
+    (when (and property (nil? (:block/type property)))
+     (db/transact! repo [(outliner-core/block-with-updated-at
+                          {:block/schema {:type property-type}
+                           :block/uuid property-uuid
+                           :block/type "property"})]
+       {:outliner-op :update-property}))
+    (when (nil? property) ;if property not exists yet
+      (db/transact! repo [(outliner-core/block-with-timestamps
+                           {:block/schema {:type property-type}
+                            :block/original-name k-name
+                            :block/name (util/page-name-sanity-lc k-name)
+                            :block/uuid property-uuid
+                            :block/type "property"})]
+        {:outliner-op :create-new-property}))))
+
+(defn add-property!
+  [repo block k-name v {:keys [old-value]}]
+  (let [k-name (name k-name)
+        property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)])
+        v (if property v (or v ""))]
+    (when (some? v)
+      (let [property-uuid (or (:block/uuid property) (random-uuid))
+            {:keys [type cardinality]} (:block/schema property)
+            multiple-values? (= cardinality :many)
+            infer-schema (when-not type (infer-schema-from-input-string v))
+            property-type (or type infer-schema :default)
+            schema (get builtin-schema-types property-type)
+            properties (:block/properties block)
+            value (get properties property-uuid)
+            v* (try
+                 (convert-property-input-string property-type v)
+                 (catch :default e
+                   (notification/show! (str e) :error false)
+                   nil))]
+        (when-not (contains? (if (set? value) value #{value}) v*)
+          (if-let [msg (me/humanize (mu/explain-data schema v*))]
+            (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))]
+              (notification/show! msg' :warning))
+            (do
+              (upsert-property! repo property k-name property-uuid property-type)
+              (let [new-value (cond
+                                (and multiple-values? old-value
+                                     (not= old-value :frontend.components.property/new-value-placeholder))
+                                (if (coll? v*)
+                                  (vec (distinct (concat value v*)))
+                                  (let [v (mapv (fn [x] (if (= x old-value) v* x)) value)]
+                                   (if (contains? (set v) v*)
+                                     v
+                                     (conj v v*))))
+
+                                multiple-values?
+                                (let [f (if (coll? v*) concat conj)]
+                                  (f value v*))
+
+                                :else
+                                v*)
+                    new-value (if (coll? new-value)
+                                (set (remove string/blank? new-value))
+                                new-value)
+                    block-properties (assoc properties property-uuid new-value)
+                    refs (outliner-core/rebuild-block-refs block block-properties)]
+                ;; TODO: fix block/properties-order
+                (db/transact! repo
+                  [[:db/retract (:db/id block) :block/refs]
+                   {:block/uuid (:block/uuid block)
+                    :block/properties block-properties
+                    :block/refs refs}]
+                  {:outliner-op :add-property})))))))))
+
+(defn remove-property!
+  [repo block property-uuid]
+  {:pre (string? property-uuid)}
+  (let [origin-properties (:block/properties block)]
+    (when (contains? (set (keys origin-properties)) property-uuid)
+      (let [properties' (dissoc origin-properties property-uuid)
+            refs (outliner-core/rebuild-block-refs block properties')]
+        (db/transact!
+         repo
+          [[:db/retract (:db/id block) :block/refs]
+           {:block/uuid (:block/uuid block)
+            :block/properties properties'
+            :block/refs refs}]
+          {:outliner-op :remove-property})))))
+
+(defn- fix-cardinality-many-values!
+  [repo property-uuid]
+  (let [ev (->> (model/get-block-property-values property-uuid)
+                (remove (fn [[_ v]] (coll? v))))
+        tx-data (map (fn [[e v]]
+                       (let [entity (db/entity e)
+                             properties (:block/properties entity)]
+                         {:db/id e
+                          :block/properties (assoc properties property-uuid #{v})})) ev)]
+    (when (seq tx-data)
+      (db/transact! repo tx-data
+        {:outliner-op :property-fix-cardinality}))))
+
+(defn update-property!
+  [repo property-uuid {:keys [property-name property-schema]}]
+  {:pre [(uuid? property-uuid)]}
+  (when-let [property (db/entity [:block/uuid property-uuid])]
+    (when (and (= :many (:cardinality property-schema))
+               (not= :many (:cardinality (:block/schema property))))
+      ;; cardinality changed from :one to :many
+      (fix-cardinality-many-values! repo property-uuid))
+    (let [tx-data (cond-> {:block/uuid property-uuid}
+                    property-name (merge
+                                   {:block/original-name property-name
+                                    :block/name (gp-util/page-name-sanity-lc property-name)})
+                    property-schema (assoc :block/schema property-schema)
+                    true outliner-core/block-with-updated-at)]
+      (db/transact! repo [tx-data]
+        {:outliner-op :update-property}))))
+
+(defn delete-property-value!
+  "Delete value if a property has multiple values"
+  [repo block property-id property-value]
+  (when (and block (uuid? property-id))
+    (when (not= property-id (:block/uuid block))
+      (when-let [property (db/pull [:block/uuid property-id])]
+        (let [schema (:block/schema property)]
+          (when (= :many (:cardinality schema))
+            (let [properties (:block/properties block)
+                  properties' (update properties property-id
+                                      (fn [col]
+                                        (set (remove #{property-value} col))))
+                  refs (outliner-core/rebuild-block-refs block properties')]
+              (db/transact! repo
+                [[:db/retract (:db/id block) :block/refs]
+                 {:block/uuid (:block/uuid block)
+                  :block/properties properties'
+                  :block/refs refs}]
+                {:outliner-op :delete-property-value})))
+          (state/clear-edit!))))))
+
+(defn set-editing-new-property!
+  [value]
+  (state/set-state! :ui/new-property-input-id value))
+
+(defn editing-new-property!
+  []
+  (set-editing-new-property! (state/get-edit-input-id))
+  (state/clear-edit!))
+
+(defn class-add-property!
+  [repo class k-name]
+  (when (= "class" (:block/type class))
+    (let [k-name (name k-name)
+          property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)])
+          property-uuid (or (:block/uuid property) (random-uuid))
+          property-type (get-in property [:block/schema :type] :default)
+          {:keys [properties] :as class-schema} (:block/schema class)
+          _ (upsert-property! repo property k-name property-uuid property-type)
+          new-properties (vec (distinct (conj properties property-uuid)))
+          class-new-schema (assoc class-schema :properties new-properties)]
+      (db/transact! repo
+        [{:db/id (:db/id class)
+          :block/schema class-new-schema}]
+        {:outliner-op :class-add-property}))))
+
+(defn class-remove-property!
+  [repo class k-uuid]
+  (when (= "class" (:block/type class))
+    (when-let [property (db/pull repo '[*] [:block/uuid k-uuid])]
+      (let [property-uuid (:block/uuid property)
+            {:keys [properties] :as class-schema} (:block/schema class)
+            new-properties (vec (distinct (remove #{property-uuid} properties)))
+            class-new-schema (assoc class-schema :properties new-properties)]
+        (db/transact! repo [{:db/id (:db/id class)
+                             :block/schema class-new-schema}]
+          {:outliner-op :class-remove-property})))))

+ 22 - 30
src/main/frontend/handler/editor.cljs

@@ -41,8 +41,7 @@
             [frontend.util.keycode :as keycode]
             [frontend.util.list :as list]
             [frontend.util.marker :as marker]
-            [frontend.util.property-edit :as property-edit]
-            [frontend.util.property :as property]
+            [frontend.handler.file-based.property :as file-property]
             [frontend.util.text :as text-util]
             [frontend.util.thingatpt :as thingatpt]
             [goog.dom :as gdom]
@@ -291,7 +290,7 @@
          format (or format (state/get-preferred-format))
          page (db/entity repo (:db/id page))
          block-id (when (map? properties) (get properties :id))
-         content (-> (property-edit/remove-built-in-properties-when-file-based repo format content)
+         content (-> (file-property/remove-built-in-properties-when-file-based repo format content)
                      (drawer/remove-logbook))]
      (cond
        (and (another-block-with-same-id-exists? uuid block-id)
@@ -483,7 +482,7 @@
                       (db/get-page-format (:db/id block))
                       (state/get-preferred-format))
               content (if (seq properties)
-                        (property-edit/insert-properties-when-file-based repo format content properties)
+                        (file-property/insert-properties-when-file-based repo format content properties)
                         content)
               new-block (-> (select-keys block [:block/page :block/journal?
                                                 :block/journal-day])
@@ -545,7 +544,7 @@
 
 (defn properties-block
   [repo properties format page]
-  (let [content (property-edit/insert-properties-when-file-based repo format "" properties)
+  (let [content (file-property/insert-properties-when-file-based repo format "" properties)
         refs (gp-block/get-page-refs-from-properties properties
                                                      (db/get-db (state/get-current-repo))
                                                      (state/get-date-formatter)
@@ -681,10 +680,10 @@
     (when-let [sibling-block-id (dom/attr sibling-block "blockid")]
       (when-let [block (db/pull repo '[*] [:block/uuid (uuid sibling-block-id)])]
         (let [original-content (util/trim-safe (:block/content block))
-              value' (-> (property-edit/remove-built-in-properties-when-file-based repo format original-content)
+              value' (-> (file-property/remove-built-in-properties-when-file-based repo format original-content)
                          (drawer/remove-logbook))
               value (->> value
-                         (property-edit/remove-properties-when-file-based repo format)
+                         (file-property/remove-properties-when-file-based repo format)
                          (drawer/remove-logbook))
               new-value (str value' value)
               tail-len (count value)
@@ -786,9 +785,9 @@
                        (dissoc properties key)
                        (assoc properties key value))
           content (if (nil? value)
-                    (property-edit/remove-property-when-file-based repo format key content)
-                    (property-edit/insert-property-when-file-based repo format content key value))
-          content (property-edit/remove-empty-properties-when-file-based repo content)]
+                    (file-property/remove-property-when-file-based repo format key content)
+                    (file-property/insert-property format content key value))
+          content (file-property/remove-empty-properties-when-file-based repo content)]
       {:block/uuid (:block/uuid block)
        :block/properties properties
        :block/properties-order (or (keys properties) [])
@@ -1189,7 +1188,7 @@
      (save-block!
       {:block block :repo repo :opts (dissoc opts :properties)}
       (if (seq properties)
-        (property-edit/insert-properties-when-file-based repo (:block/format block) content properties)
+        (file-property/insert-properties-when-file-based repo (:block/format block) content properties)
         content))))
   ([{:keys [block repo opts] :as _state} value]
    (let [repo (or repo (state/get-current-repo))]
@@ -1247,7 +1246,7 @@
   [repo format content]
   (->> (text/remove-level-spaces content format (config/get-block-pattern format))
        (drawer/remove-logbook)
-       (property-edit/remove-properties-when-file-based repo format)
+       (file-property/remove-properties-when-file-based repo format)
        string/trim))
 
 (defn insert-command!
@@ -1880,8 +1879,8 @@
           (:block/content block))
         new-content
         (cond->> new-content
-             (not keep-uuid?) (property-edit/remove-property-when-file-based repo format "id")
-             true             (property-edit/remove-property-when-file-based repo format "custom_id"))]
+             (not keep-uuid?) (file-property/remove-property-when-file-based repo format "id")
+             true             (file-property/remove-property-when-file-based repo format "custom_id"))]
     (merge (apply dissoc block (conj (when-not keep-uuid? [:block/_refs]) :block/pre-block? :block/meta))
            {:block/page {:db/id (:db/id page)}
             :block/format format
@@ -1984,7 +1983,7 @@
               (let [content (:content block)
                     props (into [] (:properties block))
                     content* (str (if (= :markdown format) "- " "* ")
-                                  (property-edit/insert-properties-when-file-based repo format content props))
+                                  (file-property/insert-properties-when-file-based repo format content props))
                     ast (mldoc/->edn content* (gp-mldoc/default-config format))
                     blocks (->> (block/extract-blocks ast content* format {:page-name page-name})
                                 (map wrap-parse-block))
@@ -2003,13 +2002,6 @@
         page-id (:db/id (:block/page target-block))
         page-name (some-> page-id (db/entity) :block/name)
         blocks (block-tree->blocks repo tree-vec format keep-uuid? page-name)
-        blocks (if (config/db-based-graph? (state/get-current-repo))
-                 ;; Remove properties from content
-                 (map (fn [b]
-                        (cond-> b
-                          (:content b)
-                          (update :content #(property/remove-properties format %)))) blocks)
-                 blocks)
         blocks (gp-block/with-parent-and-left page-id blocks)
         block-refs (->> (mapcat :block/refs blocks)
                         (set)
@@ -2059,8 +2051,8 @@
        (let [exclude-properties [:id :template :template-including-parent]
              content-update-fn (fn [content]
                                  (->> content
-                                      (property-edit/remove-property-when-file-based repo format "template")
-                                      (property-edit/remove-property-when-file-based repo format "template-including-parent")
+                                      (file-property/remove-property-when-file-based repo format "template")
+                                      (file-property/remove-property-when-file-based repo format "template-including-parent")
                                       template/resolve-dynamic-template!))
              page (if (:block/name block) block
                       (when target (:block/page (db/entity (:db/id target)))))
@@ -2211,7 +2203,7 @@
                   (save-current-block!)
                   (insert-new-block! state))
                 ;; cursor in other positions of :ke|y: or ke|y::, move to line end for inserting value.
-                (if (property-edit/property-key-exist?-when-file-based format content property-key)
+                (if (file-property/property-key-exist?-when-file-based format content property-key)
                   (notification/show!
                    [:p.content
                     (util/format "Property key \"%s\" already exists!" property-key)]
@@ -2224,7 +2216,7 @@
                    input
                    (cursor/line-beginning-pos input)
                    (inc (cursor/line-end-pos input)))
-                  (property-edit/goto-properties-end-when-file-based format input)
+                  (file-property/goto-properties-end-when-file-based format input)
                   (cursor/move-cursor-to-line-end input))
               :else
               ;;When cursor in other place of PROPERTIES drawer, add :|: in a new line and move cursor to |
@@ -2577,7 +2569,7 @@
             new-content (if next-block-has-refs?
                           (str value ""
                                (->> (:block/content next-block)
-                                    (property-edit/remove-properties-when-file-based repo (:block/format next-block))
+                                    (file-property/remove-properties-when-file-based repo (:block/format next-block))
                                     (drawer/remove-logbook)))
                           (str value "" (:block/content next-block)))
             repo (state/get-current-repo)
@@ -2613,7 +2605,7 @@
             (delete-concat current-block)))
 
         :else
-        (delete-and-update 
+        (delete-and-update
           input current-pos (util/safe-inc-current-pos-from-start (.-value input) current-pos))))))
 
 (defn keydown-backspace-handler
@@ -3603,7 +3595,7 @@
       (when-let [block (db/pull [:block/uuid link])]
         (let [block-content (:block/content block)
               format (or (:block/format block) :markdown)
-              block-content-without-prop (-> (property-edit/remove-properties-when-file-based repo format block-content)
+              block-content-without-prop (-> (file-property/remove-properties-when-file-based repo format block-content)
                                              (drawer/remove-logbook))]
           (when-let [input (state/get-input)]
             (when-let [current-block-content (gobj/get input "value")]
@@ -3634,7 +3626,7 @@
           match (block-ref/->block-ref ref-id)
           ref-block (db/entity [:block/uuid ref-id])
           block-ref-content (->> (or (:block/content ref-block) "")
-                                 (property-edit/remove-built-in-properties-when-file-based repo (:block/format ref-block))
+                                 (file-property/remove-built-in-properties-when-file-based repo (:block/format ref-block))
                                  (drawer/remove-logbook))
           content (string/replace-first (:block/content block) match
                                         block-ref-content)]

+ 5 - 5
src/main/frontend/handler/editor/property.cljs

@@ -8,7 +8,7 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.drawer :as drawer]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [goog.object :as gobj]
             [logseq.graph-parser.util :as gp-util]
             [frontend.db.listener :as db-listener]))
@@ -74,7 +74,7 @@
 
                             :else
                             (subs content 0 pos))
-               content (-> (property-edit/remove-built-in-properties-when-file-based
+               content (-> (file-property/remove-built-in-properties-when-file-based
                             repo (:block/format block) content)
                            (drawer/remove-logbook))]
            (clear-selection!)
@@ -111,11 +111,11 @@
                                     (filter (set (keys properties)))
                                     distinct
                                     vec)
-                   content (property-edit/remove-properties-when-file-based repo format content)
+                   content (file-property/remove-properties-when-file-based repo format content)
                    kvs (for [key property-ks] [key (or (get properties-text-values key)
                                                        (get properties key))])
-                   content (property-edit/insert-properties-when-file-based repo format content kvs)
-                   content (property-edit/remove-empty-properties-when-file-based repo content)
+                   content (file-property/insert-properties-when-file-based repo format content kvs)
+                   content (file-property/remove-empty-properties-when-file-based repo content)
                    block {:block/uuid block-id
                           :block/properties properties
                           :block/properties-order property-ks

+ 2 - 2
src/main/frontend/handler/export.cljs

@@ -18,7 +18,7 @@
    [logseq.publishing.html :as publish-html]
    [frontend.state :as state]
    [frontend.util :as util]
-   [frontend.util.property-edit :as property-edit]
+   [frontend.handler.file-based.property :as file-property]
    [goog.dom :as gdom]
    [lambdaisland.glogi :as log]
    [logseq.graph-parser.mldoc :as gp-mldoc]
@@ -323,7 +323,7 @@
                                      (let [b' (if (seq (:block/properties b))
                                                 (update b :block/content
                                                         (fn [content]
-                                                          (property-edit/remove-properties-when-file-based
+                                                          (file-property/remove-properties-when-file-based
                                                            repo (:block/format b) content)))
                                                 b)]
                                        (safe-keywordize b'))) blocks))

+ 3 - 3
src/main/frontend/handler/file_based/editor.cljs

@@ -10,7 +10,7 @@
             [frontend.util.clock :as clock]
             [frontend.util.drawer :as drawer]
             [frontend.util.marker :as marker]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [logseq.db.schema :as db-schema]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.util.block-ref :as block-ref]))
@@ -79,7 +79,7 @@
                      properties)
         real-content (:block/content block)
         content (if (and (seq properties) real-content (not= real-content content))
-                  (property-edit/with-built-in-properties-when-file-based repo properties content format)
+                  (file-property/with-built-in-properties-when-file-based repo properties content format)
                   content)
         content (drawer/with-logbook block content)
         content (with-timetracking block content)
@@ -115,7 +115,7 @@
         block (update block :block/refs remove-non-existed-refs!)
         block (if (and left (not= (:block/left block) left)) (assoc block :block/left left) block)
         new-properties (merge
-                        (select-keys properties (property-edit/hidden-properties))
+                        (select-keys properties (file-property/hidden-properties))
                         (:block/properties block))]
     (-> block
         (dissoc :block.temp/top?

+ 9 - 15
src/main/frontend/util/property_edit.cljs → src/main/frontend/handler/file_based/property.cljs

@@ -1,20 +1,16 @@
-(ns frontend.util.property-edit
-  "Wrappers for fns in `frontend.util.property`, do nothing when repo is db-based"
-  (:require [frontend.util.property :as property]
+(ns frontend.handler.file-based.property
+  (:require [frontend.handler.file-based.property.util :as property]
             [frontend.config :as config]))
 
-
 ;; Why need these XXX-when-file-based fns?
 ;; there're a lot of usages of property-related fns(e.g. property/insert-property) in the whole codebase.
 ;; I want to minimize extensive modifications as much as possible when we add db-based graph support.
 
-;; (def insert-property-when-file-based
+;; (def insert-property
 ;;   (fn-when-file-based property/insert-property [format content key value & args]))
-(defn insert-property-when-file-based
-  [repo format content key value & args]
-  (if (config/db-based-graph? repo)
-    content
-    (apply property/insert-property format content key value args)))
+(defn insert-property
+  [format content key value & args]
+  (apply property/insert-property format content key value args))
 
 (defn insert-properties-when-file-based
   [repo format content kvs]
@@ -34,11 +30,9 @@
     content
     (property/remove-properties format content)))
 
-(defn remove-id-property-when-file-based
-  [repo format content]
-  (if (config/db-based-graph? repo)
-    content
-    (property/remove-id-property format content)))
+(defn remove-id-property
+  [format content]
+  (property/remove-id-property format content))
 
 (defn remove-built-in-properties-when-file-based
   [repo format content]

+ 1 - 1
src/main/frontend/util/property.cljs → src/main/frontend/handler/file_based/property/util.cljs

@@ -1,4 +1,4 @@
-(ns frontend.util.property
+(ns frontend.handler.file-based.property.util
   "Property fns needed by the rest of the app and not graph-parser"
   (:require [clojure.string :as string]
             [frontend.util :as util]

+ 3 - 4
src/main/frontend/handler/page.cljs

@@ -32,7 +32,7 @@
             [frontend.util.fs :as fs-util]
             [frontend.util.page-property :as page-property]
             [frontend.util.page :as page-util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [frontend.util.url :as url-util]
             [goog.functions :refer [debounce]]
             [goog.object :as gobj]
@@ -578,11 +578,10 @@
                                            properties-content
                                            (string/includes? (util/page-name-sanity-lc properties-content)
                                                              old-page-name))
-                                  (let [front-matter? (and (property-edit/front-matter?-when-file-based properties-content)
+                                  (let [front-matter? (and (file-property/front-matter?-when-file-based properties-content)
                                                            (= :markdown (:block/format properties-block)))]
                                     {:db/id         (:db/id properties-block)
-                                     :block/content (property-edit/insert-property-when-file-based
-                                                     repo
+                                     :block/content (file-property/insert-property
                                                      (:block/format properties-block)
                                                      properties-content
                                                      :title

+ 26 - 254
src/main/frontend/handler/property.cljs

@@ -1,250 +1,34 @@
 (ns frontend.handler.property
   "Block properties handler."
-  (:require [clojure.edn :as edn]
-            [clojure.string :as string]
-            [frontend.db :as db]
-            [frontend.db.model :as model]
-            [frontend.handler.notification :as notification]
-            [frontend.modules.outliner.core :as outliner-core]
-            [frontend.state :as state]
-            [frontend.util :as util]
-            [logseq.graph-parser.util :as gp-util]
-            [malli.util :as mu]
-            [malli.error :as me]))
+  (:require [frontend.handler.db-based.property :as db-property]
+            [frontend.handler.file-based.property :as file-property]
+            [frontend.config :as config]
+            [frontend.state :as state]))
 
-;; TODO:
-;; Validate && list fixes for non-validated values when updating property schema
-
-(defn- date-str?
-  [value]
-  (when-let [d (js/Date. value)]
-    (not= (str d) "Invalid Date")))
-
-(defn- logseq-page?
-  [id]
-  (and (uuid? id)
-       (when-let [e (db/entity [:block/uuid id])]
-         (nil? (:block/page e)))))
-
-(defn- logseq-block?
-  [id]
-  (and (uuid? id)
-       (when-let [e (db/entity [:block/uuid id])]
-         (some? (:block/page e)))))
-
-(defn- logseq-object?
-  [id]
-  (and (uuid? id)
-       (when-let [e (db/entity [:block/uuid id])]
-         (seq (:block/class e)))))
-
-(def builtin-schema-types
-  {:default  string?                     ; refs/tags will not be extracted
-   :number   number?
-   :date     [:fn
-              {:error/message "should be a date"}
-              date-str?]
-   :checkbox boolean?
-   :url      [:fn
-              {:error/message "should be a URL"}
-              gp-util/url?]
-   :page     [:fn
-              {:error/message "should be a page"}
-              logseq-page?]
-   :block    [:fn
-              {:error/message "should be a block"}
-              logseq-block?]
-   :object    [:fn
-               {:error/message "should be an object"}
-               logseq-object?]})
-
-;; schema -> type, cardinality, object's class
-;;           min, max -> string length, number range, cardinality size limit
-
-;; TODO: Enable or delete if unused
-#_(def builtin-schema->type
-    (set/map-invert builtin-schema-types))
-
-(defn- infer-schema-from-input-string
-  [v-str]
-  (try
-    (cond
-      (parse-long v-str) :number
-      (parse-double v-str) :number
-      (util/uuid-string? v-str) :object
-      (gp-util/url? v-str) :url
-      (date-str? v-str) :date
-      (contains? #{"true" "false"} (string/lower-case v-str)) :boolean
-      :else :default)
-    (catch :default _e
-      :default)))
-
-(defn convert-property-input-string
-  [schema-type v-str]
-  (if (and (not (string? v-str)) (not (object? v-str)))
-    v-str
-    (case schema-type
-      :default
-      v-str
-
-      :number
-      (edn/read-string v-str)
-
-      :boolean
-      (edn/read-string (string/lower-case v-str))
-
-      :page
-      (uuid v-str)
-
-      :block
-      (uuid v-str)
-
-      :object
-      (uuid v-str)
-
-      :date
-      (js/Date. v-str)                  ; inst
-
-      :url
-      v-str)))
-
-(defn- upsert-property!
-  [repo property k-name property-uuid property-type]
-  (let [k-name (name k-name)]
-    (when (and property (nil? (:block/type property)))
-     (db/transact! repo [(outliner-core/block-with-updated-at
-                          {:block/schema {:type property-type}
-                           :block/uuid property-uuid
-                           :block/type "property"})]
-       {:outliner-op :update-property}))
-    (when (nil? property) ;if property not exists yet
-      (db/transact! repo [(outliner-core/block-with-timestamps
-                           {:block/schema {:type property-type}
-                            :block/original-name k-name
-                            :block/name (util/page-name-sanity-lc k-name)
-                            :block/uuid property-uuid
-                            :block/type "property"})]
-        {:outliner-op :create-new-property}))))
+(def builtin-schema-types db-property/builtin-schema-types)
 
 (defn add-property!
-  [repo block k-name v & {:keys [old-value]}]
-  (let [k-name (name k-name)
-        property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)])
-        v (if property v (or v ""))]
-    (when (some? v)
-      (let [property-uuid (or (:block/uuid property) (random-uuid))
-            {:keys [type cardinality]} (:block/schema property)
-            multiple-values? (= cardinality :many)
-            infer-schema (when-not type (infer-schema-from-input-string v))
-            property-type (or type infer-schema :default)
-            schema (get builtin-schema-types property-type)
-            properties (:block/properties block)
-            value (get properties property-uuid)
-            v* (try
-                 (convert-property-input-string property-type v)
-                 (catch :default e
-                   (notification/show! (str e) :error false)
-                   nil))]
-        (when-not (contains? (if (set? value) value #{value}) v*)
-          (if-let [msg (me/humanize (mu/explain-data schema v*))]
-            (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))]
-              (notification/show! msg' :warning))
-            (do
-              (upsert-property! repo property k-name property-uuid property-type)
-              (let [new-value (cond
-                                (and multiple-values? old-value
-                                     (not= old-value :frontend.components.property/new-value-placeholder))
-                                (if (coll? v*)
-                                  (vec (distinct (concat value v*)))
-                                  (let [v (mapv (fn [x] (if (= x old-value) v* x)) value)]
-                                   (if (contains? (set v) v*)
-                                     v
-                                     (conj v v*))))
-
-                                multiple-values?
-                                (let [f (if (coll? v*) concat conj)]
-                                  (f value v*))
-
-                                :else
-                                v*)
-                    new-value (if (coll? new-value)
-                                (set (remove string/blank? new-value))
-                                new-value)
-                    block-properties (assoc properties property-uuid new-value)
-                    refs (outliner-core/rebuild-block-refs block block-properties)]
-                ;; TODO: fix block/properties-order
-                (db/transact! repo
-                  [[:db/retract (:db/id block) :block/refs]
-                   {:block/uuid (:block/uuid block)
-                    :block/properties block-properties
-                    :block/refs refs}]
-                  {:outliner-op :add-property})))))))))
+  [repo block k-name v & opts]
+  (if (config/db-based-graph? repo)
+    (db-property/add-property! repo block k-name v opts)))
 
 (defn remove-property!
   [repo block property-uuid]
-  {:pre (string? property-uuid)}
-  (let [origin-properties (:block/properties block)]
-    (when (contains? (set (keys origin-properties)) property-uuid)
-      (let [properties' (dissoc origin-properties property-uuid)
-            refs (outliner-core/rebuild-block-refs block properties')]
-        (db/transact!
-         repo
-          [[:db/retract (:db/id block) :block/refs]
-           {:block/uuid (:block/uuid block)
-            :block/properties properties'
-            :block/refs refs}]
-          {:outliner-op :remove-property})))))
-
-(defn- fix-cardinality-many-values!
-  [repo property-uuid]
-  (let [ev (->> (model/get-block-property-values property-uuid)
-                (remove (fn [[_ v]] (coll? v))))
-        tx-data (map (fn [[e v]]
-                       (let [entity (db/entity e)
-                             properties (:block/properties entity)]
-                         {:db/id e
-                          :block/properties (assoc properties property-uuid #{v})})) ev)]
-    (when (seq tx-data)
-      (db/transact! repo tx-data
-        {:outliner-op :property-fix-cardinality}))))
+  {:pre (uuid? property-uuid)}
+  (if (config/db-based-graph? repo)
+    (db-property/remove-property! repo block property-uuid)))
 
 (defn update-property!
-  [repo property-uuid {:keys [property-name property-schema]}]
+  [repo property-uuid opts]
   {:pre [(uuid? property-uuid)]}
-  (when-let [property (db/entity [:block/uuid property-uuid])]
-    (when (and (= :many (:cardinality property-schema))
-               (not= :many (:cardinality (:block/schema property))))
-      ;; cardinality changed from :one to :many
-      (fix-cardinality-many-values! repo property-uuid))
-    (let [tx-data (cond-> {:block/uuid property-uuid}
-                    property-name (merge
-                                   {:block/original-name property-name
-                                    :block/name (gp-util/page-name-sanity-lc property-name)})
-                    property-schema (assoc :block/schema property-schema)
-                    true outliner-core/block-with-updated-at)]
-      (db/transact! repo [tx-data]
-        {:outliner-op :update-property}))))
+  (if (config/db-based-graph? repo)
+    (db-property/update-property! repo property-uuid opts)))
 
 (defn delete-property-value!
   "Delete value if a property has multiple values"
   [repo block property-id property-value]
-  (when (and block (uuid? property-id))
-    (when (not= property-id (:block/uuid block))
-      (when-let [property (db/pull [:block/uuid property-id])]
-        (let [schema (:block/schema property)]
-          (when (= :many (:cardinality schema))
-            (let [properties (:block/properties block)
-                  properties' (update properties property-id
-                                      (fn [col]
-                                        (set (remove #{property-value} col))))
-                  refs (outliner-core/rebuild-block-refs block properties')]
-              (db/transact! repo
-                [[:db/retract (:db/id block) :block/refs]
-                 {:block/uuid (:block/uuid block)
-                  :block/properties properties'
-                  :block/refs refs}]
-                {:outliner-op :delete-property-value})))
-          (state/clear-edit!))))))
+  (if (config/db-based-graph? repo)
+    (db-property/delete-property-value! repo block property-id property-value)))
 
 (defn set-editing-new-property!
   [value]
@@ -257,28 +41,16 @@
 
 (defn class-add-property!
   [repo class k-name]
-  (when (= "class" (:block/type class))
-    (let [k-name (name k-name)
-          property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)])
-          property-uuid (or (:block/uuid property) (random-uuid))
-          property-type (get-in property [:block/schema :type] :default)
-          {:keys [properties] :as class-schema} (:block/schema class)
-          _ (upsert-property! repo property k-name property-uuid property-type)
-          new-properties (vec (distinct (conj properties property-uuid)))
-          class-new-schema (assoc class-schema :properties new-properties)]
-      (db/transact! repo
-        [{:db/id (:db/id class)
-          :block/schema class-new-schema}]
-        {:outliner-op :class-add-property}))))
+  (when (config/db-based-graph? repo)
+    (db-property/class-add-property! repo class k-name)))
 
 (defn class-remove-property!
   [repo class k-uuid]
-  (when (= "class" (:block/type class))
-    (when-let [property (db/pull repo '[*] [:block/uuid k-uuid])]
-      (let [property-uuid (:block/uuid property)
-            {:keys [properties] :as class-schema} (:block/schema class)
-            new-properties (vec (distinct (remove #{property-uuid} properties)))
-            class-new-schema (assoc class-schema :properties new-properties)]
-        (db/transact! repo [{:db/id (:db/id class)
-                             :block/schema class-new-schema}]
-          {:outliner-op :class-remove-property})))))
+  (when (config/db-based-graph? repo)
+    (db-property/class-remove-property! repo class k-uuid)))
+
+(defn remove-id-property
+  [repo format content]
+  (if (config/db-based-graph? repo)
+    content
+    (file-property/remove-id-property format content)))

+ 2 - 2
src/main/frontend/modules/editor/undo_redo.cljs

@@ -9,7 +9,7 @@
             [clojure.set :as set]
             [medley.core :as medley]
             [frontend.util.drawer :as drawer]
-            [frontend.util.property-edit :as property-edit]))
+            [frontend.handler.file-based.property :as file-property]))
 
 ;;;; APIs
 
@@ -161,7 +161,7 @@
   (when-let [block (state/get-edit-block)]
     (when-let [content (:block/content (db/entity (:db/id block)))]
       (let [repo (state/get-current-repo)
-            content' (-> (property-edit/remove-built-in-properties-when-file-based repo (:block/format block) content)
+            content' (-> (file-property/remove-built-in-properties-when-file-based repo (:block/format block) content)
                          (drawer/remove-logbook))]
         (state/set-edit-content! (state/get-edit-input-id) content')))))
 

+ 4 - 4
src/main/frontend/modules/file/core.cljs

@@ -9,7 +9,7 @@
             [frontend.modules.file.uprint :as up]
             [frontend.state :as state]
             [frontend.util.fs :as fs-util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [logseq.common.path :as path]))
 
 (defn- indented-block-content
@@ -22,11 +22,11 @@
   [repo format content collapsed?]
   (cond
     collapsed?
-    (property-edit/insert-property-when-file-based repo format content :collapsed true)
+    (file-property/insert-property format content :collapsed true)
 
     ;; Don't check properties. Collapsed is an internal state log as property in file, but not counted into properties
     (false? collapsed?)
-    (property-edit/remove-property-when-file-based repo format :collapsed content)
+    (file-property/remove-property-when-file-based repo format :collapsed content)
 
     :else
     content))
@@ -84,7 +84,7 @@
                               " ")]
                     (str prefix sep new-content)))
         content (if block-ref-not-saved?
-                  (property-edit/insert-property-when-file-based repo format content :id (str (:block/uuid b)))
+                  (file-property/insert-property format content :id (str (:block/uuid b)))
                   content)]
     content))
 

+ 5 - 3
src/main/frontend/modules/outliner/core.cljs

@@ -12,7 +12,7 @@
             [frontend.modules.outliner.utils :as outliner-u]
             [frontend.state :as state]
             [frontend.util :as util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [frontend.config :as config]
             [logseq.graph-parser.util :as gp-util]
             [cljs.spec.alpha :as s]
@@ -518,8 +518,10 @@
             (cond-> block
               (and (some? (:block/uuid block))
                    (nil? (list-type-fn block)))
-              (-> (update :block/properties #(assoc % :logseq.order-list-type list-type))
-                  (assoc :block/content (property-edit/insert-property-when-file-based (state/get-current-repo) format content :logseq.order-list-type list-type)))))
+              (update :block/properties #(assoc % :logseq.order-list-type list-type))
+
+              (not (config/db-based-graph? (state/get-current-repo)))
+              (assoc :block/content (file-property/insert-property format content :logseq.order-list-type list-type))))
           blocks)
         blocks))))
 

+ 2 - 2
src/main/frontend/modules/outliner/datascript.cljs

@@ -11,7 +11,7 @@
             [frontend.search :as search]
             [clojure.string :as string]
             [frontend.util :as util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [logseq.graph-parser.util.block-ref :as block-ref]
             [frontend.db.validate :as db-validate]))
 
@@ -90,7 +90,7 @@
                               (let [refs (:block/_refs block)]
                                 (map (fn [ref]
                                        (let [id (:db/id ref)
-                                             block-content (property-edit/remove-properties-when-file-based
+                                             block-content (file-property/remove-properties-when-file-based
                                                             repo (:block/format block) (:block/content block))
                                              new-content (-> (:block/content ref)
                                                              (string/replace (re-pattern (util/format "(?i){{embed \\(\\(%s\\)\\)\\s?}}" (str (:block/uuid block))))

+ 2 - 2
src/main/frontend/search.cljs

@@ -12,7 +12,7 @@
             [frontend.search.protocol :as protocol]
             [frontend.state :as state]
             [frontend.util :as util]
-            [frontend.util.property-edit :as property-edit]
+            [frontend.handler.file-based.property :as file-property]
             [goog.object :as gobj]
             [promesa.core :as p]
             [clojure.set :as set]
@@ -184,7 +184,7 @@
 (defn get-all-properties
   []
   (->> (db-model/get-all-properties)
-       (remove (property-edit/hidden-properties))
+       (remove (file-property/hidden-properties))
        ;; Complete full keyword except the ':'
        (map (fn [property]
               (if (keyword? property)

+ 2 - 2
src/test/frontend/util/property_test.cljs

@@ -1,6 +1,6 @@
 (ns frontend.util.property-test
   (:require [cljs.test :refer [are deftest testing]]
-            [frontend.util.property :as property]))
+            [frontend.handler.file-based.property.util :as property]))
 
 (deftest remove-id-property
   (testing "org"
@@ -245,4 +245,4 @@ SCHEDULED: <2021-10-25 Mon>\n:PROPERTIES:\n:a: b\n:END:\nworld\n" "c" "d")
       {:logseq.table.version 2} [:logseq.table.version] {:pre-block? false}
       '()
       {:logseq.table.version 2 :foo "bar"} [:logseq.table.version :foo] {:pre-block? false}
-      [[:foo "bar"]])))
+      [[:foo "bar"]])))