From a7e57c78cf82ae6a0477d0a0f4bc59c080f27ca3 Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Thu, 18 Jun 2026 11:04:35 +0200 Subject: [PATCH] :bug: Add validation for current page on plugins API (#10271) --- frontend/src/app/plugins/api.cljs | 10 + frontend/src/app/plugins/comments.cljs | 3 + frontend/src/app/plugins/flex.cljs | 99 +++- frontend/src/app/plugins/fonts.cljs | 6 + frontend/src/app/plugins/grid.cljs | 94 +++ frontend/src/app/plugins/library.cljs | 6 + frontend/src/app/plugins/page.cljs | 6 + frontend/src/app/plugins/ruler_guides.cljs | 20 +- frontend/src/app/plugins/shape.cljs | 168 +++++- frontend/src/app/plugins/text.cljs | 543 ++++++++++-------- frontend/src/app/plugins/utils.cljs | 7 + .../plugins/page_active_validation_test.cljs | 271 +++++++++ frontend/test/frontend_tests/runner.cljs | 2 + plugins/CHANGELOG.md | 2 + plugins/libs/plugin-types/index.d.ts | 22 +- 15 files changed, 1023 insertions(+), 236 deletions(-) create mode 100644 frontend/test/frontend_tests/plugins/page_active_validation_test.cljs diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 6fe55c6415..aec912d5e9 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -317,6 +317,9 @@ (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (u/not-valid plugin-id :group-shapes shapes) + (some #(not (u/page-active? (obj/get % "$page"))) shapes) + (u/not-valid plugin-id :group "Cannot modify a page that is not currently active") + :else (let [file-id (:current-file-id @st/state) page-id (:current-page-id @st/state) @@ -335,6 +338,10 @@ (and (some? rest) (not (every? shape/shape-proxy? rest))) (u/not-valid plugin-id :ungroup rest) + (or (not (u/page-active? (obj/get group "$page"))) + (some #(not (u/page-active? (obj/get % "$page"))) rest)) + (u/not-valid plugin-id :ungroup "Cannot modify a page that is not currently active") + :else (let [shapes (concat [group] rest) ids (into #{} (map #(obj/get % "$id")) shapes)] @@ -444,6 +451,9 @@ (or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes))) (u/not-valid plugin-id :createBoolean-shapes shapes) + (some #(not (u/page-active? (obj/get % "$page"))) shapes) + (u/not-valid plugin-id :createBoolean "Cannot modify a page that is not currently active") + :else (let [ids (into #{} (map #(obj/get % "$id")) shapes) shape-id (uuid/next)] diff --git a/frontend/src/app/plugins/comments.cljs b/frontend/src/app/plugins/comments.cljs index a06994b309..629f445c40 100644 --- a/frontend/src/app/plugins/comments.cljs +++ b/frontend/src/app/plugins/comments.cljs @@ -136,6 +136,9 @@ (not (r/check-permission plugin-id "comment:write")) (u/not-valid plugin-id :position "Plugin doesn't have 'comment:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :position "Cannot modify a page that is not currently active") + :else (do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)])) (swap! data* assoc :position (gpt/point position))))))} diff --git a/frontend/src/app/plugins/flex.cljs b/frontend/src/app/plugins/flex.cljs index 1e75d09627..57c7fda4e3 100644 --- a/frontend/src/app/plugins/flex.cljs +++ b/frontend/src/app/plugins/flex.cljs @@ -44,6 +44,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :dir "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :dir "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))} @@ -60,6 +63,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :wrap "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :wrap "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value})))))} @@ -76,6 +82,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignItems "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignItems "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))} @@ -92,6 +101,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignContent "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignContent "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))} @@ -108,6 +120,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :justifyItems "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :justifyItems "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))} @@ -124,6 +139,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :justifyContent "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :justifyContent "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))} @@ -139,6 +157,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rowGap "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rowGap "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))} @@ -154,6 +175,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :columnGap "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :columnGap "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))} @@ -169,6 +193,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))} @@ -184,6 +211,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))} @@ -199,6 +229,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :topPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :topPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))} @@ -214,6 +247,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rightPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rightPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))} @@ -229,6 +265,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :bottomPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :bottomPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))} @@ -244,6 +283,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :leftPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :leftPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))} @@ -257,6 +299,10 @@ (not (shape-proxy? child)) (u/not-valid plugin-id :appendChild child) + (or (not (u/page-active? page-id)) + (not (u/page-active? (obj/get child "$page")))) + (u/not-valid plugin-id :appendChild "Cannot modify a page that is not currently active") + :else (let [child-id (obj/get child "$id") shape (u/locate-shape file-id page-id id) @@ -284,6 +330,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalSizing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalSizing "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-item-h-sizing value})))))} @@ -300,6 +349,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalSizing "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))})) @@ -326,6 +378,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :absolute "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :absolute "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value}))))} @@ -335,12 +390,15 @@ :set (fn [_ value] (cond - (sm/valid-safe-int? value) + (not (sm/valid-safe-int? value)) (u/not-valid plugin-id :zIndex value) (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :zIndex "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :zIndex "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value}))))} @@ -357,6 +415,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))} @@ -373,6 +434,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalSizing "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))} @@ -389,6 +453,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignSelf "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignSelf "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))} @@ -404,6 +471,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}}))))} @@ -419,6 +489,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}}))))} @@ -434,6 +507,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :topMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :topMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}}))))} @@ -449,6 +525,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rightMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rightMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}}))))} @@ -464,6 +543,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :bottomMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :bottomMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}}))))} @@ -479,6 +561,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :leftMargin "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :leftMargin "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}}))))} @@ -494,6 +579,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :maxWidth "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :maxWidth "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value}))))} @@ -509,6 +597,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :minWidth "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :minWidth "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value}))))} @@ -524,6 +615,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :maxHeight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :maxHeight "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value}))))} @@ -539,5 +633,8 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :minHeight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :minHeight "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value}))))})) diff --git a/frontend/src/app/plugins/fonts.cljs b/frontend/src/app/plugins/fonts.cljs index 4225623b7d..306db90698 100644 --- a/frontend/src/app/plugins/fonts.cljs +++ b/frontend/src/app/plugins/fonts.cljs @@ -60,6 +60,9 @@ (not (r/check-permission (obj/get text "$plugin") "content:write")) (u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission") + (not (u/page-active? (obj/get text "$page"))) + (u/not-valid plugin-id :applyToText "Cannot modify a page that is not currently active") + :else (let [id (obj/get text "$id") values {:font-id id @@ -78,6 +81,9 @@ (not (r/check-permission (obj/get range "$plugin") "content:write")) (u/not-valid plugin-id :applyToRange "Plugin doesn't have 'content:write' permission") + (not (u/page-active? (obj/get range "$page"))) + (u/not-valid plugin-id :applyToRange "Cannot modify a page that is not currently active") + :else (let [id (obj/get range "$id") start (obj/get range "start") diff --git a/frontend/src/app/plugins/grid.cljs b/frontend/src/app/plugins/grid.cljs index 9374bbb187..5582b80d0c 100644 --- a/frontend/src/app/plugins/grid.cljs +++ b/frontend/src/app/plugins/grid.cljs @@ -45,6 +45,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :dir "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :dir "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))} @@ -69,6 +72,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignItems "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignItems "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))} @@ -85,6 +91,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignContent "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignContent "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))} @@ -101,6 +110,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :justifyItems "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :justifyItems "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))} @@ -117,6 +129,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :justifyContent "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :justifyContent "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))} @@ -132,6 +147,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rowGap "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rowGap "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))} @@ -147,6 +165,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :columnGap "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :columnGap "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))} @@ -162,6 +183,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))} @@ -177,6 +201,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))} @@ -192,6 +219,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :topPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :topPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))} @@ -207,6 +237,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :righPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :righPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))} @@ -222,6 +255,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :bottomPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :bottomPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))} @@ -237,6 +273,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :leftPadding "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :leftPadding "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))} @@ -254,6 +293,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addRow "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addRow "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value}))))) @@ -274,6 +316,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addRowAtIndex "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addRowAtIndex "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index))))) @@ -291,6 +336,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addColumn "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addColumn "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value}))))) @@ -310,6 +358,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addColumnAtIndex "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addColumnAtIndex "Cannot modify a page that is not currently active") + :else (let [type (keyword type)] (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index))))) @@ -323,6 +374,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :removeRow "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :removeRow "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/remove-layout-track #{id} :row index)))) @@ -335,6 +389,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :removeColumn "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :removeColumn "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/remove-layout-track #{id} :column index)))) @@ -355,6 +412,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :setColumn "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :setColumn "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/change-layout-track #{id} :column index (d/without-nils {:type type :value value})))))) @@ -375,6 +435,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :setRow "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :setRow "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/change-layout-track #{id} :row index (d/without-nils {:type type :value value})))))) @@ -384,6 +447,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :remove "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/remove-layout #{id})))) @@ -402,6 +468,10 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :appendChild "Plugin doesn't have 'content:write' permission") + (or (not (u/page-active? page-id)) + (not (u/page-active? (obj/get child "$page")))) + (u/not-valid plugin-id :appendChild "Cannot modify a page that is not currently active") + :else (let [child-id (obj/get child "$id")] (st/emit! (dwt/move-shapes-to-frame #{child-id} id nil [row column]) @@ -440,6 +510,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :row "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :row "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))} @@ -460,6 +533,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rowSpan "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rowSpan "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))} @@ -480,6 +556,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :column "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :column "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))} @@ -500,6 +579,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :columnSpan "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :columnSpan "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))} @@ -520,6 +602,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :areaName "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :areaName "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))} @@ -541,6 +626,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :position "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :position "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))} @@ -562,6 +650,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :alignSelf "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :alignSelf "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))} @@ -583,5 +674,8 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :justifySelf "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :justifySelf "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))}))) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index c92823c1ad..2e5b5c434c 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -517,6 +517,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission") + (not (u/page-active? (obj/get shape "$page"))) + (u/not-valid plugin-id :applyToText "Cannot modify a page that is not currently active") + :else (let [shape-id (obj/get shape "$id") typography (u/locate-library-typography file-id id)] @@ -531,6 +534,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission") + (not (u/page-active? (obj/get range "$page"))) + (u/not-valid plugin-id :applyToText "Cannot modify a page that is not currently active") + :else (let [shape-id (obj/get range "$id") start (obj/get range "start") diff --git a/frontend/src/app/plugins/page.cljs b/frontend/src/app/plugins/page.cljs index 57e37e4a5c..3fe3a8ae9d 100644 --- a/frontend/src/app/plugins/page.cljs +++ b/frontend/src/app/plugins/page.cljs @@ -330,6 +330,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addRulerGuide "Plugin doesn't have 'content:write' permission") + (not (u/page-active? id)) + (u/not-valid plugin-id :addRulerGuide "Cannot modify a page that is not currently active") + :else (let [ruler-id (uuid/next)] (st/emit! @@ -351,6 +354,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :removeRulerGuide "Plugin doesn't have 'comment:write' permission") + (not (u/page-active? id)) + (u/not-valid plugin-id :removeRulerGuide "Cannot modify a page that is not currently active") + :else (let [guide (u/proxy->ruler-guide value)] (st/emit! (-> (dwgu/remove-guide guide) diff --git a/frontend/src/app/plugins/ruler_guides.cljs b/frontend/src/app/plugins/ruler_guides.cljs index 54782ef3a4..75658c14fe 100644 --- a/frontend/src/app/plugins/ruler_guides.cljs +++ b/frontend/src/app/plugins/ruler_guides.cljs @@ -53,6 +53,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :board "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :board "Cannot modify a page that is not currently active") + :else (let [board-id (when value (obj/get value "$id")) guide (-> self u/proxy->ruler-guide)] @@ -85,6 +88,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :position "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :position "Cannot modify a page that is not currently active") + :else (let [guide (u/proxy->ruler-guide self) position @@ -109,12 +115,20 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :color "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :color "Cannot modify a page that is not currently active") + :else (let [guide (u/proxy->ruler-guide self)] (st/emit! (dwgu/update-guides (assoc guide :color value))))))} :remove (fn [] - (let [guide (u/locate-ruler-guide file-id page-id id)] - (st/emit! (-> (dwgu/remove-guide guide) - (se/add-event plugin-id))))))) + (cond + (not (u/page-active? page-id)) + (u/not-valid plugin-id :remove "Cannot modify a page that is not currently active") + + :else + (let [guide (u/locate-ruler-guide file-id page-id id)] + (st/emit! (-> (dwgu/remove-guide guide) + (se/add-event plugin-id)))))))) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index ad44488723..7cb2389dbb 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -196,6 +196,9 @@ (not (sm/validate [:vector types.fills/schema:fill] value)) (u/not-valid plugin-id :fills value) + (not (u/page-active? (obj/get self "$page"))) + (u/not-valid plugin-id :fills "Cannot modify a page that is not currently active") + (cfh/text-shape? shape) (st/emit! (dwt/update-attrs id {:fills value})) @@ -216,6 +219,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :strokes "Plugin doesn't have 'content:write' permission") + (not (u/page-active? (obj/get self "$page"))) + (u/not-valid plugin-id :strokes "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value)))))) @@ -268,6 +274,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :name "Cannot modify a page that is not currently active") + (not valid?) (u/not-valid plugin-id :name value) @@ -286,6 +295,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :blocked "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :blocked "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value))))))} @@ -302,6 +314,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :hidden "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :hidden "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value))))))} @@ -318,6 +333,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :visible "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :visible "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden (not value)))))))} @@ -334,6 +352,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :proportionLock "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :proportionLock "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value))))))} @@ -352,6 +373,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :constraintsHorizontal "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :constraintsHorizontal "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))} @@ -369,6 +393,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :constraintsVertical "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :constraintsVertical "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))} @@ -385,6 +412,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :borderRadius "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :borderRadius "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-all-corners % value))))))} @@ -401,6 +431,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :borderRadiusTopLeft "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r1 value))))))} @@ -417,6 +450,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :borderRadiusTopRight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :borderRadiusTopRight "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r2 value))))))} @@ -433,6 +469,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :borderRadiusBottomRight "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r3 value))))))} @@ -449,6 +488,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :borderRadiusBottomLeft "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r4 value))))))} @@ -465,6 +507,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :opacity "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :opacity "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))} @@ -482,6 +527,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :blendMode "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :blendMode "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))} @@ -499,6 +547,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :shadows "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :shadows "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))} @@ -519,6 +570,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :blur "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :blur "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))} @@ -539,6 +593,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :background-blur "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :background-blur "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :background-blur value)))))))} @@ -556,6 +613,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :exports "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :exports "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :exports value))))))} @@ -573,6 +633,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :x "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :x "Cannot modify a page that is not currently active") + :else (st/emit! (dw/update-position id {:x value} @@ -591,6 +654,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :y "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :y "Cannot modify a page that is not currently active") + :else (st/emit! (dw/update-position id {:y value} @@ -636,6 +702,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :parentX "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :parentX "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id") parent-id (-> self u/proxy->shape :parent-id) @@ -663,6 +732,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :parentY "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :parentY "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id") parent-id (-> self u/proxy->shape :parent-id) @@ -690,6 +762,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :frameX "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :frameX "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id") frame-id (-> self u/proxy->shape :frame-id) @@ -717,6 +792,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :frameY "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :frameY "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id") frame-id (-> self u/proxy->shape :frame-id) @@ -754,6 +832,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rotation "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rotation "Cannot modify a page that is not currently active") + :else (let [shape (u/proxy->shape self)] (st/emit! (dw/increase-rotation #{(:id shape)} value)))))} @@ -770,6 +851,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :flipX "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :flipX "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dw/flip-horizontal-selected #{id})))))} @@ -786,6 +870,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :flipY "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :flipY "Cannot modify a page that is not currently active") + :else (let [id (obj/get self "$id")] (st/emit! (dw/flip-vertical-selected #{id})))))} @@ -853,6 +940,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :resize "Cannot modify a page that is not currently active") + :else (st/emit! (dw/update-dimensions [id] :width width) (dw/update-dimensions [id] :height height)))) @@ -870,6 +960,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :rotate "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :rotate "Cannot modify a page that is not currently active") + :else (st/emit! (dw/increase-rotation [id] angle {:center center :delta? true}))))) @@ -880,6 +973,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :clone "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :clone "Cannot modify a page that is not currently active") + :else (do (st/emit! (dws/duplicate-shapes #{id} :change-selection? false :return-ref ret-v)) (shape-proxy plugin-id (deref ret-v)))))) @@ -890,6 +986,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :remove "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/delete-shapes #{id})))) @@ -916,6 +1015,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :setPluginData "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :setPluginData "Cannot modify a page that is not currently active") + :else (st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "plugin" (str plugin-id)) key value)))) @@ -952,6 +1054,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :setSharedPluginData "Cannot modify a page that is not currently active") + :else (st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "shared" namespace) key value)))) @@ -1002,6 +1107,10 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :appendChild "Plugin doesn't have 'content:write' permission") + (or (not (u/page-active? page-id)) + (not (u/page-active? (obj/get child "$page")))) + (u/not-valid plugin-id :appendChild "Cannot modify a page that is not currently active") + :else (let [child-id (obj/get child "$id") child-shape (u/locate-shape file-id page-id child-id) @@ -1032,6 +1141,10 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :insertChild "Plugin doesn't have 'content:write' permission") + (or (not (u/page-active? page-id)) + (not (u/page-active? (obj/get child "$page")))) + (u/not-valid plugin-id :insertChild "Cannot modify a page that is not currently active") + :else (let [child-id (obj/get child "$id") child-shape (u/locate-shape file-id page-id child-id) @@ -1057,6 +1170,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addFlexLayout "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addFlexLayout "Cannot modify a page that is not currently active") + :else (do (st/emit! (dwsl/create-layout-from-id id :flex :from-frame? true :calculate-params? false) @@ -1073,6 +1189,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addGridLayout "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addGridLayout "Cannot modify a page that is not currently active") + :else (do (st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false)) (se/event plugin-id "create-shape-layout" :layout "grid") @@ -1089,6 +1208,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :makeMask "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :makeMask "Cannot modify a page that is not currently active") + :else (st/emit! (dwg/mask-group #{id}) @@ -1104,6 +1226,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :removeMask "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :removeMask "Cannot modify a page that is not currently active") + :else (st/emit! (dwg/unmask-group #{id}))))) @@ -1148,6 +1273,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :applyTypography "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :applyTypography "Cannot modify a page that is not currently active") + :else (let [typography (u/proxy->library-typography typography)] (st/emit! (dwt/apply-typography #{id} typography file-id)))))) @@ -1162,6 +1290,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :setParentIndex "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :setParentIndex "Cannot modify a page that is not currently active") + :else (st/emit! (dw/set-shape-index file-id page-id id index)))) @@ -1247,7 +1378,12 @@ :detach (fn [] - (st/emit! (dwl/detach-component id))) + (cond + (not (u/page-active? page-id)) + (u/not-valid plugin-id :detach "Cannot modify a page that is not currently active") + + :else + (st/emit! (dwl/detach-component id)))) ;; Export :export @@ -1363,6 +1499,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :addRulerGuide "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :addRulerGuide "Cannot modify a page that is not currently active") + :else (let [ruler-id (uuid/next) axis (parser/orientation->axis orientation) @@ -1388,6 +1527,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :removeRulerGuide "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :removeRulerGuide "Cannot modify a page that is not currently active") + :else (let [guide (u/proxy->ruler-guide value)] (st/emit! (-> (dwgu/remove-guide guide) @@ -1494,6 +1636,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :children "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :children "Cannot modify a page that is not currently active") + (not (every? shape-proxy? children)) (u/not-valid plugin-id :children "Every children needs to be shape proxies") @@ -1527,6 +1672,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :clipContent "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :clipContent "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :show-content (not value))))))} @@ -1543,6 +1691,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :showInViewMode "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :showInViewMode "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :hide-in-viewer (not value))))))} @@ -1578,6 +1729,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :guides "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :guides "Cannot modify a page that is not currently active") + :else (st/emit! (dwsh/update-shapes [id] #(assoc % :grids value))))))} @@ -1603,6 +1757,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :horizontalSizing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :horizontalSizing "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-item-h-sizing value})))))} @@ -1619,6 +1776,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalSizing "Cannot modify a page that is not currently active") + :else (st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))} @@ -1643,6 +1803,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :content "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :content "Cannot modify a page that is not currently active") + (not (sm/validate path/schema:segments segments)) (u/not-valid plugin-id :content segments) @@ -1669,6 +1832,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :content "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :content "Cannot modify a page that is not currently active") + (not (cfh/path-shape? data)) (u/not-valid plugin-id :content-type type) diff --git a/frontend/src/app/plugins/text.cljs b/frontend/src/app/plugins/text.cljs index 8006f3cda9..26e13b1039 100644 --- a/frontend/src/app/plugins/text.cljs +++ b/frontend/src/app/plugins/text.cljs @@ -127,6 +127,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontId "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontId "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end (font-data font variant))))))} @@ -149,6 +152,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontFamily "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontFamily "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end (font-data font variant))))))} @@ -170,6 +176,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontVariantId "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end (variant-data variant))))))} @@ -190,6 +199,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontSize "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontSize "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:font-size value})))))} @@ -217,6 +229,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontWeight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontWeight "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end (variant-data variant))))))} @@ -243,6 +258,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fontStyle "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontStyle "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end (variant-data variant))))))} @@ -263,6 +281,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :lineHeight "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :lineHeight "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:line-height value})))))} @@ -277,12 +298,15 @@ (fn [_ value] (let [value (str/trim (dm/str value))] (cond - (or (empty? value) (re-matches letter-spacing-re value)) + (or (not (string? value)) (not (re-matches letter-spacing-re value))) (u/not-valid plugin-id :letterSpacing value) (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :letterSpacing "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))} @@ -302,6 +326,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :textTransform "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :textTransform "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:text-transform value}))))} @@ -315,12 +342,15 @@ :set (fn [_ value] (cond - (and (string? value) (re-matches text-decoration-re value)) + (or (not (string? value)) (not (re-matches text-decoration-re value))) (u/not-valid plugin-id :textDecoration value) (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :textDecoration "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :textDecoration "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:text-decoration value}))))} @@ -334,12 +364,15 @@ :set (fn [_ value] (cond - (and (string? value) (re-matches text-direction-re value)) + (or (not (string? value)) (not (re-matches text-direction-re value))) (u/not-valid plugin-id :direction value) (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :direction "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :direction "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:direction value}))))} @@ -353,12 +386,15 @@ :set (fn [_ value] (cond - (and (string? value) (re-matches text-align-re value)) + (or (not (string? value)) (not (re-matches text-align-re value))) (u/not-valid plugin-id :align value) (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :align "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :align "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:text-align value}))))} @@ -379,6 +415,9 @@ (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :fills "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fills "Cannot modify a page that is not currently active") + :else (st/emit! (dwt/update-text-range id start end {:fills value})))))} @@ -393,274 +432,320 @@ (defn add-text-props [shape-proxy plugin-id] - (crc/add-properties! - shape-proxy - {:name "characters" - :get #(-> % u/proxy->shape :content txt/content->text) - :set - (fn [self value] - (let [id (obj/get self "$id")] - ;; The user is currently editing the text. We need to update the - ;; editor as well - (cond - (or (not (string? value)) (empty? value)) - (u/not-valid plugin-id :characters value) + (let [page-id (obj/get shape-proxy "$page")] + (crc/add-properties! + shape-proxy + {:name "characters" + :get #(-> % u/proxy->shape :content txt/content->text) + :set + (fn [self value] + (let [id (obj/get self "$id")] + ;; The user is currently editing the text. We need to update the + ;; editor as well + (cond + (or (not (string? value)) (empty? value)) + (u/not-valid plugin-id :characters value) - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :characters "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :characters "Plugin doesn't have 'content:write' permission") - (contains? (:workspace-editor-state @st/state) id) - (let [shape (u/proxy->shape self) - editor - (-> shape - (get :content) - (txt/change-text value) - ted/import-content - ted/create-editor-state)] - (st/emit! (dwt/update-editor-state shape editor))) + (not (u/page-active? page-id)) + (u/not-valid plugin-id :characters "Cannot modify a page that is not currently active") - :else - (do - (st/emit! (dwsh/update-shapes [id] #(update % :content txt/change-text value))) - (when (features/active-feature? @st/state "render-wasm/v1") - (st/emit! (dwwt/resize-wasm-text-debounce id)))))))} + (contains? (:workspace-editor-state @st/state) id) + (let [shape (u/proxy->shape self) + editor + (-> shape + (get :content) + (txt/change-text value) + ted/import-content + ted/create-editor-state)] + (st/emit! (dwt/update-editor-state shape editor))) - {:name "growType" - :get #(-> % u/proxy->shape :grow-type d/name) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (keyword value)] - (cond - (not (contains? #{:auto-width :auto-height :fixed} value)) - (u/not-valid plugin-id :growType value) + :else + (do + (st/emit! (dwsh/update-shapes [id] #(update % :content txt/change-text value))) + (when (features/active-feature? @st/state "render-wasm/v1") + (st/emit! (dwwt/resize-wasm-text-debounce id)))))))} - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :growType "Plugin doesn't have 'content:write' permission") + {:name "growType" + :get #(-> % u/proxy->shape :grow-type d/name) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (keyword value)] + (cond + (not (contains? #{:auto-width :auto-height :fixed} value)) + (u/not-valid plugin-id :growType value) - :else - (st/emit! - (dwsh/update-shapes [id] #(assoc % :grow-type value)) - (when (features/active-feature? @st/state "render-wasm/v1") - (st/emit! (dwwt/resize-wasm-text-debounce id)))))))} + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :growType "Plugin doesn't have 'content:write' permission") - {:name "fontId" - :get #(-> % u/proxy->shape text-props :font-id format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - font (when (string? value) (fonts/get-font-data value)) - variant (fonts/get-default-variant font)] - (cond - (not font) - (u/not-valid plugin-id :fontId value) + (not (u/page-active? page-id)) + (u/not-valid plugin-id :growType "Cannot modify a page that is not currently active") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontId "Plugin doesn't have 'content:write' permission") + :else + (st/emit! + (dwsh/update-shapes [id] #(assoc % :grow-type value)) + (when (features/active-feature? @st/state "render-wasm/v1") + (st/emit! (dwwt/resize-wasm-text-debounce id)))))))} - :else - (st/emit! (dwt/update-attrs id (font-data font variant))))))} + {:name "fontId" + :get #(-> % u/proxy->shape text-props :font-id format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + font (when (string? value) (fonts/get-font-data value)) + variant (fonts/get-default-variant font)] + (cond + (not font) + (u/not-valid plugin-id :fontId value) - {:name "fontFamily" - :get #(-> % u/proxy->shape text-props :font-family format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - font (fonts/find-font-data {:family value}) - variant (fonts/get-default-variant font)] - (cond - (not font) - (u/not-valid plugin-id :fontFamily value) + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontId "Plugin doesn't have 'content:write' permission") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontFamily "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontId "Cannot modify a page that is not currently active") - :else - (st/emit! (dwt/update-attrs id (font-data font variant))))))} + :else + (st/emit! (dwt/update-attrs id (font-data font variant))))))} - {:name "fontVariantId" - :get #(-> % u/proxy->shape text-props :font-variant-id format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - font (fonts/get-font-data (obj/get self "fontId")) - variant (fonts/get-variant font value)] - (cond - (not variant) - (u/not-valid plugin-id :fontVariantId value) + {:name "fontFamily" + :get #(-> % u/proxy->shape text-props :font-family format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + font (fonts/find-font-data {:family value}) + variant (fonts/get-default-variant font)] + (cond + (not font) + (u/not-valid plugin-id :fontFamily value) - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontFamily "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwt/update-attrs id (variant-data variant))))))} + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontFamily "Cannot modify a page that is not currently active") - {:name "fontSize" - :get #(-> % u/proxy->shape text-props :font-size format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (str/trim (dm/str value))] - (cond - (or (empty? value) (not (re-matches font-size-re value))) - (u/not-valid plugin-id :fontSize value) + :else + (st/emit! (dwt/update-attrs id (font-data font variant))))))} - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontSize "Plugin doesn't have 'content:write' permission") + {:name "fontVariantId" + :get #(-> % u/proxy->shape text-props :font-variant-id format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + font (fonts/get-font-data (obj/get self "fontId")) + variant (fonts/get-variant font value)] + (cond + (not variant) + (u/not-valid plugin-id :fontVariantId value) - :else - (st/emit! (dwt/update-attrs id {:font-size value})))))} + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'content:write' permission") - {:name "fontWeight" - :get #(-> % u/proxy->shape text-props :font-weight format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - font (fonts/get-font-data (obj/get self "fontId")) - weight (dm/str value) - style (obj/get self "fontStyle") - variant - (or - (fonts/find-variant font {:style style :weight weight}) - (fonts/find-variant font {:weight weight}))] - (cond - (nil? variant) - (u/not-valid plugin-id :fontWeight (dm/str "Font weight '" value "' not supported for the current font")) + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontVariantId "Cannot modify a page that is not currently active") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontWeight "Plugin doesn't have 'content:write' permission") + :else + (st/emit! (dwt/update-attrs id (variant-data variant))))))} - :else - (st/emit! (dwt/update-attrs id (variant-data variant))))))} + {:name "fontSize" + :get #(-> % u/proxy->shape text-props :font-size format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (str/trim (dm/str value))] + (cond + (or (empty? value) (not (re-matches font-size-re value))) + (u/not-valid plugin-id :fontSize value) - {:name "fontStyle" - :get #(-> % u/proxy->shape text-props :font-style format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - font (fonts/get-font-data (obj/get self "fontId")) - style (dm/str value) - weight (obj/get self "fontWeight") - variant - (or - (fonts/find-variant font {:weight weight :style style}) - (fonts/find-variant font {:style style}))] - (cond - (nil? variant) - (u/not-valid plugin-id :fontStyle (dm/str "Font style '" value "' not supported for the current font")) + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontSize "Plugin doesn't have 'content:write' permission") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :fontStyle "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontSize "Cannot modify a page that is not currently active") - :else - (st/emit! (dwt/update-attrs id (variant-data variant))))))} + :else + (st/emit! (dwt/update-attrs id {:font-size value})))))} - {:name "lineHeight" - :get #(-> % u/proxy->shape text-props :line-height format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (str/trim (dm/str value))] - (cond - (or (empty? value) (not (re-matches line-height-re value))) - (u/not-valid plugin-id :lineHeight value) + {:name "fontWeight" + :get #(-> % u/proxy->shape text-props :font-weight format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + font (fonts/get-font-data (obj/get self "fontId")) + weight (dm/str value) + style (obj/get self "fontStyle") + variant + (or + (fonts/find-variant font {:style style :weight weight}) + (fonts/find-variant font {:weight weight}))] + (cond + (nil? variant) + (u/not-valid plugin-id :fontWeight (dm/str "Font weight '" value "' not supported for the current font")) - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :lineHeight "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontWeight "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwt/update-attrs id {:line-height value})))))} + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontWeight "Cannot modify a page that is not currently active") - {:name "letterSpacing" - :get #(-> % u/proxy->shape text-props :letter-spacing format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (str/trim (dm/str value))] - (cond - (or (not (string? value)) (not (re-matches letter-spacing-re value))) - (u/not-valid plugin-id :letterSpacing value) + :else + (st/emit! (dwt/update-attrs id (variant-data variant))))))} - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'content:write' permission") + {:name "fontStyle" + :get #(-> % u/proxy->shape text-props :font-style format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + font (fonts/get-font-data (obj/get self "fontId")) + style (dm/str value) + weight (obj/get self "fontWeight") + variant + (or + (fonts/find-variant font {:weight weight :style style}) + (fonts/find-variant font {:style style}))] + (cond + (nil? variant) + (u/not-valid plugin-id :fontStyle (dm/str "Font style '" value "' not supported for the current font")) - :else - (st/emit! (dwt/update-attrs id {:letter-spacing value})))))} + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :fontStyle "Plugin doesn't have 'content:write' permission") - {:name "textTransform" - :get #(-> % u/proxy->shape text-props :text-transform format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (or (not (string? value)) (not (re-matches text-transform-re value))) - (u/not-valid plugin-id :textTransform value) + (not (u/page-active? page-id)) + (u/not-valid plugin-id :fontStyle "Cannot modify a page that is not currently active") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :textTransform "Plugin doesn't have 'content:write' permission") + :else + (st/emit! (dwt/update-attrs id (variant-data variant))))))} - :else - (st/emit! (dwt/update-attrs id {:text-transform value})))))} + {:name "lineHeight" + :get #(-> % u/proxy->shape text-props :line-height format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (str/trim (dm/str value))] + (cond + (or (empty? value) (not (re-matches line-height-re value))) + (u/not-valid plugin-id :lineHeight value) - {:name "textDecoration" - :get #(-> % u/proxy->shape text-props :text-decoration format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (or (not (string? value)) (not (re-matches text-decoration-re value))) - (u/not-valid plugin-id :textDecoration value) + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :lineHeight "Plugin doesn't have 'content:write' permission") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :textDecoration "Plugin doesn't have 'content:write' permission") + (not (u/page-active? page-id)) + (u/not-valid plugin-id :lineHeight "Cannot modify a page that is not currently active") - :else - (st/emit! (dwt/update-attrs id {:text-decoration value})))))} + :else + (st/emit! (dwt/update-attrs id {:line-height value})))))} - {:name "direction" - :get #(-> % u/proxy->shape text-props :text-direction format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (or (not (string? value)) (not (re-matches text-direction-re value))) - (u/not-valid plugin-id :textDirection value) + {:name "letterSpacing" + :get #(-> % u/proxy->shape text-props :letter-spacing format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (str/trim (dm/str value))] + (cond + (or (not (string? value)) (not (re-matches letter-spacing-re value))) + (u/not-valid plugin-id :letterSpacing value) - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :textDirection "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwt/update-attrs id {:text-direction value})))))} + (not (u/page-active? page-id)) + (u/not-valid plugin-id :letterSpacing "Cannot modify a page that is not currently active") - {:name "align" - :get #(-> % u/proxy->shape text-props :text-align format/format-mixed) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (or (not (string? value)) (not (re-matches text-align-re value))) - (u/not-valid plugin-id :align value) + :else + (st/emit! (dwt/update-attrs id {:letter-spacing value})))))} - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :align "Plugin doesn't have 'content:write' permission") + {:name "textTransform" + :get #(-> % u/proxy->shape text-props :text-transform format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (string? value)) (not (re-matches text-transform-re value))) + (u/not-valid plugin-id :textTransform value) - :else - (st/emit! (dwt/update-attrs id {:text-align value})))))} + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :textTransform "Plugin doesn't have 'content:write' permission") - {:name "verticalAlign" - :get #(-> % u/proxy->shape text-props :vertical-align) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (or (not (string? value)) (not (re-matches vertical-align-re value))) - (u/not-valid plugin-id :verticalAlign value) + (not (u/page-active? page-id)) + (u/not-valid plugin-id :textTransform "Cannot modify a page that is not currently active") - (not (r/check-permission plugin-id "content:write")) - (u/not-valid plugin-id :verticalAlign "Plugin doesn't have 'content:write' permission") + :else + (st/emit! (dwt/update-attrs id {:text-transform value})))))} - :else - (st/emit! (dwt/update-attrs id {:vertical-align value})))))} + {:name "textDecoration" + :get #(-> % u/proxy->shape text-props :text-decoration format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (string? value)) (not (re-matches text-decoration-re value))) + (u/not-valid plugin-id :textDecoration value) - {:name "textBounds" - :get #(-> % u/proxy->shape gst/shape->bounds format/format-geom-rect)})) + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :textDecoration "Plugin doesn't have 'content:write' permission") + + (not (u/page-active? page-id)) + (u/not-valid plugin-id :textDecoration "Cannot modify a page that is not currently active") + + :else + (st/emit! (dwt/update-attrs id {:text-decoration value})))))} + + {:name "direction" + :get #(-> % u/proxy->shape text-props :text-direction format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (string? value)) (not (re-matches text-direction-re value))) + (u/not-valid plugin-id :textDirection value) + + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :textDirection "Plugin doesn't have 'content:write' permission") + + (not (u/page-active? page-id)) + (u/not-valid plugin-id :textDirection "Cannot modify a page that is not currently active") + + :else + (st/emit! (dwt/update-attrs id {:text-direction value})))))} + + {:name "align" + :get #(-> % u/proxy->shape text-props :text-align format/format-mixed) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (string? value)) (not (re-matches text-align-re value))) + (u/not-valid plugin-id :align value) + + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :align "Plugin doesn't have 'content:write' permission") + + (not (u/page-active? page-id)) + (u/not-valid plugin-id :align "Cannot modify a page that is not currently active") + + :else + (st/emit! (dwt/update-attrs id {:text-align value})))))} + + {:name "verticalAlign" + :get #(-> % u/proxy->shape text-props :vertical-align) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (string? value)) (not (re-matches vertical-align-re value))) + (u/not-valid plugin-id :verticalAlign value) + + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :verticalAlign "Plugin doesn't have 'content:write' permission") + + (not (u/page-active? page-id)) + (u/not-valid plugin-id :verticalAlign "Cannot modify a page that is not currently active") + + :else + (st/emit! (dwt/update-attrs id {:vertical-align value})))))} + + {:name "textBounds" + :get #(-> % u/proxy->shape gst/shape->bounds format/format-geom-rect)}))) diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index 3c3a6b3530..e08e792ada 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -43,6 +43,13 @@ (assert (uuid? id) "Shape not valid uuid") (dm/get-in (locate-page file-id page-id) [:objects id])) +(defn page-active? + "Returns true if `page-id` is the currently active page. Plugin structural + operations only affect the active page, so callers use this to reject + attempts to modify shapes that live on a different page." + [page-id] + (= page-id (:current-page-id @st/state))) + (defn locate-library-color [file-id id] (assert (uuid? id) "Color not valid uuid") diff --git a/frontend/test/frontend_tests/plugins/page_active_validation_test.cljs b/frontend/test/frontend_tests/plugins/page_active_validation_test.cljs new file mode 100644 index 0000000000..5b36691404 --- /dev/null +++ b/frontend/test/frontend_tests/plugins/page_active_validation_test.cljs @@ -0,0 +1,271 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC Sucursal en EspaƱa SL + +(ns frontend-tests.plugins.page-active-validation-test + "Tests for the guard that prevents plugins from modifying shapes/properties that + live on a page which is not the currently active one (see + `app.plugins.utils/page-active?`). + + Strategy: build a rich page2 via the public API *while page2 is active*, then + enable `throwValidationErrors` so rejections surface as exceptions. + + - Negative test: with page1 active, every guarded property/method invoked on a + page2-bound proxy must throw. + - Positive test: with page2 active, every setter (with a valid value) must NOT + throw. This proves the values used are valid, so the negative throws can only + come from the page guard." + (:require + [app.common.test-helpers.files :as cthf] + [app.main.store :as st] + [app.plugins.api :as api] + [app.util.object :as obj] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw] + [potok.v2.core :as ptk])) + +(def ^:private plugin-id "00000000-0000-0000-0000-000000000000") + +(defn- activate-page! + [store page-id] + (ptk/emit! store #(assoc % :current-page-id page-id))) + +(defn- throws? + [thunk] + (try (thunk) false (catch :default _ true))) + +(defn- setup + "Creates a file with two pages and builds, *on the non-active page2*, a set of + proxies covering every guarded proxy type. Leaves page1 active with the + `throwValidationErrors` flag enabled. Returns the proxies + store + page ids." + [] + (let [file (-> (cthf/sample-file :file1 :page-label :page1) + (cthf/add-sample-page :page2)) + store (ths/setup-store file) + _ (set! st/state store) + _ (set! st/stream (ptk/input-stream store)) + ^js context (api/create-context plugin-id) + pages (.. context -currentFile -pages) + page1-id (obj/get (aget pages 0) "$id") + ^js page2 (aget pages 1) + page2-id (obj/get page2 "$id") + + ;; Build everything with page2 active so the constructive API succeeds. + _ (activate-page! store page2-id) + + ^js rect (.createRectangle context) + ^js rect2 (.createRectangle context) + ^js board (.createBoard context) + ^js child (.createRectangle context) + _ (.appendChild board child) + + ^js flex-board (.createBoard context) + ^js flex (.addFlexLayout flex-board) + ^js flex-child (.createRectangle context) + _ (.appendChild flex-board flex-child) + ^js layout-child (.-layoutChild flex-child) + + ^js grid-board (.createBoard context) + ^js grid (.addGridLayout grid-board) + + ^js text (.createText context "hello") + ^js range (.getRange text 0 1) + + ^js guide (.addRulerGuide page2 "vertical" 10 board) + + ;; Back to page1: page2 proxies must now be read-only. + _ (activate-page! store page1-id) + _ (ptk/emit! store #(assoc-in % [:plugins :flags plugin-id :throw-validation-errors] true))] + + {:store store :page1-id page1-id :page2-id page2-id :context context :page2 page2 + :rect rect :rect2 rect2 :board board :child child + :flex flex :flex-board flex-board :layout-child layout-child :flex-child flex-child + :grid grid :grid-board grid-board + :text text :range range :guide guide})) + +(defn- setter-specs + "Property setters (value mutations). Used by both the positive and negative + tests, so they must be idempotent / non-destructive." + [{:keys [^js rect ^js child ^js flex ^js grid ^js layout-child + ^js range ^js guide ^js board ^js text]}] + [;; ---- ShapeProxy ---- + ["ShapeProxy.name" #(set! (.-name rect) "X")] + ["ShapeProxy.x" #(set! (.-x rect) 10)] + ["ShapeProxy.y" #(set! (.-y rect) 10)] + ["ShapeProxy.blocked" #(set! (.-blocked rect) true)] + ["ShapeProxy.hidden" #(set! (.-hidden rect) true)] + ["ShapeProxy.visible" #(set! (.-visible rect) false)] + ["ShapeProxy.proportionLock" #(set! (.-proportionLock rect) true)] + ["ShapeProxy.constraintsHorizontal" #(set! (.-constraintsHorizontal rect) "right")] + ["ShapeProxy.constraintsVertical" #(set! (.-constraintsVertical rect) "bottom")] + ["ShapeProxy.borderRadius" #(set! (.-borderRadius rect) 5)] + ["ShapeProxy.borderRadiusTopLeft" #(set! (.-borderRadiusTopLeft rect) 5)] + ["ShapeProxy.borderRadiusTopRight" #(set! (.-borderRadiusTopRight rect) 5)] + ["ShapeProxy.borderRadiusBottomRight" #(set! (.-borderRadiusBottomRight rect) 5)] + ["ShapeProxy.borderRadiusBottomLeft" #(set! (.-borderRadiusBottomLeft rect) 5)] + ["ShapeProxy.opacity" #(set! (.-opacity rect) 0.5)] + ["ShapeProxy.blendMode" #(set! (.-blendMode rect) "multiply")] + ["ShapeProxy.shadows" #(set! (.-shadows rect) #js [#js {:style "drop-shadow" :color #js {:color "#000000" :opacity 1}}])] + ["ShapeProxy.blur" #(set! (.-blur rect) #js {:value 10})] + ["ShapeProxy.exports" #(set! (.-exports rect) #js [#js {:type "png" :scale 1 :suffix ""}])] + ["ShapeProxy.flipX" #(set! (.-flipX rect) true)] + ["ShapeProxy.flipY" #(set! (.-flipY rect) true)] + ["ShapeProxy.rotation" #(set! (.-rotation rect) 45)] + ["ShapeProxy.fills" #(set! (.-fills rect) #js [#js {:fillColor "#fabada" :fillOpacity 1}])] + ["ShapeProxy.strokes" #(set! (.-strokes rect) #js [#js {:strokeColor "#fabada" :strokeOpacity 1 :strokeWidth 2}])] + ;; relative geometry (shape inside a board) + ["ShapeProxy.boardX" #(set! (.-boardX child) 10)] + ["ShapeProxy.boardY" #(set! (.-boardY child) 10)] + ["ShapeProxy.parentX" #(set! (.-parentX child) 10)] + ["ShapeProxy.parentY" #(set! (.-parentY child) 10)] + ;; layout-item sizing (frame is a layout item) + ["ShapeProxy.horizontalSizing" #(set! (.-horizontalSizing board) "fix")] + ["ShapeProxy.verticalSizing" #(set! (.-verticalSizing board) "fix")] + ;; text shape props (added via add-text-props) + ["ShapeProxy.growType" #(set! (.-growType text) "fixed")] + ["ShapeProxy.verticalAlign" #(set! (.-verticalAlign text) "center")] + + ;; ---- FlexLayoutProxy ---- + ["FlexLayoutProxy.dir" #(set! (.-dir flex) "row")] + ["FlexLayoutProxy.wrap" #(set! (.-wrap flex) "wrap")] + ["FlexLayoutProxy.alignItems" #(set! (.-alignItems flex) "center")] + ["FlexLayoutProxy.alignContent" #(set! (.-alignContent flex) "center")] + ["FlexLayoutProxy.justifyItems" #(set! (.-justifyItems flex) "start")] + ["FlexLayoutProxy.justifyContent" #(set! (.-justifyContent flex) "center")] + ["FlexLayoutProxy.rowGap" #(set! (.-rowGap flex) 5)] + ["FlexLayoutProxy.columnGap" #(set! (.-columnGap flex) 5)] + ["FlexLayoutProxy.verticalPadding" #(set! (.-verticalPadding flex) 5)] + ["FlexLayoutProxy.horizontalPadding" #(set! (.-horizontalPadding flex) 5)] + ["FlexLayoutProxy.topPadding" #(set! (.-topPadding flex) 5)] + ["FlexLayoutProxy.rightPadding" #(set! (.-rightPadding flex) 5)] + ["FlexLayoutProxy.bottomPadding" #(set! (.-bottomPadding flex) 5)] + ["FlexLayoutProxy.leftPadding" #(set! (.-leftPadding flex) 5)] + + ;; ---- LayoutChildProxy ---- + ["LayoutChildProxy.absolute" #(set! (.-absolute layout-child) true)] + ["LayoutChildProxy.zIndex" #(set! (.-zIndex layout-child) 1)] + ["LayoutChildProxy.horizontalSizing" #(set! (.-horizontalSizing layout-child) "fix")] + ["LayoutChildProxy.verticalSizing" #(set! (.-verticalSizing layout-child) "fix")] + ["LayoutChildProxy.alignSelf" #(set! (.-alignSelf layout-child) "center")] + ["LayoutChildProxy.horizontalMargin" #(set! (.-horizontalMargin layout-child) 5)] + ["LayoutChildProxy.verticalMargin" #(set! (.-verticalMargin layout-child) 5)] + ["LayoutChildProxy.topMargin" #(set! (.-topMargin layout-child) 5)] + ["LayoutChildProxy.rightMargin" #(set! (.-rightMargin layout-child) 5)] + ["LayoutChildProxy.bottomMargin" #(set! (.-bottomMargin layout-child) 5)] + ["LayoutChildProxy.leftMargin" #(set! (.-leftMargin layout-child) 5)] + ["LayoutChildProxy.maxWidth" #(set! (.-maxWidth layout-child) 100)] + ["LayoutChildProxy.maxHeight" #(set! (.-maxHeight layout-child) 100)] + ["LayoutChildProxy.minWidth" #(set! (.-minWidth layout-child) 0)] + ["LayoutChildProxy.minHeight" #(set! (.-minHeight layout-child) 0)] + + ;; ---- GridLayoutProxy ---- + ["GridLayoutProxy.dir" #(set! (.-dir grid) "row")] + ["GridLayoutProxy.alignItems" #(set! (.-alignItems grid) "center")] + ["GridLayoutProxy.alignContent" #(set! (.-alignContent grid) "center")] + ["GridLayoutProxy.justifyItems" #(set! (.-justifyItems grid) "start")] + ["GridLayoutProxy.justifyContent" #(set! (.-justifyContent grid) "center")] + ["GridLayoutProxy.rowGap" #(set! (.-rowGap grid) 5)] + ["GridLayoutProxy.columnGap" #(set! (.-columnGap grid) 5)] + ["GridLayoutProxy.verticalPadding" #(set! (.-verticalPadding grid) 5)] + ["GridLayoutProxy.horizontalPadding" #(set! (.-horizontalPadding grid) 5)] + ["GridLayoutProxy.topPadding" #(set! (.-topPadding grid) 5)] + ["GridLayoutProxy.rightPadding" #(set! (.-rightPadding grid) 5)] + ["GridLayoutProxy.bottomPadding" #(set! (.-bottomPadding grid) 5)] + ["GridLayoutProxy.leftPadding" #(set! (.-leftPadding grid) 5)] + + ;; NOTE: GridCellProxy setters (row/column/rowSpan/columnSpan/areaName/position/ + ;; justifySelf/alignSelf) share the identical guard but require a child placed in + ;; a grid cell, which cannot be fixtured in this headless test env + ;; (move-shapes-to-frame is WASM-bound). They are verified by source review. + + ;; ---- TextRangeProxy ---- + ["TextRangeProxy.align" #(set! (.-align range) "center")] + ["TextRangeProxy.direction" #(set! (.-direction range) "ltr")] + ["TextRangeProxy.textTransform" #(set! (.-textTransform range) "uppercase")] + ["TextRangeProxy.textDecoration" #(set! (.-textDecoration range) "underline")] + ["TextRangeProxy.fontSize" #(set! (.-fontSize range) "16")] + ["TextRangeProxy.lineHeight" #(set! (.-lineHeight range) "1.2")] + ["TextRangeProxy.letterSpacing" #(set! (.-letterSpacing range) "1")] + ["TextRangeProxy.fills" #(set! (.-fills range) #js [#js {:fillColor "#fabada" :fillOpacity 1}])] + + ;; ---- RulerGuideProxy ---- + ["RulerGuideProxy.board" #(set! (.-board guide) board)] + ["RulerGuideProxy.color" #(set! (.-color guide) "#fabada")] + ["RulerGuideProxy.position" #(set! (.-position guide) 20)]]) + +(defn- method-specs + "Structural / destructive methods. Tested in the negative direction only (they + are rejected before performing any mutation), with trivially valid arguments." + [{:keys [^js context ^js page2 ^js rect ^js rect2 ^js board ^js child + ^js flex ^js grid ^js guide]}] + [;; ---- ShapeProxy ---- + ["ShapeProxy.resize" #(.resize rect 10 10)] + ["ShapeProxy.rotate" #(.rotate rect 45)] + ["ShapeProxy.clone" #(.clone rect)] + ["ShapeProxy.remove" #(.remove rect)] + ["ShapeProxy.setParentIndex" #(.setParentIndex child 0)] + ["ShapeProxy.setPluginData" #(.setPluginData rect "k" "v")] + ["ShapeProxy.setSharedPluginData" #(.setSharedPluginData rect "ns" "k" "v")] + ["ShapeProxy.appendChild" #(.appendChild board rect)] + ["ShapeProxy.insertChild" #(.insertChild board 0 rect)] + ["ShapeProxy.addFlexLayout" #(.addFlexLayout board)] + ["ShapeProxy.addGridLayout" #(.addGridLayout board)] + + ;; ---- FlexLayoutProxy ---- + ["FlexLayoutProxy.appendChild" #(.appendChild flex rect)] + + ;; ---- GridLayoutProxy ---- + ["GridLayoutProxy.appendChild" #(.appendChild grid rect 1 1)] + ["GridLayoutProxy.addRow" #(.addRow grid "flex" 1)] + ["GridLayoutProxy.addColumn" #(.addColumn grid "flex" 1)] + ["GridLayoutProxy.addRowAtIndex" #(.addRowAtIndex grid 0 "flex" 1)] + ["GridLayoutProxy.addColumnAtIndex" #(.addColumnAtIndex grid 0 "flex" 1)] + ["GridLayoutProxy.setRow" #(.setRow grid 1 "flex" 1)] + ["GridLayoutProxy.setColumn" #(.setColumn grid 1 "flex" 1)] + ["GridLayoutProxy.removeRow" #(.removeRow grid 0)] + ["GridLayoutProxy.removeColumn" #(.removeColumn grid 0)] + ["GridLayoutProxy.remove" #(.remove grid)] + + ;; ---- GridCellProxy methods ---- + ;; (none beyond setters) + + ;; ---- RulerGuideProxy ---- + ["RulerGuideProxy.remove" #(.remove guide)] + + ;; ---- Context ---- + ["context.group" #(.group context #js [rect rect2])] + ["context.ungroup" #(.ungroup context rect)] + ["context.createBoolean" #(.createBoolean context "union" #js [rect rect2])] + + ;; ---- PageProxy ---- + ["page.addRulerGuide" #(.addRulerGuide page2 "vertical" 20 board)] + ["page.removeRulerGuide" #(.removeRulerGuide page2 guide)]]) + +(t/deftest test-all-setters-rejected-on-non-active-page + (thw/with-wasm-mocks* + (fn [] + (let [m (setup)] + (doseq [[label thunk] (setter-specs m)] + (t/is (throws? thunk) (str label " must be rejected on a non-active page"))))))) + +(t/deftest test-all-methods-rejected-on-non-active-page + (thw/with-wasm-mocks* + (fn [] + (let [m (setup)] + (doseq [[label thunk] (method-specs m)] + (t/is (throws? thunk) (str label " must be rejected on a non-active page"))))))) + +(t/deftest test-all-setters-allowed-on-active-page + ;; Sanity / value-validation anchor: every setter uses a valid value, so when + ;; page2 is active none of them must throw. This guarantees the negative test's + ;; throws come from the page guard and not from value validation. + (thw/with-wasm-mocks* + (fn [] + (let [{:keys [store page2-id] :as m} (setup)] + (activate-page! store page2-id) + (doseq [[label thunk] (setter-specs m)] + (t/is (not (throws? thunk)) (str label " must be allowed on the active page"))))))) + diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index da17eeed40..a46a20f85e 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -26,6 +26,7 @@ [frontend-tests.logic.pasting-in-containers-test] [frontend-tests.main-errors-test] [frontend-tests.plugins.context-shapes-test] + [frontend-tests.plugins.page-active-validation-test] [frontend-tests.plugins.page-test] [frontend-tests.plugins.parser-test] [frontend-tests.plugins.tokens-test] @@ -77,6 +78,7 @@ frontend-tests.logic.groups-test frontend-tests.logic.pasting-in-containers-test frontend-tests.plugins.context-shapes-test + frontend-tests.plugins.page-active-validation-test frontend-tests.plugins.page-test frontend-tests.plugins.parser-test frontend-tests.plugins.tokens-test diff --git a/plugins/CHANGELOG.md b/plugins/CHANGELOG.md index c9c48444c6..0fba42fa05 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -1,5 +1,7 @@ ## 1.5.0 (Unreleased) +- **plugins-runtime**: changes outside the current page now raise a validation error when the target belongs to a page that is not currently active, instead of silently operating on the active page. +- **plugins-runtime**: Fix inverted validation that rejected valid values (and accepted invalid ones) on text range `align`, `direction`, `textDecoration`, `letterSpacing` and on layout child `zIndex`. - **plugins-runtime**: Added `version` field that returns the current version - **plugins-runtime**: Added optional parameter `throwOnError` to `penpot.ui.sendMessage` (default false, backwards-compatible) - **plugin-types**: Added a flags subcontexts with the flag `naturalChildrenOrdering` diff --git a/plugins/libs/plugin-types/index.d.ts b/plugins/libs/plugin-types/index.d.ts index c283c6f808..4b082cb506 100644 --- a/plugins/libs/plugin-types/index.d.ts +++ b/plugins/libs/plugin-types/index.d.ts @@ -1263,12 +1263,30 @@ export interface Context { openViewer(): void; /** - * Creates a new page. Requires `content:write` permission. + * Creates a new page and returns it. Requires `content:write` permission. + * + * IMPORTANT: creating a page does **not** make it the active page. + * To build content inside the new page, activate it first with + * {@link Context.openPage} (and `await` it) before mutate shapes: + * + * @example + * ```js + * const page = penpot.createPage(); + * page.name = 'New Page'; + * await penpot.openPage(page); // make the new page active first + * const board = penpot.createBoard(); + * board.resize(375, 812); + * page.root.appendChild(board); + * ``` */ createPage(): Page; /** - * Changes the current open page to given page. Requires `content:read` permission. + * Changes the current open page to the given page, making it the **active page**. + * The active page is the one all shape creation and structural operations + * (`createBoard`, `appendChild`, `insertChild`, property setters, etc.) act upon, + * so call this (and `await` it) after {@link Context.createPage} before adding + * shapes to the newly created page. Requires `content:read` permission. * @param page the page to open (a Page object or a page UUID string) * @param newWindow if true opens the page in a new window, defaults to false *