Răsfoiți Sursa

wip page rename

Tienson Qin 2 ani în urmă
părinte
comite
8f8acf1d77

+ 0 - 180
src/main/frontend/components/conversion.cljs

@@ -1,180 +0,0 @@
-(ns frontend.components.conversion
-  (:require [clojure.core.async :as async]
-            [cljs.core.async.interop :refer [p->c]]
-            [promesa.core :as p]
-            [electron.ipc :as ipc]
-            [logseq.common.util :as common-util]
-            [frontend.util :as util]
-            [frontend.state :as state]
-            [frontend.ui :as ui]
-            [frontend.handler.file-based.page :as file-page-handler]
-            [frontend.handler.conversion :refer [supported-filename-formats write-filename-format! calc-rename-target]]
-            [frontend.db :as db]
-            [frontend.context.i18n :refer [t]]
-            [rum.core :as rum]
-            [frontend.handler.file-sync :as file-sync-handler]
-            [frontend.fs.sync :as sync]
-            [frontend.handler.notification :as notification]))
-
-(defn- ask-for-re-index
-  "Multiple-windows? (optional) - if multiple exist on the current graph
-   Dont receive param `repo` as `graph/ask-for-re-index` event doesn't accept repo param"
-  ([]
-   (p/let [repo (state/get-current-repo)
-           multiple-windows? (ipc/ipc "graphHasMultipleWindows" repo)]
-     (ask-for-re-index multiple-windows?)))
-  ([multiple-windows?]
-   (state/pub-event! [:graph/ask-for-re-index (atom multiple-windows?)
-                      (ui/admonition :tip [:p (t :file-rn/re-index)])])))
-
-(defn- <close-modal-on-done
-  "Ask users to re-index when the modal is exited"
-  [sync? rename-items]
-  (async/go
-    (state/close-modal!)
-    (async/<! (async/timeout 100)) ;; modal race condition requires investigation
-    (let [renamed-paths (keep (fn [{:keys [file file-name target]}]
-                                (when (not= file-name target)
-                                  (sync/relative-path (:file/path file)))) rename-items)
-          graph-txid (second @sync/graphs-txid)]
-      (when (and (seq renamed-paths) sync? graph-txid)
-        (async/<!
-         (sync/<delete-remote-files-control
-          sync/remoteapi
-          graph-txid
-          renamed-paths))))
-    (if sync?
-      (notification/show!
-       [:div "Please re-index this graph after all the changes are synced."]
-       :warning
-       false)
-      (ask-for-re-index))))
-
-(rum/defc legacy-warning
-  [repo *target-format *dir-format *solid-format]
-  [:div ;; Normal UX stage 1: show the admonition & button for users using legacy format
-   (ui/admonition :warning [:p (t :file-rn/format-deprecated)])
-   [:p (t :file-rn/instruct-1)]
-   [:p (t :file-rn/instruct-2)
-    (ui/button (t :file-rn/confirm-proceed) ;; the button is for triple-lowbar only
-               :class "text-md p-2 mr-1"
-               :on-click #(do (reset! *target-format :triple-lowbar)
-                              (reset! *dir-format (state/get-filename-format repo)) ;; assure it's uptodate
-                              (write-filename-format! repo :triple-lowbar)
-                              (reset! *solid-format :triple-lowbar)))]
-   [:p (t :file-rn/instruct-3)]])
-
-(rum/defc filename-format-select
-  "A dropdown menu for selecting the target filename format"
-  [*target-format disabled?]
-  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-   [:label.block.text-sm.font-medium.leading-5
-    (t :file-rn/select-format)
-    [:select.form-select.is-small {:disabled disabled?
-                                   :value     (name @*target-format)
-                                   :on-change (fn [e]
-                                                (let [format-str (util/evalue e)]
-                                                  (reset! *target-format (keyword format-str))))}
-     (for [format supported-filename-formats]
-       (let [format-str (name format)]
-         [:option {:key format-str :value format-str} format-str]))]]])
-
-;; UI for files that have been breaking changed. Conversion required to revert the change.
-;; UI logic:
-;;   When dropdown box item switched, activate the `Proceed` button;
-;;   When `Proceed` button clicked, write the filename format to config and allow the renaming actions
-(rum/defcs files-breaking-changed < rum/reactive
-  (rum/local nil ::pages)          ;; pages require renaming, a map of {path -> [page-entity file-entity]}
-  (rum/local nil ::dir-format)     ;; format previously (on `proceed `button clicked)
-  (rum/local nil ::target-format)  ;; format to be converted to (on `proceed` button clicked)
-  (rum/local nil ::solid-format)   ;; format persisted to config
-  (rum/local false ::switch-disabled?) ;; disable the dropdown box when proceeded
-  [state]
-  (let [repo           (state/sub :git/current-repo)
-        *dir-format    (::dir-format state)
-        *target-format (::target-format state)
-        *solid-format  (::solid-format state)
-        *pages         (::pages state)
-        need-persist?  (not= @*solid-format @*target-format)
-        *switch-disabled? (::switch-disabled? state)]
-    (when (nil? @*pages) ;; would triggered on initialization
-      (let [pages-with-file (db/get-pages-with-file repo)
-            the-keys        (map (fn [[_page file]] (:file/path file)) pages-with-file)]
-        (reset! *pages (zipmap the-keys pages-with-file))))
-    (when (and (nil? @*dir-format) ;; would triggered on initialization
-               (nil? @*solid-format)
-               (nil? @*target-format))
-      (let [config-format (state/get-filename-format repo)]
-        (reset! *dir-format config-format)
-        (reset! *solid-format config-format)
-        (reset! *target-format :triple-lowbar)))
-    [:div
-     (when (state/developer-mode?)
-       [:div
-        (filename-format-select *target-format @*switch-disabled?)
-        (ui/button (t :file-rn/select-confirm-proceed) ;; the button is for persisting selected format
-                   :disabled (not need-persist?)
-                   :class "text-sm p-1 mr-1"
-                   :on-click #(do (reset! *dir-format (state/get-filename-format repo)) ;; assure it's uptodate
-                                  (write-filename-format! repo @*target-format)
-                                  (reset! *solid-format @*target-format)
-                                  (reset! *switch-disabled? true)))
-        [:hr]])
-     [:h1.title (t :settings-page/filename-format)]
-     [:div.rounded-md.opacity-70
-      [:p (t :file-rn/filename-desc-1)]
-      [:p (t :file-rn/filename-desc-2)]
-      [:p (t :file-rn/filename-desc-3)]
-      [:p (t :file-rn/filename-desc-4)]]
-     (when (= @*solid-format :legacy)
-       (legacy-warning repo *target-format *dir-format *solid-format))
-     [:div.cp__settings-files-breaking-changed {:disabled need-persist?} [:hr]
-      (let [rename-items  (->> (vals @*pages)
-                               (map (fn [[page file]]
-                                      (when-let [ret (calc-rename-target page (:file/path file) @*dir-format @*target-format)]
-                                        (merge ret {:page page :file file}))))
-                               (remove nil?))
-            sync? (file-sync-handler/current-graph-sync-on?)
-            <rename-all   #(async/go
-                             (doseq [{:keys [file target status]} rename-items]
-                               (when (not= status :unreachable)
-                                 (async/<! (p->c (file-page-handler/rename-file! file target (constantly nil) true)))))
-                             (<close-modal-on-done sync? rename-items))]
-
-        (if (not-empty rename-items)
-          [:div ;; Normal UX stage 2: close stage 1 UI, show the action description as admolition
-           (if (and (= @*solid-format :triple-lowbar)
-                    (= @*dir-format :legacy))
-             (ui/admonition :tip [:p (t :file-rn/need-action)])
-             [:p (t :file-rn/need-action)])
-           [:p
-            (ui/button
-             (t :file-rn/all-action (count rename-items))
-             :on-click <rename-all
-             :class "text-md p-2 mr-1")
-            (t :file-rn/or-select-actions)
-            [:a {:on-click <close-modal-on-done}
-             (t :file-rn/close-panel)]
-            (t :file-rn/or-select-actions-2)]
-           [:p (t :file-rn/legend)]
-           [:table.table-auto
-            [:tbody
-             (for [{:keys [page file status target old-title changed-title]} rename-items]
-               (let [path           (:file/path file)
-                     src-file-name  (common-util/path->file-name path)
-                     tgt-file-name  (str target "." (common-util/path->file-ext path))
-                     rm-item-fn     #(swap! *pages dissoc path)
-                     rename-fn      #(file-page-handler/rename-file! file target rm-item-fn)
-                     rename-but     [:a {:on-click rename-fn
-                                         :title (t :file-rn/apply-rename)}
-                                     [:span (t :file-rn/rename src-file-name tgt-file-name)]]]
-                 [:tr {:key (:block/name page)}
-                  [:td [:div [:p "📄 " old-title]]
-                   (case status
-                     :breaking ;; if property title override the title, it't not breaking change
-                     [:div [:p "🟡 " (t :file-rn/suggest-rename) rename-but]
-                      [:p (t :file-rn/otherwise-breaking) " \"" changed-title \"]]
-                     :unreachable
-                     [:div [:p "🔴 " (t :file-rn/unreachable-title changed-title)]]
-                     [:div [:p "🟢 " (t :file-rn/optional-rename) rename-but]])]]))]]]
-          [:div "🎉 " (t :file-rn/no-action)]))]]))

