Browse Source

fix: disallow built-in page or property names from being edited

fixes LOG-3049
Tienson Qin 1 year ago
parent
commit
4c03e58b6c

+ 12 - 3
deps/db/src/logseq/db.cljs

@@ -65,8 +65,8 @@
        (let [f (or @*transact-fn d/transact!)]
          (f repo-or-conn tx-data tx-meta))))))
 
-(defn build-default-pages-tx
-  []
+(defn build-pages-tx
+  [pages]
   (let [time (common-util/time-ms)]
     (map
      (fn [m]
@@ -74,7 +74,11 @@
            (assoc :block/created-at time)
            (assoc :block/updated-at time)
            (assoc :block/format :markdown)))
-     default-db/built-in-pages)))
+      pages)))
+
+(defn build-default-pages-tx
+  []
+  (build-pages-tx default-db/built-in-pages))
 
 (defn create-default-pages!
   "Creates default pages if one of the default pages does not exist. This
@@ -544,6 +548,11 @@
     (when (seq eids)
       (d/pull-many db '[*] eids))))
 
+(defn built-in?
+  "Built-in page or block"
+  [entity]
+  (:built-in? (:block/metadata entity)))
+
 (comment
   (defn db-based-graph?
     "Whether the current graph is db-only"

+ 3 - 0
deps/db/src/logseq/db/frontend/class.cljs

@@ -5,5 +5,8 @@
 (def ^:large-vars/data-var built-in-classes
   {:task {:original-name "Task"
           :schema {:properties ["status" "priority"]}}
+   :card {:original-name "card"
+          ;; :schema {:property []}
+          }
    ;; TODO: Add more classes such as :book, :paper, :movie, :music, :project
    })

+ 8 - 6
deps/db/src/logseq/db/frontend/default.cljs

@@ -15,10 +15,12 @@
    (set built-in-priorities)
    #{"Favorites" "Contents" "card"}))
 
+(defn page-title->block
+  [title]
+  {:block/name (string/lower-case title)
+   :block/original-name title
+   :block/journal? false
+   :block/uuid (random-uuid)})
+
 (def built-in-pages
-  (mapv (fn [p]
-          {:block/name (string/lower-case p)
-           :block/original-name p
-           :block/journal? false
-           :block/uuid (random-uuid)})
-        built-in-pages-names))
+  (mapv page-title->block built-in-pages-names))

+ 35 - 25
deps/db/src/logseq/db/sqlite/create_graph.cljs

@@ -7,7 +7,12 @@
             [logseq.db.frontend.property.util :as db-property-util]
             [logseq.common.util :as common-util]
             [datascript.core :as d]
-            [logseq.db :as ldb]))
+            [logseq.db :as ldb]
+            [logseq.db.frontend.default :as default-db]))
+
+(defn- mark-block-as-built-in
+  [block]
+  (update block :block/metadata assoc :built-in? true))
 
 (defn- build-initial-properties
   []
@@ -21,18 +26,19 @@
                              (into {}))]
     (mapcat
      (fn [[k-keyword {:keys [schema original-name closed-values db-ident]}]]
-       (let [k-name (name k-keyword)]
-         (if closed-values
-           (db-property-util/build-closed-values
-            (or original-name k-name)
-            {:block/schema schema :block/uuid (d/squuid) :closed-values closed-values}
-            {:icon-id (get default-property-uuids :icon)
-             :db-ident db-ident})
-           [(sqlite-util/build-new-property
-             (or original-name k-name)
-             schema
-             (get default-property-uuids k-keyword (d/squuid))
-             {:db-ident db-ident})])))
+       (let [k-name (name k-keyword)
+             blocks (if closed-values
+                      (db-property-util/build-closed-values
+                       (or original-name k-name)
+                       {:block/schema schema :block/uuid (d/squuid) :closed-values closed-values}
+                       {:icon-id (get default-property-uuids :icon)
+                        :db-ident db-ident})
+                      [(sqlite-util/build-new-property
+                        (or original-name k-name)
+                        schema
+                        (get default-property-uuids k-keyword (d/squuid))
+                        {:db-ident db-ident})])]
+         (update blocks 0 mark-block-as-built-in)))
      built-in-properties)))
 
 (defn build-db-initial-data
@@ -51,7 +57,8 @@
                         :file/path (str "logseq/" "custom.js")
                         :file/content ""
                         :file/last-modified-at (js/Date.)}]
-        default-pages (ldb/build-default-pages-tx)
+        default-pages (->> (ldb/build-pages-tx (map default-db/page-title->block ["Contents"]))
+                           (map mark-block-as-built-in))
         default-properties (build-initial-properties)
         name->properties (zipmap
                           (map :block/name default-properties)
@@ -59,16 +66,19 @@
         default-classes (map
                          (fn [[k-keyword {:keys [schema original-name]}]]
                            (let [k-name (name k-keyword)]
-                             (sqlite-util/build-new-class
-                              {:block/original-name (or original-name k-name)
-                               :block/name (common-util/page-name-sanity-lc k-name)
-                               :block/uuid (d/squuid)
-                               :block/schema {:properties
-                                              (mapv
-                                               (fn [property-name]
-                                                 (let [id (:block/uuid (get name->properties property-name))]
-                                                   (assert id (str "Built-in property " property-name " is not defined yet"))
-                                                   id))
-                                               (:properties schema))}})))
+                             (mark-block-as-built-in
+                              (sqlite-util/build-new-class
+                               (let [properties (mapv
+                                                 (fn [property-name]
+                                                   (let [id (:block/uuid (get name->properties property-name))]
+                                                     (assert id (str "Built-in property " property-name " is not defined yet"))
+                                                     id))
+                                                 (:properties schema))]
+                                 (cond->
+                                  {:block/original-name (or original-name k-name)
+                                   :block/name (common-util/page-name-sanity-lc k-name)
+                                   :block/uuid (d/squuid)}
+                                   (seq properties)
+                                   (assoc-in [:block/schema :properties] properties)))))))
                          db-class/built-in-classes)]
     (vec (concat initial-data initial-files default-pages default-classes default-properties))))

+ 1 - 1
deps/outliner/src/logseq/outliner/core.cljs