+ 21 - 25
src/main/frontend/components/settings.cljs

@@ -5,7 +5,6 @@
             [frontend.shui :refer [make-shui-context]]
             [frontend.colors :as colors]
             [frontend.components.assets :as assets]
-            [frontend.components.conversion :as conversion-component]
             [frontend.components.file-sync :as fs]
             [frontend.components.plugins :as plugins]
             [frontend.components.svg :as svg]
@@ -348,23 +347,6 @@
                               :stretch    true
                               :action     pick-theme})]))
 
-(defn file-format-row [t preferred-format]
-  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
-   [:label.block.text-sm.font-medium.leading-5.opacity-70
-    {:for "preferred_format"}
-    (t :settings-page/preferred-file-format)]
-   [:div.mt-1.sm:mt-0.sm:col-span-2
-    [:div.max-w-lg.rounded-md
-     [:select.form-select.is-small
-      {:value     (name preferred-format)
-       :on-change (fn [e]
-                    (let [format (-> (util/evalue e)
-                                     (string/lower-case)
-                                     keyword)]
-                      (user-handler/set-preferred-format! format)))}
-      (for [format (map name [:org :markdown])]
-        [:option {:key format :value format} (string/capitalize format)])]]]])
-
 (defn date-format-row [t preferred-date-format]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
    [:label.block.text-sm.font-medium.leading-5.opacity-70
@@ -646,9 +628,10 @@
   (row-with-button-action
    {:left-label (t :settings-page/filename-format)
     :button-label (t :settings-page/edit-setting)
-    :on-click #(state/set-sub-modal!
-                (fn [_] (conversion-component/files-breaking-changed))
-                {:id :filename-format-panel :center? true})}))
+    ;; :on-click #(state/set-sub-modal!
+    ;;             (fn [_] (conversion-component/files-breaking-changed))
+    ;;             {:id :filename-format-panel :center? true})
+    }))
 
 (rum/defcs native-titlebar-row < rum/reactive
   [state t]
@@ -682,6 +665,23 @@
      (when current-repo (edit-custom-css))
      (when current-repo (edit-export-css))]))
 
+(defn file-format-row [t preferred-format]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "preferred_format"}
+    (t :settings-page/preferred-file-format)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md
+     [:select.form-select.is-small
+      {:value     (name preferred-format)
+       :on-change (fn [e]
+                    (let [format (-> (util/evalue e)
+                                     (string/lower-case)
+                                     keyword)]
+                      (user-handler/set-preferred-format! format)))}
+      (for [format (map name [:org :markdown])]
+        [:option {:key format :value format} (string/capitalize format)])]]]])
+
 (rum/defcs settings-editor < rum/reactive
   [_state current-repo]
   (let [preferred-format (state/get-preferred-format)
@@ -753,10 +753,6 @@
      (when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
      (when (util/electron?) (https-user-agent-row https-agent-opts))
      (when (util/electron?) (auto-chmod-row t))
-     (when (and (util/electron?)
-                (not (config/demo-graph? current-repo))
-                (not (config/db-based-graph? (state/get-current-repo))))
-       (filename-format-row))
      (clear-cache-row t)
 
      (ui/admonition

+ 0 - 5
src/main/frontend/fs/sync.cljs

@@ -516,11 +516,6 @@
             fs-util/include-reserved-chars?
             #(fs-util/include-reserved-chars? (-relative-path %)))
         reserved-paths (filter f paths)]
-    (when (seq reserved-paths)
-      (let [paths (if path-string? reserved-paths (map -relative-path reserved-paths))]
-        (when (seq paths)
-          (state/pub-event! [:ui/notify-outdated-filename-format paths]))
-        (prn "Skipped uploading those file paths with reserved chars: " paths)))
     (vec (remove f paths))))
 
 (defn- diffs->filetxns

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

@@ -4,6 +4,7 @@
   is still some file-specific tech debt to remove from create!"
   (:require [clojure.string :as string]
             [frontend.db :as db]
+            [frontend.db.model :as model]
             [frontend.handler.config :as config-handler]
             [frontend.handler.route :as route-handler]
             [frontend.state :as state]
@@ -14,7 +15,8 @@
             [frontend.config :as config]
             [frontend.fs :as fs]
             [promesa.core :as p]
-            [frontend.handler.block :as block-handler]))
+            [frontend.handler.block :as block-handler]
+            [frontend.handler.file-based.recent :as file-recent-handler]))
 
 (defn create!
   "Create page. Has the following options:
@@ -114,7 +116,9 @@
       ;; TODO: move favorite && unfavorite to worker too
     (unfavorite-page! page-name)
 
-    (route-handler/redirect-to-home!)
+    (when (= (common-util/page-name-sanity-lc (state/get-current-page))
+             (common-util/page-name-sanity-lc page-name))
+      (route-handler/redirect-to-home!))
 
     ;; TODO: why need this?
     (ui-handler/re-render-root!)
@@ -123,3 +127,45 @@
       (-> (p/let [exists? (fs/file-exists? repo-dir file-path)]
             (when exists? (fs/unlink! repo (config/get-repo-fpath repo file-path) nil)))
           (p/catch (fn [error] (js/console.error error)))))))