@@ -294,7 +294,7 @@
   [conn m]
   (or
    (let [marker (:block/marker m)
-         property (db-property/get-property @conn "status")
+         property (d/entity @conn :task/status)
          matched-status-id (when marker
                              (->> (get-in property [:block/schema :values])
                                  (some (fn [id]

+ 28 - 27
src/main/frontend/components/page.cljs

@@ -50,7 +50,8 @@
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]
             [frontend.extensions.graph.pixi :as pixi]
-            [frontend.db.async :as db-async]))
+            [frontend.db.async :as db-async]
+            [logseq.db :as ldb]))
 
 (defn- get-page-name
   [state]
@@ -302,13 +303,13 @@
                   (when untitled? (reset! *title-value "")))}]))
 
 (rum/defcs ^:large-vars/cleanup-todo page-title < rum/reactive
-                                                  (rum/local false ::edit?)
-                                                  (rum/local "" ::input-value)
-                                                  {:init (fn [state]
-                                                           (let [page-name (first (:rum/args state))
-                                                                 original-name (:block/original-name (db/entity [:block/name (util/page-name-sanity-lc page-name)]))
-                                                                 *title-value (atom original-name)]
-                                                             (assoc state ::title-value *title-value)))}
+  (rum/local false ::edit?)
+  (rum/local "" ::input-value)
+  {:init (fn [state]
+           (let [page-name (first (:rum/args state))
+                 original-name (:block/original-name (db/entity [:block/name (util/page-name-sanity-lc page-name)]))
+                 *title-value (atom original-name)]
+             (assoc state ::title-value *title-value)))}
   [state page-name {:keys [fmt-journal? preview? *hover?]}]
   (when page-name
     (let [page (when page-name (db/entity [:block/name page-name]))
@@ -338,13 +339,13 @@
               {:on-mouse-down util/stop-propagation}
               (if (and (map? icon) db-based?)
                 (icon-component/icon-picker icon
-                  {:on-chosen (fn [_e icon]
-                                (let [icon-property-id (db-pu/get-built-in-property-uuid :icon)]
-                                  (db-property-handler/<update-property!
-                                    repo
-                                    (:block/uuid page)
-                                    {:properties {icon-property-id icon}})))
-                   :icon-props {:size 38}})
+                                            {:on-chosen (fn [_e icon]
+                                                          (let [icon-property-id (db-pu/get-built-in-property-uuid :icon)]
+                                                            (db-property-handler/<update-property!
+                                                             repo
+                                                             (:block/uuid page)
+                                                             {:properties {icon-property-id icon}})))
+                                             :icon-props {:size 38}})
                 icon)])
            [:h1.page-title.flex-1.cursor-pointer.gap-1
             {:class (when-not whiteboard-page? "title")
@@ -357,16 +358,16 @@
                            (if (gobj/get e "shiftKey")
                              (when-let [page (db/pull repo '[*] [:block/name page-name])]
                                (state/sidebar-add-block!
-                                 repo
-                                 (:db/id page)
-                                 :page))
-                             (when (and (not hls-page?)
-                                     (not fmt-journal?)
-                                     (not config/publishing?)
-                                     (not (and (contains? (:block/type page) "property")
-                                            (contains? db-property/built-in-properties-keys-str page-name))))
-                               (reset! *input-value (if untitled? "" old-name))
-                               (reset! *edit? true)))))}
+                                repo
+                                (:db/id page)
+                                :page))
+                             (do
+                               (when (and (not hls-page?)
+                                          (not fmt-journal?)
+                                          (not config/publishing?)
+                                          (not (ldb/built-in? page)))
+                                 (reset! *input-value (if untitled? "" old-name))
+                                 (reset! *edit? true))))))}
 
             (if @*edit?
               (page-title-editor page {:*title-value *title-value
@@ -385,10 +386,10 @@
                 :data-ref   page-name
                 :style      {:opacity (when @*edit? 0)}}
                (let [nested? (and (string/includes? title page-ref/left-brackets)
-                               (string/includes? title page-ref/right-brackets))]
+                                  (string/includes? title page-ref/right-brackets))]
                  (cond untitled? [:span.opacity-50 (t :untitled)]
                        nested? (component-block/map-inline {} (gp-mldoc/inline->edn title (mldoc/get-default-config
-                                                                                            (:block/format page))))
+                                                                                           (:block/format page))))
                        :else title))])]])))))
 
 (defn- page-mouse-over

+ 201 - 197
src/main/frontend/components/property.cljs

@@ -30,7 +30,8 @@
             [frontend.components.dnd :as dnd]
             [frontend.components.property.closed-value :as closed-value]
             [frontend.components.property.util :as components-pu]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [logseq.db :as ldb]))
 
 (defn- <create-class-if-not-exists!
   [value]
@@ -153,215 +154,218 @@
   (let [values (rum/react (::values state))]
     (when-not (= :loading values)
       (let [*property-name (::property-name state)
-           *property-schema (::property-schema state)
-           built-in-property? (contains? db-property/built-in-properties-keys-str (:block/original-name property))
-           property (db/sub-block (:db/id property))
-           disabled? (or built-in-property? config/publishing?)
-           hide-delete? (or (= (:db/id block) (:db/id property)) ; property page
-                            add-new-property?)
-           class? (contains? (:block/type block) "class")
-           property-type (get-in property [:block/schema :type])
-           save-property-fn (fn [] (components-pu/update-property! property @*property-name @*property-schema))
-           enable-closed-values? (contains? db-property-type/closed-value-property-types (or property-type :default))]
-       [:div.property-configure.flex.flex-1.flex-col
-        {:on-mouse-down #(state/set-state! :editor/mouse-down-from-property-configure? true)
-         :on-mouse-up #(state/set-state! :editor/mouse-down-from-property-configure? nil)}
-        [:div.grid.gap-2.p-1
-         [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-          [:label.col-span-1 "Name:"]
-          (shui/input
-           {:class         "col-span-2 !px-2 !py-0 !h-8"
-            :auto-focus    (not add-new-property?)
-            :on-change     #(reset! *property-name (util/evalue %))
-            :on-blur       save-property-fn
-            :on-key-press  (fn [e]
-                             (when (= "Enter" (util/ekey e))
-                               (save-property-fn)))
-            :disabled      disabled?
-            :default-value @*property-name})]
-
-         [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-          [:label.col-span-1 "Icon:"]
-          (let [icon-value (pu/get-block-property-value property :icon)]
-            [:div.col-span-3.flex.flex-row.items-center.gap-2
-             (icon-component/icon-picker icon-value
-                                         {:disabled? disabled?
-                                          :on-chosen (fn [_e icon]
-                                                       (let [icon-property-id (db-pu/get-built-in-property-uuid :icon)]
-                                                         (db-property-handler/<update-property!
-                                                          (state/get-current-repo)
-                                                          (:block/uuid property)
-                                                          {:properties {icon-property-id icon}})))})
-
-             (when (and icon-value (not disabled?))
-               [:a.fade-link.flex {:on-click (fn [_e]
-                                               (db-property-handler/remove-block-property!
-                                                (state/get-current-repo)
-                                                (:block/uuid property)
-                                                (db-pu/get-built-in-property-uuid :icon)))
-                                   :title "Delete this icon"}
-                (ui/icon "X")])])]
-
-         [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-          [:label.col-span-1 "Schema type:"]
-          (let [schema-types (->> (concat db-property-type/user-built-in-property-types
-                                          (when built-in-property?
-                                            db-property-type/internal-built-in-property-types))
-                                  (map (fn [type]
-                                         {:label (property-type-label type)
-                                          :disabled disabled?
-                                          :value type
-                                          :selected (= type (:type @*property-schema))})))]
-            (if (and property-type
-                     (seq values))
-              [:div.flex.items-center.col-span-2
-               (property-type-label property-type)
-               (ui/tippy {:html        "The type of this property is locked once you start using it. This is to make sure all your existing information stays correct if the property type is changed later. To unlock, all uses of a property must be deleted."
-                          :class       "tippy-hover ml-2"
-                          :interactive true
-                          :disabled    false}
-                         (svg/help-circle))]
-              [:div.flex.items-center.col-span-2
-               (shui/select
+            *property-schema (::property-schema state)
+            built-in-property? (contains? db-property/built-in-properties-keys-str (:block/original-name property))
+            property (db/sub-block (:db/id property))
+            built-in? (ldb/built-in? property)
+            disabled? (or built-in-property? config/publishing?)
+            hide-delete? (or (= (:db/id block) (:db/id property)) ; property page
+                             add-new-property?)
+            class? (contains? (:block/type block) "class")
+            property-type (get-in property [:block/schema :type])
+            save-property-fn (fn [] (components-pu/update-property! property @*property-name @*property-schema))
+            enable-closed-values? (contains? db-property-type/closed-value-property-types (or property-type :default))]
+        [:div.property-configure.flex.flex-1.flex-col
+         {:on-mouse-down #(state/set-state! :editor/mouse-down-from-property-configure? true)
+          :on-mouse-up #(state/set-state! :editor/mouse-down-from-property-configure? nil)}
+         [:div.grid.gap-2.p-1
+          [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+           [:label.col-span-1 "Name:"]
+           (if built-in?
+             [:div.col-span-2 (:block/original-name property)]
+             (shui/input
+              {:class         "col-span-2 !px-2 !py-0 !h-8"
+               :auto-focus    (not add-new-property?)
+               :on-change     #(reset! *property-name (util/evalue %))
+               :on-blur       save-property-fn
+               :on-key-press  (fn [e]
+                                (when (= "Enter" (util/ekey e))
+                                  (save-property-fn)))
+               :disabled      disabled?
+               :default-value @*property-name}))]
+
+          [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+           [:label.col-span-1 "Icon:"]
+           (let [icon-value (pu/get-block-property-value property :icon)]
+             [:div.col-span-3.flex.flex-row.items-center.gap-2
+              (icon-component/icon-picker icon-value
+                                          {:disabled? disabled?
+                                           :on-chosen (fn [_e icon]
+                                                        (let [icon-property-id (db-pu/get-built-in-property-uuid :icon)]
+                                                          (db-property-handler/<update-property!
+                                                           (state/get-current-repo)
+                                                           (:block/uuid property)
+                                                           {:properties {icon-property-id icon}})))})
+
+              (when (and icon-value (not disabled?))
+                [:a.fade-link.flex {:on-click (fn [_e]
+                                                (db-property-handler/remove-block-property!
+                                                 (state/get-current-repo)
+                                                 (:block/uuid property)
+                                                 (db-pu/get-built-in-property-uuid :icon)))
+                                    :title "Delete this icon"}
+                 (ui/icon "X")])])]
+
+          [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+           [:label.col-span-1 "Schema type:"]
+           (let [schema-types (->> (concat db-property-type/user-built-in-property-types
+                                           (when built-in-property?
+                                             db-property-type/internal-built-in-property-types))
+                                   (map (fn [type]
+                                          {:label (property-type-label type)
+                                           :disabled disabled?
+                                           :value type
+                                           :selected (= type (:type @*property-schema))})))]
+             (if (or (ldb/built-in? property)
+                     (and property-type (seq values)))
+               [:div.flex.items-center.col-span-2
+                (property-type-label property-type)
+                (ui/tippy {:html        "The type of this property is locked once you start using it. This is to make sure all your existing information stays correct if the property type is changed later. To unlock, all uses of a property must be deleted."
+                           :class       "tippy-hover ml-2"
+                           :interactive true
+                           :disabled    false}
+                          (svg/help-circle))]
+               [:div.flex.items-center.col-span-2
+                (shui/select
                  {:on-value-change
                   (fn [v]
                     (let [type (keyword (string/lower-case v))
                           update-schema-fn (apply comp
-                                             #(assoc % :type type)
+                                                  #(assoc % :type type)
                                              ;; always delete previous closed values as they
                                              ;; are not valid for the new type
-                                             #(dissoc % :values)
-                                             (keep
-                                               (fn [attr]
-                                                 (when-not (db-property-type/property-type-allows-schema-attribute? type attr)
-                                                   #(dissoc % attr)))
-                                               [:cardinality :classes :position]))]
+                                                  #(dissoc % :values)
+                                                  (keep
+                                                   (fn [attr]
+                                                     (when-not (db-property-type/property-type-allows-schema-attribute? type attr)
+                                                       #(dissoc % attr)))
+                                                   [:cardinality :classes :position]))]
                       (swap! *property-schema update-schema-fn)
                       (components-pu/update-property! property @*property-name @*property-schema)))
                   :default-value :default}
                  (shui/select-trigger
-                   {:class "!px-2 !py-0 !h-8"}
-                   (shui/select-value
-                     {:placeholder "Select a schema type"})
-                   (shui/tabler-icon "selector" {:class "opacity-30"}))
+                  {:class "!px-2 !py-0 !h-8"}
+                  (shui/select-value
+                   {:placeholder "Select a schema type"})
+                  (shui/tabler-icon "selector" {:class "opacity-30"}))
                  (shui/select-content
-                   (shui/select-group
-                     (for [{:keys [label value disabled]} schema-types]
-                       (shui/select-item {:value value :disabled disabled} label)))))
-
-               (ui/tippy {:html        "Changing the property type clears some property configurations."
-                          :class       "tippy-hover ml-2"
-                          :interactive true
-                          :disabled    false}
-                         (svg/info))]))]
-
-         (when (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :cardinality)
-           [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-            [:label "Multiple values:"]
-            (let [many? (boolean (= :many (:cardinality @*property-schema)))]
-              (shui/checkbox
-               {:checked           many?
-                :disabled          disabled?
-                :on-checked-change (fn []
-                                     (swap! *property-schema assoc :cardinality (if many? :one :many))
-                                     (save-property-fn))}))])
-
-         (when (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :classes)
-           (case (:type @*property-schema)
-             :page
-             (when (empty? (:values @*property-schema))
-               [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-                [:label "Specify classes:"]
-                (class-select *property-schema
-                              (:classes @*property-schema)
-                              (assoc opts
-                                     :disabled? disabled?
-                                     :save-property-fn save-property-fn))])
-
-             :template
-             [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-              [:label "Specify template:"]
-              (class-select *property-schema (:classes @*property-schema)
-                            (assoc opts
-                                   :multiple-choices? false
-                                   :disabled? disabled?
-                                   :save-property-fn save-property-fn))]
-
-             nil))
-
-         (when (and enable-closed-values? (empty? (:classes @*property-schema)))
-           [:div.grid.grid-cols-4.gap-1.items-start.leading-8
-            [:label.col-span-1 "Available choices:"]
-            [:div.col-span-3
-             (closed-value/choices property *property-name *property-schema opts)]])
-
-         (when (and enable-closed-values?
-                    (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :position)
-                    (seq (:values @*property-schema)))
-           (let [position (:position @*property-schema)
-                 choices (map
-                          (fn [item]
-                            (assoc item :selected
-                                   (or (and position (= (:value item) position))
-                                       (and (nil? position) (= (:value item) "properties")))))
-                          [{:label "Block properties"
-                            :value "properties"}
-                           {:label "Beginning of the block"
-                            :value "block-beginning"}
+                  (shui/select-group
+                   (for [{:keys [label value disabled]} schema-types]
+                     (shui/select-item {:value value :disabled disabled} label)))))
+
+                (ui/tippy {:html        "Changing the property type clears some property configurations."
+                           :class       "tippy-hover ml-2"
+                           :interactive true
+                           :disabled    false}
+                          (svg/info))]))]
+
+          (when (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :cardinality)
+            [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+             [:label "Multiple values:"]
+             (let [many? (boolean (= :many (:cardinality @*property-schema)))]
+               (shui/checkbox
+                {:checked           many?
+                 :disabled          disabled?
+                 :on-checked-change (fn []
+                                      (swap! *property-schema assoc :cardinality (if many? :one :many))
+                                      (save-property-fn))}))])
+
+          (when (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :classes)
+            (case (:type @*property-schema)
+              :page
+              (when (empty? (:values @*property-schema))
+                [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+                 [:label "Specify classes:"]
+                 (class-select *property-schema
+                               (:classes @*property-schema)
+                               (assoc opts
+                                      :disabled? disabled?
+                                      :save-property-fn save-property-fn))])
+
+              :template
+              [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+               [:label "Specify template:"]
+               (class-select *property-schema (:classes @*property-schema)
+                             (assoc opts
+                                    :multiple-choices? false
+                                    :disabled? disabled?
+                                    :save-property-fn save-property-fn))]
+
+              nil))
+
+          (when (and enable-closed-values? (empty? (:classes @*property-schema)))
+            [:div.grid.grid-cols-4.gap-1.items-start.leading-8
+             [:label.col-span-1 "Available choices:"]
+             [:div.col-span-3
+              (closed-value/choices property *property-name *property-schema opts)]])
+
+          (when (and enable-closed-values?
+                     (db-property-type/property-type-allows-schema-attribute? (:type @*property-schema) :position)
+                     (seq (:values @*property-schema)))
+            (let [position (:position @*property-schema)
+                  choices (map
+                           (fn [item]
+                             (assoc item :selected
+                                    (or (and position (= (:value item) position))
+                                        (and (nil? position) (= (:value item) "properties")))))
+                           [{:label "Block properties"
+                             :value "properties"}
+                            {:label "Beginning of the block"
+                             :value "block-beginning"}
                         ;; {:label "Ending of the block"
                         ;;  :value "block-ending"}
-                           ])]
-             [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-              [:label.col-span-1 "UI position:"]
-              [:div.col-span-2
-               (shui/select
-                (cond-> {:on-value-change (fn [v]
-                                            (swap! *property-schema assoc :position v)
-                                            (save-property-fn))}
-                  (string? position)
-                  (assoc :default-value position))
-                (shui/select-trigger
-                 {:class "!px-2 !py-0 !h-8"}
-                 (shui/select-value
-                  {:placeholder "Select a position mode"})
-                 (shui/tabler-icon "selector" {:class "opacity-30"}))
-                (shui/select-content
-                 (shui/select-group
-                  (for [{:keys [label value]} choices]
-                    (shui/select-item {:value value} label)))))]]))
-
-         (let [hide? (:hide? @*property-schema)]
-           [:div.grid.grid-cols-4.gap-1.items-center.leading-8
-            [:label "Hide by default:"]
-            (shui/checkbox
-             {:checked           hide?
-              :disabled          disabled?
-              :on-checked-change (fn []
-                                   (swap! *property-schema assoc :hide? (not hide?))
-                                   (save-property-fn))})])
-
-         [:div.grid.grid-cols-4.gap-1.items-start.leading-8
-          [:label "Description:"]
-          [:div.col-span-3
-           (if (and disabled? inline-text)
-             (inline-text {} :markdown (:description @*property-schema))
-             [:div.mt-1
-              (shui/textarea
-               {:on-change     (fn [e]
-                                 (swap! *property-schema assoc :description (util/evalue e)))
-                :on-blur       save-property-fn
-                :disabled      disabled?
-                :default-value (:description @*property-schema)})])]]
-
-         (when-not hide-delete?
-           (shui/button
-            {:variant :secondary
-             :class "mt-4 hover:text-red-700"
-             :on-click (fn [e]
-                         (util/stop e)
-                         (handle-delete-property! block property {:class? class? :class-schema? class-schema?})
-                         (when toggle-fn (toggle-fn)))}
-            "Delete property from this block"))]]))))
+                            ])]
+              [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+               [:label.col-span-1 "UI position:"]
+               [:div.col-span-2
+                (shui/select
+                 (cond-> {:on-value-change (fn [v]
+                                             (swap! *property-schema assoc :position v)
+                                             (save-property-fn))}
+                   (string? position)
+                   (assoc :default-value position))
+                 (shui/select-trigger
+                  {:class "!px-2 !py-0 !h-8"}
+                  (shui/select-value
+                   {:placeholder "Select a position mode"})
+                  (shui/tabler-icon "selector" {:class "opacity-30"}))
+                 (shui/select-content
+                  (shui/select-group
+                   (for [{:keys [label value]} choices]
+                     (shui/select-item {:value value} label)))))]]))
+
+          (let [hide? (:hide? @*property-schema)]
+            [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+             [:label "Hide by default:"]
+             (shui/checkbox
+              {:checked           hide?
+               :disabled          disabled?
+               :on-checked-change (fn []
+                                    (swap! *property-schema assoc :hide? (not hide?))
+                                    (save-property-fn))})])
+
+          [:div.grid.grid-cols-4.gap-1.items-start.leading-8
+           [:label "Description:"]
+           [:div.col-span-3
+            (if (and disabled? inline-text)
+              (inline-text {} :markdown (:description @*property-schema))
+              [:div.mt-1
+               (shui/textarea
+                {:on-change     (fn [e]
+                                  (swap! *property-schema assoc :description (util/evalue e)))
+                 :on-blur       save-property-fn
+                 :disabled      disabled?
+                 :default-value (:description @*property-schema)})])]]
+
+          (when-not hide-delete?
+            (shui/button
+             {:variant :secondary
+              :class "mt-4 hover:text-red-700"
+              :on-click (fn [e]
+                          (util/stop e)
+                          (handle-delete-property! block property {:class? class? :class-schema? class-schema?})
+                          (when toggle-fn (toggle-fn)))}
+             "Delete property from this block"))]]))))
 
 (defn- get-property-from-db [name]
   (when-not (string/blank? name)

+ 2 - 2
src/main/frontend/components/property/value.cljs

@@ -184,8 +184,8 @@
   [property
    {:keys [block classes multiple-choices? dropdown? input-opts on-chosen] :as opts}]
   (let [repo (state/get-current-repo)
-        tags? (= "tags" (:block/name property))
-        alias? (= "alias" (:block/name property))
+        tags? (= :tags (:db/ident property))
+        alias? (= :alias (:db/ident property))
         tags-or-alias? (or tags? alias?)
         selected-choices (when block
                            (->>

+ 1 - 1
src/main/frontend/db.cljs

@@ -58,7 +58,7 @@
  [frontend.db.query-react
   react-query custom-query-result-transform]
 
- [logseq.db.frontend.default built-in-pages-names built-in-pages])
+ [logseq.db.frontend.default built-in-pages-names built-in-pages page-title->block])
 
 (defn start-db-conn!
   ([repo]

+ 4 - 3
src/main/frontend/handler/db_based/property.cljs

@@ -20,7 +20,8 @@
             [logseq.db.frontend.property :as db-property]
             [frontend.handler.property.util :as pu]
             [promesa.core :as p]
-            [frontend.db.async :as db-async]))
+            [frontend.db.async :as db-async]
+            [logseq.db :as ldb]))
 
 ;; schema -> type, cardinality, object's class
 ;;           min, max -> string length, number range, cardinality size limit
@@ -228,7 +229,7 @@
                 (if-let [msg (validate-property-value schema v*)]
                   (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))]
                     (notification/show! msg' :warning))
-                  (do
+                  (p/do!
                     (upsert-property! repo k-name (assoc property-schema :type property-type)
                                       {:property-uuid property-uuid})
                     (let [status? (= "status" (string/lower-case k-name))
@@ -312,7 +313,7 @@
             property-values (db-async/<get-block-property-values repo property-uuid)]
       (when (or (not type-changed?)
                 ;; only change type if property hasn't been used yet
-                (empty? property-values))
+                (and (not (ldb/built-in? property)) (empty? property-values)))
         (when (not= ::skip-transact (handle-cardinality-changes repo property-uuid property property-schema property-values))
           (let [tx-data (cond-> {:block/uuid property-uuid}
                           property-name (merge

+ 2 - 0
src/main/frontend/handler/page.cljs

@@ -131,6 +131,8 @@
             result (.page-rename worker repo old-name new-name)
             result' (:result (bean/->clj result))]
       (case result'
+        :built-in-page
+        (notification/show! "Built-in page's name cann't be modified" :error)
         :invalid-empty-name
         (notification/show! "Please use a valid name, empty name is not allowed!" :error)
         :merge-whiteboard-pages

+ 37 - 36
src/main/frontend/worker/handler/page.cljs

@@ -210,41 +210,42 @@
                                      [:db.fn/retractEntity [:block/uuid (:block/uuid block)]])
                                    blocks)
           db-based? (sqlite-util/db-based-graph? repo)]
-      (if-let [msg (and db-based? (page-unable-to-delete conn page))]
-        (do
-          (ldb/transact! conn truncate-blocks-tx-data
-                         {:outliner-op :truncate-page-blocks :persist-op? persist-op?})
-          (error-handler msg))
-        (let [file (ldb/get-page-file @conn page-name)
-              file-path (:file/path file)
-              delete-file-tx (when file
-                               [[:db.fn/retractEntity [:file/path file-path]]])
+      (when-not (ldb/built-in? page)
+        (if-let [msg (and db-based? (page-unable-to-delete conn page))]
+          (do
+            (ldb/transact! conn truncate-blocks-tx-data
+                           {:outliner-op :truncate-page-blocks :persist-op? persist-op?})
+            (error-handler msg))
+          (let [file (ldb/get-page-file @conn page-name)
+                file-path (:file/path file)
+                delete-file-tx (when file
+                                 [[:db.fn/retractEntity [:file/path file-path]]])
               ;; if other page alias this pagename,
               ;; then just remove some attrs of this entity instead of retractEntity
-              delete-page-tx (cond
-                               (or (and db-based? (not (:block/_namespace page)))
-                                   (not db-based?))
-                               (if (and db-based? (ldb/get-alias-source-page @conn page-name))
-                                 (when-let [id (:db/id (d/entity @conn [:block/name page-name]))]
-                                   (mapv (fn [attribute]
-                                           [:db/retract id attribute])
-                                         db-schema/retract-page-attributes))
-                                 (concat (db-refs->page repo page)
-                                         [[:db.fn/retractEntity [:block/name page-name]]]))
-
-                               :else
-                               nil)
-              tx-data (concat truncate-blocks-tx-data delete-page-tx delete-file-tx)]
-
-          (ldb/transact! conn tx-data
-                         (cond-> {:outliner-op :delete-page
-                                  :deleted-page page-name
-                                  :persist-op? persist-op?}
-                           rename?
-                           (assoc :real-outliner-op :rename-page)
-                           file-path
-                           (assoc :file-path file-path)))
-
-          (when (fn? ok-handler) (ok-handler))
-
-          true)))))
+                delete-page-tx (cond
+                                 (or (and db-based? (not (:block/_namespace page)))
+                                     (not db-based?))
+                                 (if (and db-based? (ldb/get-alias-source-page @conn page-name))
+                                   (when-let [id (:db/id (d/entity @conn [:block/name page-name]))]
+                                     (mapv (fn [attribute]
+                                             [:db/retract id attribute])
+                                           db-schema/retract-page-attributes))
+                                   (concat (db-refs->page repo page)
+                                           [[:db.fn/retractEntity [:block/name page-name]]]))
+
+                                 :else
+                                 nil)
+                tx-data (concat truncate-blocks-tx-data delete-page-tx delete-file-tx)]
+
+            (ldb/transact! conn tx-data
+                           (cond-> {:outliner-op :delete-page
+                                    :deleted-page page-name
+                                    :persist-op? persist-op?}
+                             rename?
+                             (assoc :real-outliner-op :rename-page)
+                             file-path
+                             (assoc :file-path file-path)))
+
+            (when (fn? ok-handler) (ok-handler))
+
+            true))))))

+ 3 - 0
src/main/frontend/worker/handler/page/rename.cljs

@@ -254,6 +254,9 @@
         new-page-e (d/entity db [:block/name new-page-name])
         name-changed? (not= old-name new-name)]
     (cond
+      (ldb/built-in? page-e)
+      :built-in-page
+
       (string/blank? new-name)
       :invalid-empty-name