+
+(defn rename-file!
+  "emit file-rename events to :file/rename-event-chan
+   force-fs? - when true, rename file event the db transact is failed."
+  [old-path new-path]
+  (let [repo (state/get-current-repo)]
+    (->
+     (p/let [_ (state/offer-file-rename-event-chan! {:repo repo
+                                                     :old-path old-path
+                                                     :new-path new-path})]
+       (fs/rename! repo old-path new-path))
+     (p/catch (fn [error]
+                (println "file rename failed: " error))))))
+
+(defn after-page-renamed!
+  [repo {:keys [old-name new-name old-path new-path]}]
+  (let [db-based?           (config/db-based-graph? repo)
+        old-page-name       (common-util/page-name-sanity-lc old-name)
+        new-page-name       (common-util/page-name-sanity-lc new-name)
+        page (db/entity [:block/name new-page-name])
+        redirect? (= (common-util/page-name-sanity-lc (state/get-current-page))
+                     (common-util/page-name-sanity-lc old-page-name))]
+    ;; Redirect to the newly renamed page
+    (when redirect?
+      (route-handler/redirect! {:to          (if (model/whiteboard-page? page) :whiteboard :page)
+                                :push        false
+                                :path-params {:name new-page-name}}))
+
+    (when (favorited? old-page-name)
+      (unfavorite-page! old-page-name)
+      (favorite-page! new-page-name))
+    (let [home (get (state/get-config) :default-home {})]
+      (when (= old-page-name (common-util/page-name-sanity-lc (get home :page "")))
+        (config-handler/set-config! :default-home (assoc home :page new-name))))
+
+    (when-not db-based?
+      (file-recent-handler/update-or-add-renamed-page repo old-page-name new-page-name)
+
+      (when (and old-path new-path)
+        (rename-file! old-path new-path)))
+
+    (ui-handler/re-render-root!)))

+ 135 - 12
src/main/frontend/handler/db_based/page.cljs

@@ -13,9 +13,14 @@
             [frontend.handler.common.page :as page-common-handler]
             [datascript.core :as d]
             [medley.core :as medley]
-            [clojure.string :as string]))
+            [clojure.string :as string]
+            [logseq.common.util.page-ref :as page-ref]
+            [frontend.worker.file.util :as wfu]
+            [frontend.worker.file.page-rename :as page-rename]
+            [logseq.db.sqlite.util :as sqlite-util]
+            [logseq.db :as ldb]))
 
-(defn- replace-ref
+(defn- replace-page-ref
   "Replace from-page refs with to-page"
   [from-page to-page]
   (let [refs (:block/_refs from-page)
@@ -59,8 +64,16 @@
                      refs)]
         tx-data))))
 
+(defn- rename-update-block-refs!
+  [refs from-id to-id]
+  (->> refs
+       (remove #{{:db/id from-id}})
+       (cons {:db/id to-id})
+       (distinct)
+       (vec)))
+
 (defn- based-merge-pages!
-  [from-page-name to-page-name persist-op? redirect?]
+  [db config from-page-name to-page-name persist-op? redirect?]
   (when (and (db/page-exists? from-page-name)
              (db/page-exists? to-page-name)
              (not= from-page-name to-page-name))
@@ -75,6 +88,7 @@
                                     (outliner-core/get-data))
           to-last-direct-child-id (model/get-block-last-direct-child-id (db/get-db) to-id)
           repo (state/get-current-repo)
+          db-based? (sqlite-util/db-based-graph? repo)
           conn (conn/get-db repo false)
           datoms (d/datoms @conn :avet :block/page from-id)
           block-eids (mapv :e datoms)
@@ -83,14 +97,17 @@
                                 (let [id (:db/id block)]
                                   (cond->
                                    {:db/id id
-                                    :block/page {:db/id to-id}}
+                                    :block/page {:db/id to-id}
+                                    :block/refs (rename-update-block-refs! (:block/refs block) from-id to-id)}
 
                                     (and from-first-child (= id (:db/id from-first-child)))
                                     (assoc :block/left {:db/id (or to-last-direct-child-id to-id)})
 
                                     (= (:block/parent block) {:db/id from-id})
                                     (assoc :block/parent {:db/id to-id})))) blocks)
-          replace-ref-tx-data (replace-ref from-page to-page)
+          replace-ref-tx-data (if db-based?
+                                (replace-page-ref from-page to-page)
+                                (page-rename/replace-page-ref db config from-page-name to-page-name))
           tx-data (concat blocks-tx-data replace-ref-tx-data)]
       (db/transact! repo tx-data {:persist-op? persist-op?})
       (page-common-handler/rename-update-namespace! from-page
@@ -104,11 +121,121 @@
                                 :push        false
                                 :path-params {:name to-page-name}}))))
 
+(defn- compute-new-file-path
+  "Construct the full path given old full path and the file sanitized body.
+   Ext. included in the `old-path`."
+  [old-path new-file-name-body]
+  (let [result (string/split old-path "/")
+        ext (last (string/split (last result) "."))
+        new-file (str new-file-name-body "." ext)
+        parts (concat (butlast result) [new-file])]
+    (util/string-join-path parts)))
+
+(defn- update-file-tx
+  [db old-page-name new-page-name]
+  (let [page (d/entity db [:block/name old-page-name])
+        file (:block/file page)]
+    (when (and file (not (:block/journal? page)))
+      (let [old-path (:file/path file)
+            new-file-name (wfu/file-name-sanity new-page-name) ;; w/o file extension
+            new-path (compute-new-file-path old-path new-file-name)]
+        {:old-path old-path
+         :new-path new-path
+         :tx-data [{:db/id (:db/id file)
+                    :file/path new-path}]}))))
+
+(defn- rename-page-aux
+  "Only accepts unsanitized page names"
+  [conn config old-name new-name]
+  (let [db                  @conn
+        old-page-name       (util/page-name-sanity-lc old-name)
+        new-page-name       (util/page-name-sanity-lc new-name)
+        repo                (state/get-current-repo)
+        db-based?           (sqlite-util/db-based-graph? repo)
+        page                (db/pull [:block/name old-page-name])]
+    (when (and repo page)
+      (let [old-original-name   (:block/original-name page)
+            page-txs            [{:db/id               (:db/id page)
+                                  :block/uuid          (:block/uuid page)
+                                  :block/name          new-page-name
+                                  :block/original-name new-name}]
+            {:keys [old-path new-path tx-data]} (update-file-tx db old-page-name new-name)
+            txs (concat page-txs
+                        (when-not db-based?
+                          (->> [;;  update page refes in block content when ref name changes
+                                (page-rename/replace-page-ref db config old-name new-name)
+
+                                ;; update file path
+                                tx-data]
+                               (remove nil?))))]
+
+        (ldb/transact! conn txs {:outliner-op :rename-page
+                                 :data (cond->
+                                        {:old-name old-name
+                                         :new-name new-name}
+                                         (and old-path new-path)
+                                         (merge {:old-path old-path
+                                                 :new-path new-path}))})
+
+        (page-common-handler/rename-update-namespace! page old-original-name new-name)))))
+
+(defn- rename-namespace-pages!
+  "Original names (unsanitized only)"
+  [conn config repo old-name new-name]
+  (let [pages (db/get-namespace-pages repo old-name)
+        page (db/pull [:block/name (util/page-name-sanity-lc old-name)])
+        pages (cons page pages)]
+    (doseq [{:block/keys [name original-name]} pages]
+      (let [old-page-title (or original-name name)
+            ;; only replace one time, for the case that the namespace is a sub-string of the sub-namespace page name
+            ;; Example: has pages [[work]] [[work/worklog]],
+            ;; we want to rename [[work/worklog]] to [[work1/worklog]] when rename [[work]] to [[work1]],
+            ;; but don't rename [[work/worklog]] to [[work1/work1log]]
+            new-page-title (string/replace-first old-page-title old-name new-name)]
+        (when (and old-page-title new-page-title)
+          (rename-page-aux conn config old-page-title new-page-title)
+          (println "Renamed " old-page-title " to " new-page-title))))))
+
+(defn- rename-nested-pages
+  "Unsanitized names only"
+  [conn config old-ns-name new-ns-name]
+  (let [repo            (state/get-current-repo)
+        nested-page-str (page-ref/->page-ref (util/page-name-sanity-lc old-ns-name))
+        ns-prefix-format-str (str page-ref/left-brackets "%s/")
+        ns-prefix       (util/format ns-prefix-format-str (util/page-name-sanity-lc old-ns-name))
+        nested-pages    (db/get-pages-by-name-partition repo nested-page-str)
+        nested-pages-ns (db/get-pages-by-name-partition repo ns-prefix)]
+    (when nested-pages
+      ;; rename page "[[obsidian]] is a tool" to "[[logseq]] is a tool"
+      (doseq [{:block/keys [name original-name]} nested-pages]
+        (let [old-page-title (or original-name name)
+              new-page-title (string/replace
+                              old-page-title
+                              (page-ref/->page-ref old-ns-name)
+                              (page-ref/->page-ref new-ns-name))]
+          (when (and old-page-title new-page-title)
+            (rename-page-aux conn config old-page-title new-page-title)
+            (println "Renamed " old-page-title " to " new-page-title)))))
+    (when nested-pages-ns
+      ;; rename page "[[obsidian/page1]] is a tool" to "[[logseq/page1]] is a tool"
+      (doseq [{:block/keys [name original-name]} nested-pages-ns]
+        (let [old-page-title (or original-name name)
+              new-page-title (string/replace
+                              old-page-title
+                              (util/format ns-prefix-format-str old-ns-name)
+                              (util/format ns-prefix-format-str new-ns-name))]
+          (when (and old-page-title new-page-title)
+            (rename-page-aux conn config old-page-title new-page-title)
+            (println "Renamed " old-page-title " to " new-page-title)))))))
+
 (defn rename!
   ([old-name new-name]
    (rename! old-name new-name true true))
   ([old-name new-name redirect? persist-op?]
    (let [repo (state/get-current-repo)
+         conn (db/get-db false)
+         db @conn
+         config (state/get-config repo)
          old-name      (string/trim old-name)
          new-name      (string/trim new-name)
          old-page-name (util/page-name-sanity-lc old-name)
@@ -140,12 +267,8 @@
 
           (and (not= old-page-name new-page-name)
                (db/entity [:block/name new-page-name])) ; merge page
-          (based-merge-pages! old-page-name new-page-name persist-op? redirect?)
+          (based-merge-pages! db config old-page-name new-page-name persist-op? redirect?)
 
           :else                          ; rename
-          (page-common-handler/create! new-name
-                                       {:rename? true
-                                        :uuid (:block/uuid page-e)
-                                        :redirect? redirect?
-                                        :create-first-block? false
-                                        :persist-op? persist-op?})))))))
+          (rename-namespace-pages! db config repo old-name new-name))
+         (rename-nested-pages conn config old-name new-name))))))

+ 3 - 29
src/main/frontend/handler/events.cljs

@@ -13,7 +13,6 @@
             [frontend.commands :as commands]
             [frontend.components.class :as class-component]
             [frontend.components.cmdk :as cmdk]
-            [frontend.components.conversion :as conversion-component]
             [frontend.components.diff :as diff]
             [frontend.components.encryption :as encryption]
             [frontend.components.file-sync :as file-sync]
@@ -350,6 +349,9 @@
 (defmethod handle :page/deleted [[_ repo page-name file-path]]
   (page-common-handler/after-page-deleted! repo page-name file-path))
 
+(defmethod handle :page/renamed [[_ repo tx-meta]]
+  (page-common-handler/after-page-renamed! repo tx-meta))
+
 (defmethod handle :page/create-today-journal [[_ _repo]]
   (p/let [_ (page-handler/create-today-journal!)]
     (ui-handler/re-render-root!)))
@@ -745,34 +747,6 @@
     (when (= dir (config/get-repo-dir repo))
       (fs/watch-dir! dir))))
 
-(defmethod handle :ui/notify-outdated-filename-format [[_ paths]]
-  ;; paths - the affected paths that contains reserved characters
-  (notification/show!
-   [:div
-    [:div.mb-4
-     [:div.font-semibold.mb-4.text-xl "It seems that some of your filenames are in the outdated format."]
-
-     [:div
-      [:p
-       "We suggest you upgrade now to avoid potential bugs."]
-      (when (seq paths)
-        [:p
-         "For example, the files below have reserved characters that can't be synced on some platforms."])]]
-    (ui/button
-     "Update filename format"
-     :aria-label "Update filename format"
-     :on-click (fn []
-                 (notification/clear-all!)
-                 (state/set-modal!
-                  (fn [_] (conversion-component/files-breaking-changed))
-                  {:id :filename-format-panel :center? true})))
-    (when (seq paths)
-      [:ol.my-2
-       (for [path paths]
-         [:li path])])]
-   :warning
-   false))
-
 (defmethod handle :ui/notify-skipped-downloading-files [[_ paths]]
   (notification/show!
    [:div

+ 0 - 380
src/main/frontend/handler/file_based/page.cljs

@@ -1,380 +0,0 @@
-(ns frontend.handler.file-based.page
-  "Page handlers for file based graphs"
-  (:require [frontend.db :as db]
-            [frontend.db.conn :as conn]
-            [frontend.db.utils :as db-utils]
-            [frontend.db.model :as model]
-            [frontend.handler.file-based.property :as file-property-handler]
-            [frontend.handler.property.file :as property-file]
-            [frontend.handler.file-based.page-property :as file-page-property]
-            [frontend.handler.file-based.recent :as file-recent-handler]
-            [frontend.handler.config :as config-handler]
-            [frontend.handler.common.page :as page-common-handler]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.route :as route-handler]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.state :as state]
-            [frontend.util :as util]
-            [frontend.util.fs :as fs-util]
-            [logseq.outliner.core :as outliner-core]
-            [logseq.outliner.tree :as otree]
-            [frontend.fs :as fs]
-            [logseq.graph-parser.property :as gp-property]
-            [logseq.common.util.page-ref :as page-ref]
-            [lambdaisland.glogi :as log]
-            [promesa.core :as p]
-            [datascript.core :as d]
-            [clojure.walk :as walk]
-            [clojure.string :as string]))
-
-(defn- replace-page-ref!
-  "Unsanitized names"
-  [content old-name new-name]
-  (let [[original-old-name original-new-name] (map string/trim [old-name new-name])
-        [old-ref new-ref] (map page-ref/->page-ref [old-name new-name])
-        [old-name new-name] (map #(if (string/includes? % "/")
-                                    (string/replace % "/" ".")
-                                    %)
-                                 [original-old-name original-new-name])
-        old-org-ref (and (= :org (state/get-preferred-format))
-                         (:org-mode/insert-file-link? (state/get-config))
-                         (re-find
-                          (re-pattern
-                           (util/format
-                            "\\[\\[file:\\.*/.*%s\\.org\\]\\[(.*?)\\]\\]" old-name))
-                          content))]
-    (-> (if old-org-ref
-          (let [[old-full-ref old-label] old-org-ref
-                new-label (if (= old-label original-old-name)
-                            original-new-name
-                            old-label)
-                new-full-ref (-> (string/replace old-full-ref old-name new-name)
-                                 (string/replace (str "[" old-label "]")
-                                                 (str "[" new-label "]")))]
-            (string/replace content old-full-ref new-full-ref))
-          content)
-        (string/replace old-ref new-ref))))
-
-(defn- replace-tag-ref!
-  [content old-name new-name]
-  (let [old-tag (util/format "#%s" old-name)
-        new-tag (if (re-find #"[\s\t]+" new-name)
-                  (util/format "#[[%s]]" new-name)
-                  (str "#" new-name))]
-    ;; hash tag parsing rules https://github.com/logseq/mldoc/blob/701243eaf9b4157348f235670718f6ad19ebe7f8/test/test_markdown.ml#L631
-    ;; Safari doesn't support look behind, don't use
-    ;; TODO: parse via mldoc
-    (string/replace content
-                    (re-pattern (str "(?i)(^|\\s)(" (util/escape-regex-chars old-tag) ")(?=[,\\.]*($|\\s))"))
-                    ;;    case_insense^    ^lhs   ^_grp2                       look_ahead^         ^_grp3
-                    (fn [[_match lhs _grp2 _grp3]]
-                      (str lhs new-tag)))))
-
-(defn- replace-property-ref!
-  [content old-name new-name format]
-  (let [new-name (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
-        org-format? (= :org format)
-        old-property (if org-format? (gp-property/colons-org old-name) (str old-name gp-property/colons))
-        new-property (if org-format? (gp-property/colons-org (name new-name)) (str (name new-name) gp-property/colons))]
-    (util/replace-ignore-case content old-property new-property)))
-
-(defn- replace-old-page!
-  "Unsanitized names"
-  [content old-name new-name format]
-  (when (and (string? content) (string? old-name) (string? new-name))
-    (-> content
-        (replace-page-ref! old-name new-name)
-        (replace-tag-ref! old-name new-name)
-        (replace-property-ref! old-name new-name format))))
-
-(defn- walk-replace-old-page!
-  "Unsanitized names"
-  [form old-name new-name format]
-  (walk/postwalk (fn [f]
-                   (cond
-                     (and (vector? f)
-                          (contains? #{"Search" "Label"} (first f))
-                          (string/starts-with? (second f) (str old-name "/")))
-                     [(first f) (string/replace-first (second f)
-                                                      (str old-name "/")
-                                                      (str new-name "/"))]
-
-                     (string? f)
-                     (if (= f old-name)
-                       new-name
-                       (replace-old-page! f old-name new-name format))
-
-                     (and (keyword f) (= (name f) old-name))
-                     (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
-
-                     :else
-                     f))
-                 form))
-
-(defn- rename-update-block-refs!
-  [refs from-id to-id]
-  (->> refs
-       (remove #{{:db/id from-id}})
-       (cons {:db/id to-id})
-       (distinct)
-       (vec)))
-
-(defn- rename-update-refs!
-  "Unsanitized only"
-  [page old-original-name new-name]
-  ;; update all pages which have references to this page
-  (let [repo (state/get-current-repo)
-        to-page (db/entity [:block/name (util/page-name-sanity-lc new-name)])
-        blocks (:block/_refs (db/entity (:db/id page)))
-        tx       (->> (map (fn [{:block/keys [uuid content properties format] :as block}]
-                             (let [content    (let [content' (replace-old-page! content old-original-name new-name format)]
-                                                (when-not (= content' content)
-                                                  content'))
-                                   properties (let [properties' (walk-replace-old-page! properties old-original-name new-name format)]
-                                                (when-not (= properties' properties)
-                                                  properties'))]
-                               (when (or content properties)
-                                 (util/remove-nils-non-nested
-                                  {:block/uuid       uuid
-                                   :block/content    content
-                                   :block/properties properties
-                                   :block/properties-order (when (seq properties)
-                                                             (map first properties))
-                                   :block/refs (->> (rename-update-block-refs! (:block/refs block) (:db/id page) (:db/id to-page))
-                                                    (map :db/id)
-                                                    (set))})))) blocks)
-                      (remove nil?))]
-    (db/transact! repo tx)))
-
-(defn- compute-new-file-path
-  "Construct the full path given old full path and the file sanitized body.
-   Ext. included in the `old-path`."
-  [old-path new-file-name-body]
-  (let [result (string/split old-path "/")
-        ext (last (string/split (last result) "."))
-        new-file (str new-file-name-body "." ext)
-        parts (concat (butlast result) [new-file])]
-    (util/string-join-path parts)))
-
-(defn rename-file!
-  "emit file-rename events to :file/rename-event-chan
-   force-fs? - when true, rename file event the db transact is failed."
-  ([file new-file-name-body ok-handler]
-   (rename-file! file new-file-name-body ok-handler false))
-  ([file new-file-name-body ok-handler force-fs?]
-   (let [repo (state/get-current-repo)
-         file (db/pull (:db/id file))
-         old-path (:file/path file)
-         new-path (compute-new-file-path old-path new-file-name-body)
-         transact #(db/transact! repo [{:db/id (:db/id file)
-                                        :file/path new-path}])]
-    ;; update db
-     (if force-fs?
-       (try (transact) ;; capture error and continue FS rename if failed
-            (catch :default e
-              (log/error :rename-file e)))
-       (transact)) ;; interrupted if failed
-
-     (->
-      (p/let [_ (state/offer-file-rename-event-chan! {:repo repo
-                                                      :old-path old-path
-                                                      :new-path new-path})
-              _ (fs/rename! repo old-path new-path)]
-        (ok-handler))
-      (p/catch (fn [error]
-                 (println "file rename failed: " error)))))))
-
-(defn- rename-page-aux
-  "Only accepts unsanitized page names"
-  [old-name new-name redirect?]
-  (let [old-page-name       (util/page-name-sanity-lc old-name)
-        new-file-name-body  (fs-util/file-name-sanity new-name) ;; w/o file extension
-        new-page-name       (util/page-name-sanity-lc new-name)
-        repo                (state/get-current-repo)
-        page                (db/pull [:block/name old-page-name])]
-    (when (and repo page)
-      (let [old-original-name   (:block/original-name page)
-            file                (:block/file page)
-            journal?            (:block/journal? page)
-            properties-block    (:data (otree/-get-down (outliner-core/block (db/get-db) page) (db/get-db false)))
-            properties-content  (:block/content properties-block)
-            properties-block-tx (when (and properties-block
-                                           properties-content
-                                           (string/includes? (util/page-name-sanity-lc properties-content)
-                                                             old-page-name))
-                                  (let [front-matter? (and (property-file/front-matter?-when-file-based properties-content)
-                                                           (= :markdown (:block/format properties-block)))]
-                                    {:db/id         (:db/id properties-block)
-                                     :block/content (file-property-handler/insert-property
-                                                     (:block/format properties-block)
-                                                     properties-content
-                                                     :title
-                                                     new-name
-                                                     front-matter?)}))
-            page-txs            [{:db/id               (:db/id page)
-                                  :block/uuid          (:block/uuid page)
-                                  :block/name          new-page-name
-                                  :block/original-name new-name}]
-            page-txs            (if properties-block-tx (conj page-txs properties-block-tx) page-txs)]
-
-        (db/transact! repo page-txs)
-
-        (when (fs-util/create-title-property? new-page-name)
-          (file-page-property/add-property! new-page-name :title new-name))
-
-        (when (and file (not journal?))
-          (rename-file! file new-file-name-body (fn [] nil)))
-
-        (let [home (get (state/get-config) :default-home {})]
-          (when (= old-page-name (util/page-name-sanity-lc (get home :page "")))
-            (config-handler/set-config! :default-home (assoc home :page new-name))))
-
-        (rename-update-refs! page old-original-name new-name)
-
-        (page-common-handler/rename-update-namespace! page old-original-name new-name))
-
-      ;; Redirect to the newly renamed page
-      (when redirect?
-        (route-handler/redirect! {:to          (if (model/whiteboard-page? page) :whiteboard :page)
-                                  :push        false
-                                  :path-params {:name new-page-name}}))
-
-      (when (page-common-handler/favorited? old-page-name)
-        (p/do!
-         (page-common-handler/unfavorite-page! old-page-name)
-         (page-common-handler/favorite-page! new-page-name)))
-
-      (file-recent-handler/update-or-add-renamed-page repo old-page-name new-page-name)
-
-      (ui-handler/re-render-root!))))
-
-(defn- rename-nested-pages
-  "Unsanitized names only"
-  [old-ns-name new-ns-name]
-  (let [repo            (state/get-current-repo)
-        nested-page-str (page-ref/->page-ref (util/page-name-sanity-lc old-ns-name))
-        ns-prefix-format-str (str page-ref/left-brackets "%s/")
-        ns-prefix       (util/format ns-prefix-format-str (util/page-name-sanity-lc old-ns-name))
-        nested-pages    (db/get-pages-by-name-partition repo nested-page-str)
-        nested-pages-ns (db/get-pages-by-name-partition repo ns-prefix)]
-    (when nested-pages
-      ;; rename page "[[obsidian]] is a tool" to "[[logseq]] is a tool"
-      (doseq [{:block/keys [name original-name]} nested-pages]
-        (let [old-page-title (or original-name name)
-              new-page-title (string/replace
-                              old-page-title
-                              (page-ref/->page-ref old-ns-name)
-                              (page-ref/->page-ref new-ns-name))]
-          (when (and old-page-title new-page-title)
-            (p/do!
-             (rename-page-aux old-page-title new-page-title false)
-             (println "Renamed " old-page-title " to " new-page-title))))))
-    (when nested-pages-ns
-      ;; rename page "[[obsidian/page1]] is a tool" to "[[logseq/page1]] is a tool"
-      (doseq [{:block/keys [name original-name]} nested-pages-ns]
-        (let [old-page-title (or original-name name)
-              new-page-title (string/replace
-                              old-page-title
-                              (util/format ns-prefix-format-str old-ns-name)
-                              (util/format ns-prefix-format-str new-ns-name))]
-          (when (and old-page-title new-page-title)
-            (p/do!
-             (rename-page-aux old-page-title new-page-title false)
-             (println "Renamed " old-page-title " to " new-page-title))))))))
-
-(defn- rename-namespace-pages!
-  "Original names (unsanitized only)"
-  [repo old-name new-name]
-  (let [pages (db/get-namespace-pages repo old-name)
-        page (db/pull [:block/name (util/page-name-sanity-lc old-name)])
-        pages (cons page pages)]
-    (doseq [{:block/keys [name original-name]} pages]
-      (let [old-page-title (or original-name name)
-            ;; only replace one time, for the case that the namespace is a sub-string of the sub-namespace page name
-            ;; Example: has pages [[work]] [[work/worklog]],
-            ;; we want to rename [[work/worklog]] to [[work1/worklog]] when rename [[work]] to [[work1]],
-            ;; but don't rename [[work/worklog]] to [[work1/work1log]]
-            new-page-title (string/replace-first old-page-title old-name new-name)
-            redirect? (= name (:block/name page))]
-        (when (and old-page-title new-page-title)
-          (p/let [_ (rename-page-aux old-page-title new-page-title redirect?)]
-            (println "Renamed " old-page-title " to " new-page-title)))))))
-
-(defn merge-pages!
-  "Only accepts sanitized page names"
-  [from-page-name to-page-name]
-  (when (and (db/page-exists? from-page-name)
-             (db/page-exists? to-page-name)
-             (not= from-page-name to-page-name))
-    (let [to-page (db/entity [:block/name to-page-name])
-          conn (db/get-db false)
-          to-id (:db/id to-page)
-          from-page (db/entity [:block/name from-page-name])
-          from-id (:db/id from-page)
-          from-first-child (some->> (db/pull from-id)
-                                    (outliner-core/block @conn)
-                                    (otree/-get-down conn)
-                                    (outliner-core/get-data))
-          to-last-direct-child-id (model/get-block-last-direct-child-id (db/get-db) to-id)
-          repo (state/get-current-repo)
-          conn (conn/get-db repo false)
-          datoms (d/datoms @conn :avet :block/page from-id)
-          block-eids (mapv :e datoms)
-          blocks (db-utils/pull-many repo '[:db/id :block/page :block/refs :block/path-refs :block/left :block/parent] block-eids)
-          tx-data (map (fn [block]
-                         (let [id (:db/id block)]
-                           (cond->
-                            {:db/id id
-                             :block/page {:db/id to-id}
-                             :block/refs (rename-update-block-refs! (:block/refs block) from-id to-id)}
-
-                             (and from-first-child (= id (:db/id from-first-child)))
-                             (assoc :block/left {:db/id (or to-last-direct-child-id to-id)})
-
-                             (= (:block/parent block) {:db/id from-id})
-                             (assoc :block/parent {:db/id to-id})))) blocks)]
-      (db/transact! repo tx-data)
-
-      (rename-update-refs! from-page
-                           (util/get-page-original-name from-page)
-                           (util/get-page-original-name to-page))
-
-      (page-common-handler/rename-update-namespace! from-page
-                                                    (util/get-page-original-name from-page)
-                                                    (util/get-page-original-name to-page)))
-
-
-    (page-common-handler/delete! from-page-name nil)
-
-    (route-handler/redirect! {:to          :page
-                              :push        false
-                              :path-params {:name to-page-name}})))
-
-(defn rename!
-  "Accepts unsanitized page names"
-  ([old-name new-name] (rename! old-name new-name true))
-  ([old-name new-name redirect?]
-   (let [repo          (state/get-current-repo)
-         old-name      (string/trim old-name)
-         new-name      (string/trim new-name)
-         old-page-name (util/page-name-sanity-lc old-name)
-         new-page-name (util/page-name-sanity-lc new-name)
-         name-changed? (not= old-name new-name)]
-     (if (and old-name
-              new-name
-              (not (string/blank? new-name))
-              name-changed?)
-       (do
-         (cond
-           (= old-page-name new-page-name)
-           (rename-page-aux old-name new-name redirect?)
-
-           (db/pull [:block/name new-page-name])
-           (merge-pages! old-page-name new-page-name)
-
-           :else
-           (rename-namespace-pages! repo old-name new-name))
-         (rename-nested-pages old-name new-name))
-       (when (string/blank? new-name)
-         (notification/show! "Please use a valid name, empty name is not allowed!" :error)))
-     (ui-handler/re-render-root!))))

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

@@ -16,7 +16,6 @@
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.db-based.page :as db-page-handler]
-            [frontend.handler.file-based.page :as file-page-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.web.nfs :as web-nfs]
@@ -74,9 +73,7 @@
   ([old-name new-name] (rename! old-name new-name true))
   ([old-name new-name redirect?] (rename! old-name new-name redirect? true))
   ([old-name new-name redirect? persist-op?]
-   (if (config/db-based-graph? (state/get-current-repo))
-     (db-page-handler/rename! old-name new-name redirect? persist-op?)
-     (file-page-handler/rename! old-name new-name redirect?))))
+   (db-page-handler/rename! old-name new-name redirect? persist-op?)))
 
 (defn reorder-favorites!
   [favorites]

+ 1 - 1
src/main/frontend/mobile/intent.cljs

@@ -95,7 +95,7 @@
                         common-util/page-name-sanity)
           path (node-path/join (config/get-repo-dir (state/get-current-repo))
                                (config/get-pages-directory)
-                               (str (js/encodeURI (fs-util/file-name-sanity title)) (node-path/extname url)))
+                               (str (js/encodeURI (fs-util/file-name-sanity title :markdown)) (node-path/extname url)))
           _ (p/catch
              (.copy Filesystem (clj->js {:from url :to path}))
              (fn [error]

+ 3 - 0
src/main/frontend/modules/outliner/pipeline.cljs

@@ -56,6 +56,9 @@
     (when (= (:outliner-op tx-meta) :delete-page)
       (state/pub-event! [:page/deleted repo (:deleted-page tx-meta) (:file-path tx-meta)]))
 
+    (when (= (:outliner-op tx-meta) :rename-page)
+      (state/pub-event! [:page/renamed repo tx-meta]))
+
     (when-not (or from-disk? new-graph?)
       (try
         (reset-editing-block-content! tx-data tx-meta)

+ 3 - 1
src/main/frontend/util/fs.cljs

@@ -75,7 +75,9 @@
 
 (def include-reserved-chars? wfu/include-reserved-chars?)
 (def windows-reserved-filebodies wfu/windows-reserved-filebodies)
-(def file-name-sanity wfu/file-name-sanity)
+(defn file-name-sanity
+  [name _format]
+  (wfu/file-name-sanity name))
 
 (defn create-title-property?
   [page-name]

+ 1 - 1
src/main/frontend/worker/file/core.cljs

@@ -123,7 +123,7 @@
           filename (if journal-page?
                      (worker-date/date->file-name journal-title date-formatter)
                      (-> (or (:block/original-name page-block) (:block/name page-block))
-                         (wfu/file-name-sanity nil)))
+                         wfu/file-name-sanity))
           sub-dir (cond
                     journal-page?    (:journals-directory context)
                     whiteboard-page? (:whiteboards-directory context)

+ 128 - 0
src/main/frontend/worker/file/page_rename.cljs

@@ -0,0 +1,128 @@
+(ns frontend.worker.file.page-rename
+  "File based page rename fns"
+  (:require [frontend.util :as util]
+            [logseq.graph-parser.property :as gp-property]
+            [logseq.common.util.page-ref :as page-ref]
+            [clojure.walk :as walk]
+            [clojure.string :as string]
+            [datascript.core :as d]
+            [logseq.common.config :as common-config]))
+
+(defn- replace-page-ref-aux
+  "Unsanitized names"
+  [config content old-name new-name]
+  (let [preferred-format (common-config/get-preferred-format config)
+        [original-old-name original-new-name] (map string/trim [old-name new-name])
+        [old-ref new-ref] (map page-ref/->page-ref [old-name new-name])
+        [old-name new-name] (map #(if (string/includes? % "/")
+                                    (string/replace % "/" ".")
+                                    %)
+                                 [original-old-name original-new-name])
+        old-org-ref (and (= :org preferred-format)
+                         (:org-mode/insert-file-link? config)
+                         (re-find
+                          (re-pattern
+                           (util/format
+                            "\\[\\[file:\\.*/.*%s\\.org\\]\\[(.*?)\\]\\]" old-name))
+                          content))]
+    (-> (if old-org-ref
+          (let [[old-full-ref old-label] old-org-ref
+                new-label (if (= old-label original-old-name)
+                            original-new-name
+                            old-label)
+                new-full-ref (-> (string/replace old-full-ref old-name new-name)
+                                 (string/replace (str "[" old-label "]")
+                                                 (str "[" new-label "]")))]
+            (string/replace content old-full-ref new-full-ref))
+          content)
+        (string/replace old-ref new-ref))))
+
+(defn- replace-tag-ref!
+  [content old-name new-name]
+  (let [old-tag (util/format "#%s" old-name)
+        new-tag (if (re-find #"[\s\t]+" new-name)
+                  (util/format "#[[%s]]" new-name)
+                  (str "#" new-name))]
+    ;; hash tag parsing rules https://github.com/logseq/mldoc/blob/701243eaf9b4157348f235670718f6ad19ebe7f8/test/test_markdown.ml#L631
+    ;; Safari doesn't support look behind, don't use
+    ;; TODO: parse via mldoc
+    (string/replace content
+                    (re-pattern (str "(?i)(^|\\s)(" (util/escape-regex-chars old-tag) ")(?=[,\\.]*($|\\s))"))
+                    ;;    case_insense^    ^lhs   ^_grp2                       look_ahead^         ^_grp3
+                    (fn [[_match lhs _grp2 _grp3]]
+                      (str lhs new-tag)))))
+
+(defn- replace-property-ref!
+  [content old-name new-name format]
+  (let [new-name (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
+        org-format? (= :org format)
+        old-property (if org-format? (gp-property/colons-org old-name) (str old-name gp-property/colons))
+        new-property (if org-format? (gp-property/colons-org (name new-name)) (str (name new-name) gp-property/colons))]
+    (util/replace-ignore-case content old-property new-property)))
+
+(defn- replace-old-page!
+  "Unsanitized names"
+  [config content old-name new-name format]
+  (when (and (string? content) (string? old-name) (string? new-name))
+    (-> (replace-page-ref-aux config content old-name new-name)
+        (replace-tag-ref! old-name new-name)
+        (replace-property-ref! old-name new-name format))))
+
+(defn- walk-replace-old-page!
+  "Unsanitized names"
+  [config form old-name new-name format]
+  (walk/postwalk (fn [f]
+                   (cond
+                     (and (vector? f)
+                          (contains? #{"Search" "Label"} (first f))
+                          (string/starts-with? (second f) (str old-name "/")))
+                     [(first f) (string/replace-first (second f)
+                                                      (str old-name "/")
+                                                      (str new-name "/"))]
+
+                     (string? f)
+                     (if (= f old-name)
+                       new-name
+                       (replace-old-page! config f old-name new-name format))
+
+                     (and (keyword f) (= (name f) old-name))
+                     (keyword (string/replace (string/lower-case new-name) #"\s+" "-"))
+
+                     :else
+                     f))
+                 form))
+
+(defn- rename-update-block-refs!
+  [refs from-id to-id]
+  (->> refs
+       (remove #{{:db/id from-id}})
+       (cons {:db/id to-id})
+       (distinct)
+       (vec)))
+
+(defn replace-page-ref
+  "Unsanitized only"
+  [db config old-original-name new-name]
+  ;; update all pages which have references to this page
+  (let [page (d/entity db [:block/name (util/page-name-sanity-lc old-original-name)])
+        to-page (d/entity db [:block/name (util/page-name-sanity-lc new-name)])
+        blocks (:block/_refs (d/entity db (:db/id page)))
+        tx       (->> (map (fn [{:block/keys [uuid content properties format] :as block}]
+                             (let [content    (let [content' (replace-old-page! config content old-original-name new-name format)]
+                                                (when-not (= content' content)
+                                                  content'))
+                                   properties (let [properties' (walk-replace-old-page! config properties old-original-name new-name format)]
+                                                (when-not (= properties' properties)
+                                                  properties'))]
+                               (when (or content properties)
+                                 (util/remove-nils-non-nested
+                                  {:block/uuid       uuid
+                                   :block/content    content
+                                   :block/properties properties
+                                   :block/properties-order (when (seq properties)
+                                                             (map first properties))
+                                   :block/refs (->> (rename-update-block-refs! (:block/refs block) (:db/id page) (:db/id to-page))
+                                                    (map :db/id)
+                                                    (set))})))) blocks)
+                      (remove nil?))]
+    tx))

+ 1 - 1
src/main/frontend/worker/file/util.cljs

@@ -64,7 +64,7 @@
 ;; frontend.util.fs         (sanitization only)
 ;; frontend.handler.conversion (both)
 (defn file-name-sanity
-  [title _file-name-format]
+  [title]
   (when (string? title)
     (tri-lb-file-name-sanity title)))