mirror of
https://github.com/penpot/penpot.git
synced 2026-09-19 02:16:14 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
df383be6b2
@ -19,6 +19,9 @@ right flow.
|
||||
- **Create from draft body** — Taiga story, user report, discussion; no PR
|
||||
yet.
|
||||
→ memory section **Creating Issues from Draft Body**
|
||||
- **Create as sub-issue** — the issue must be grouped under an umbrella/EPIC
|
||||
issue; create it first, then link it to its parent.
|
||||
→ memory section **Adding an Issue as a Sub-issue**
|
||||
- **Retitle existing issue** — current title is vague, prefixed, or stale.
|
||||
→ memory section **Retitling an Existing Issue**
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ Frontend: CLJS SPA; React/Rumext; Potok; RxJS; okulary refs; SCSS modules; share
|
||||
- `app.util.*`: DOM, HTTP, i18n, keyboard, codegen, and general frontend utilities.
|
||||
- `frontend/packages/*` and `frontend/text-editor`: JS/TS workspace packages consumed by the app.
|
||||
- Nitrate subscription/organization UI and flows live under `app.main.data.nitrate` and `app.main.ui.nitrate*`; backend/API behavior is covered by backend memories, and shared permission rules are in `common/src/app/common/types/nitrate_permissions.cljc`.
|
||||
- `app.config` external bridges (`external-feature-flag`, `external-session-id`, `external-context-info`, `external-notify-register-success`, `initialize-external-context-info`) delegate to `globalThis` hooks injected by the SaaS host. Never delete `external-feature-flag`, even with zero call sites: it is the seam for future A/B tests.
|
||||
|
||||
|
||||
## Lint and Format
|
||||
|
||||
@ -159,6 +159,47 @@ query { repository(owner: "penpot", name: "penpot") {
|
||||
rm -f /tmp/issue-body.md
|
||||
```
|
||||
|
||||
## Adding an Issue as a Sub-issue
|
||||
|
||||
Sub-issues group work under an umbrella/EPIC issue. `gh issue create` cannot
|
||||
link a sub-issue at creation time: create the issue first (normal flow above),
|
||||
then link it.
|
||||
|
||||
**1. Create the sub-issue** as usual and note its number (`NNNN`).
|
||||
|
||||
**2. Get the issue's database id** (the REST `id`, not the `number`):
|
||||
|
||||
```bash
|
||||
SUB_ID=$(gh api repos/penpot/penpot/issues/NNNN --jq .id)
|
||||
```
|
||||
|
||||
**3. Link it to the parent** (`PARENT` = umbrella/EPIC issue number):
|
||||
|
||||
```bash
|
||||
gh api --method POST repos/penpot/penpot/issues/PARENT/sub_issues \
|
||||
-F sub_issue_id=$SUB_ID
|
||||
```
|
||||
|
||||
Use `-F` (typed field), never `-f`: with `-f` the value is sent as a string
|
||||
and the API rejects it with `422 ... /sub_issue_id ... is not of type integer`.
|
||||
|
||||
**4. Verify both directions:**
|
||||
|
||||
```bash
|
||||
gh api repos/penpot/penpot/issues/NNNN/parent --jq '{number, title}'
|
||||
gh api repos/penpot/penpot/issues/PARENT/sub_issues --jq '.[] | {number, title}'
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The `POST` response is the parent issue and includes `sub_issues_summary`
|
||||
with `total`, `completed` and `percent_completed`, useful to track EPIC
|
||||
progress.
|
||||
- A sub-issue has a single parent.
|
||||
- Issue Type is independent of the parent relationship: choose it with the
|
||||
normal mapping above (an EPIC child that fixes broken behavior is a Bug,
|
||||
not a Task).
|
||||
|
||||
## Creating Issues from PRs
|
||||
|
||||
Used when the project board needs an issue as the primary changelog/release
|
||||
|
||||
11
CHANGES.md
11
CHANGES.md
@ -134,6 +134,7 @@
|
||||
- Normalize string inputs before processing [#11060](https://github.com/penpot/penpot/issues/11060) (PR: [#11061](https://github.com/penpot/penpot/pull/11061))
|
||||
- Add cooldown to avoid sending duplicate invitation emails [#11062](https://github.com/penpot/penpot/issues/11062) (PR: [#11063](https://github.com/penpot/penpot/pull/11063))
|
||||
- Enable SSRF protection for organization SSO validation [#11064](https://github.com/penpot/penpot/issues/11064) (PR: [#11065](https://github.com/penpot/penpot/pull/11065))
|
||||
- Fix LDAP authentication storing client-supplied email instead of directory email and not escaping filter special characters [#11084](https://github.com/penpot/penpot/issues/11084) (PR: [#11085](https://github.com/penpot/penpot/pull/11085))
|
||||
- Fix clone-file-media-object allowing to clone media objects from files without read access [#11087](https://github.com/penpot/penpot/issues/11087) (PR: [#11090](https://github.com/penpot/penpot/pull/11090))
|
||||
- Fix 404 error page logo not visible in dark mode [#11091](https://github.com/penpot/penpot/issues/11091) (PR: [#11167](https://github.com/penpot/penpot/pull/11167))
|
||||
- Fix incorrect permission handling when creating an invitation [#11098](https://github.com/penpot/penpot/issues/11098) (PR: [#11099](https://github.com/penpot/penpot/pull/11099))
|
||||
@ -158,19 +159,27 @@
|
||||
- Fix get-team-invitation-token requiring only read permissions [#11358](https://github.com/penpot/penpot/issues/11358) (PR: [#11359](https://github.com/penpot/penpot/pull/11359))
|
||||
- Fix missing text in legacy SVG board thumbnails [#10182](https://github.com/penpot/penpot/issues/10182) (PR: [#11552](https://github.com/penpot/penpot/pull/11552))
|
||||
- Fix workspace crash when applying transform modifiers in the WASM renderer [#10894](https://github.com/penpot/penpot/issues/10894) (PR: [#10896](https://github.com/penpot/penpot/pull/10896))
|
||||
- Fix double-click on text bounding box entering edit mode instead of applying auto-width/auto-height with the WebGL renderer [#10934](https://github.com/penpot/penpot/issues/10934) (PR: [#11068](https://github.com/penpot/penpot/pull/11068))
|
||||
- Limit ZIP entry count and object size on V3 binfile import [#11021](https://github.com/penpot/penpot/issues/11021) (PR: [#11022](https://github.com/penpot/penpot/pull/11022))
|
||||
- Block plugin UI iframe URLs targeting the Penpot domain [#11271](https://github.com/penpot/penpot/issues/11271) (PR: [#11273](https://github.com/penpot/penpot/pull/11273))
|
||||
- Restrict the MCP REPL code execution endpoint to development environments [#11283](https://github.com/penpot/penpot/issues/11283) (PR: [#11282](https://github.com/penpot/penpot/pull/11282))
|
||||
- Filter share-link tokens from the get-view-only-bundle response [#11285](https://github.com/penpot/penpot/issues/11285) (PR: [#11286](https://github.com/penpot/penpot/pull/11286))
|
||||
- Fix HTTP error responses disclosing internal database and filesystem paths to API clients [#11287](https://github.com/penpot/penpot/issues/11287) (PR: [#11288](https://github.com/penpot/penpot/pull/11288))
|
||||
- Disable MCP developer tools in multi-user mode [#11291](https://github.com/penpot/penpot/issues/11291) (PR: [#11310](https://github.com/penpot/penpot/pull/11310))
|
||||
- Fix Hide comments setting being ignored after opening the Comments section [#11308](https://github.com/penpot/penpot/issues/11308) (PR: [#11492](https://github.com/penpot/penpot/pull/11492))
|
||||
- Block NAT64/6to4/Teredo IPv6 transition addresses in the SSRF guard [#11319](https://github.com/penpot/penpot/issues/11319) (PR: [#11320](https://github.com/penpot/penpot/pull/11320))
|
||||
- Prevent team admins from removing the team owner [#11367](https://github.com/penpot/penpot/issues/11367) (PR: [#11368](https://github.com/penpot/penpot/pull/11368))
|
||||
- Enforce share-link comment permissions and page scope [#11370](https://github.com/penpot/penpot/issues/11370) (PR: [#11371](https://github.com/penpot/penpot/pull/11371))
|
||||
- Clean up orphaned teams, projects and files on profile deletion [#11394](https://github.com/penpot/penpot/issues/11394) (PR: [#11395](https://github.com/penpot/penpot/pull/11395))
|
||||
- Use constant-time comparison for Management API shared key authentication [#11426](https://github.com/penpot/penpot/issues/11426) (PR: [#11534](https://github.com/penpot/penpot/pull/11534))
|
||||
- Fix crash when pressing Ctrl+D with no shape selected [#11448](https://github.com/penpot/penpot/issues/11448) (PR: [#11491](https://github.com/penpot/penpot/pull/11491))
|
||||
- Fix text layout not updating when auto-width is set by double-clicking the bounding box [#11480](https://github.com/penpot/penpot/issues/11480) (PR: [#11541](https://github.com/penpot/penpot/pull/11541))
|
||||
- Fix boolean shapes rendering deformed in the WASM renderer and exports [#11482](https://github.com/penpot/penpot/issues/11482) (PR: [#11551](https://github.com/penpot/penpot/pull/11551))
|
||||
- Fix workspace crash with index out of bounds when removing multiple interactions rapidly [#11546](https://github.com/penpot/penpot/issues/11546) (PR: [#11621](https://github.com/penpot/penpot/pull/11621))
|
||||
- Fix workspace crash with 'too much recursion' error when editing grid layout [#11562](https://github.com/penpot/penpot/issues/11562) (PR: [#11563](https://github.com/penpot/penpot/pull/11563))
|
||||
- Fix plugin API error when removing interactions [#11618](https://github.com/penpot/penpot/issues/11618) (PR: [#11621](https://github.com/penpot/penpot/pull/11621))
|
||||
- Fix chunked upload storing duplicate objects when the same chunk index is sent twice [#11634](https://github.com/penpot/penpot/issues/11634) (PR: [#11635](https://github.com/penpot/penpot/pull/11635))
|
||||
- Fix access token expiration select always showing Never after picking another option (by @chulgil) [#11663](https://github.com/penpot/penpot/issues/11663) (PR: [#11664](https://github.com/penpot/penpot/pull/11664))
|
||||
- Fix workspace dropping unsaved changes when read-only blocks persistence [#11672](https://github.com/penpot/penpot/issues/11672) (PR: [#11699](https://github.com/penpot/penpot/pull/11699))
|
||||
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
|
||||
@ -33,11 +33,9 @@
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
[app.setup :as-alias setup]
|
||||
[app.setup.welcome-file :refer [create-welcome-file]]
|
||||
[app.storage :as sto]
|
||||
[app.tokens :as tokens]
|
||||
[app.util.services :as sv]
|
||||
[app.worker :as wrk]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def schema:password
|
||||
@ -308,7 +306,6 @@
|
||||
[:fullname ::sm/text]
|
||||
[:email ::sm/email]
|
||||
[:password schema:password]
|
||||
[:create-welcome-file {:optional true} :boolean]
|
||||
[:accept-newsletter-updates {:optional true} :boolean]
|
||||
[:invitation-token {:optional true} schema:token]])
|
||||
|
||||
@ -446,7 +443,7 @@
|
||||
:extra-data ptoken}))))
|
||||
|
||||
(defn register-profile
|
||||
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}]
|
||||
[{:keys [::db/conn] :as cfg} {:keys [token] :as params}]
|
||||
(let [claims (tokens/verify cfg {:token token :iss :prepared-register})
|
||||
params (cond-> claims
|
||||
(:accept-newsletter-updates params)
|
||||
@ -469,14 +466,7 @@
|
||||
(tokens/verify cfg {:token token :iss :team-invitation}))
|
||||
|
||||
props (-> (audit/profile->props profile)
|
||||
(assoc :from-invitation (some? invitation)))
|
||||
|
||||
|
||||
create-welcome-file-when-needed
|
||||
(fn []
|
||||
(when (:create-welcome-file params)
|
||||
(let [cfg (dissoc cfg ::db/conn)]
|
||||
(wrk/submit! executor (create-welcome-file cfg profile)))))]
|
||||
(assoc :from-invitation (some? invitation)))]
|
||||
|
||||
(cond
|
||||
;; When profile is blocked, we just ignore it and return plain data
|
||||
@ -525,7 +515,6 @@
|
||||
:email (:email profile)
|
||||
:invitation-token token}
|
||||
(rph/with-transform (session/create-fn cfg profile claims))
|
||||
(rph/with-defer create-welcome-file-when-needed)
|
||||
(rph/with-meta {::audit/replace-props props
|
||||
::audit/context {:action "accept-invitation"}
|
||||
::audit/profile-id (:id profile)})))
|
||||
@ -533,7 +522,6 @@
|
||||
(:is-active profile)
|
||||
(-> (profile/strip-private-attrs profile)
|
||||
(rph/with-transform (session/create-fn cfg profile claims))
|
||||
(rph/with-defer create-welcome-file-when-needed)
|
||||
(rph/with-meta
|
||||
{::audit/replace-props props
|
||||
::audit/context {:action "login"}
|
||||
@ -548,7 +536,6 @@
|
||||
|
||||
(-> {:id (:id profile)
|
||||
:email (:email profile)}
|
||||
(rph/with-defer create-welcome-file-when-needed)
|
||||
(rph/with-meta
|
||||
{::audit/replace-props props
|
||||
::audit/context {:action "email-verification"}
|
||||
|
||||
@ -69,7 +69,6 @@
|
||||
[:onboarding-questions-answered {:optional true} ::sm/boolean]
|
||||
[:nitrate-onboarding-viewed {:optional true} ::sm/boolean]
|
||||
[:v2-info-shown {:optional true} ::sm/boolean]
|
||||
[:welcome-file-id {:optional true} [:maybe ::sm/boolean]]
|
||||
[:release-notes-viewed {:optional true}
|
||||
[::sm/text {:max 100}]]
|
||||
[:notifications {:optional true} schema:props-notifications]
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
;; 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 SUBSIDIARY SL
|
||||
|
||||
(ns app.setup.welcome-file
|
||||
(:require
|
||||
[app.common.logging :as l]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as-alias climit]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.files-update :as fupdate]
|
||||
[app.rpc.commands.management :as management]
|
||||
[app.rpc.commands.profile :as profile]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.setup :as-alias setup]
|
||||
[app.setup.templates :as tmpl]
|
||||
[app.worker :as-alias wrk]))
|
||||
|
||||
(def ^:private page-id #uuid "2c6952ee-d00e-8160-8004-d2250b7210cb")
|
||||
(def ^:private shape-id #uuid "765e9f82-c44e-802e-8004-d72a10b7b445")
|
||||
|
||||
(def ^:private update-path
|
||||
[:data :pages-index page-id :objects shape-id
|
||||
:content :children 0 :children 0 :children 0])
|
||||
|
||||
(def ^:private sql:mark-file-object-thumbnails-deleted
|
||||
"UPDATE file_tagged_object_thumbnail
|
||||
SET deleted_at = now()
|
||||
WHERE file_id = ?")
|
||||
|
||||
(def ^:private sql:mark-file-thumbnail-deleted
|
||||
"UPDATE file_thumbnail
|
||||
SET deleted_at = now()
|
||||
WHERE file_id = ?")
|
||||
|
||||
(defn- update-welcome-shape
|
||||
[_ file name]
|
||||
(let [text (str "Welcome to Penpot, " name "!")]
|
||||
(-> file
|
||||
(update-in update-path assoc :text text)
|
||||
(update-in [:data :pages-index page-id :objects shape-id] assoc :name "Welcome to Penpot!")
|
||||
(update-in [:data :pages-index page-id :objects shape-id] dissoc :position-data))))
|
||||
|
||||
(defn create-welcome-file
|
||||
[cfg {:keys [id fullname] :as profile}]
|
||||
(try
|
||||
(let [cfg (dissoc cfg ::db/conn)
|
||||
params {:profile-id (:id profile)
|
||||
:project-id (:default-project-id profile)}
|
||||
template-stream (tmpl/get-template-stream cfg "welcome")
|
||||
file-id (-> (management/clone-template cfg params template-stream)
|
||||
first)
|
||||
file-name (str fullname "'s first file")]
|
||||
|
||||
(db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
|
||||
(files/rename-file conn {:id file-id :name file-name})
|
||||
(fupdate/update-file! cfg file-id update-welcome-shape fullname)
|
||||
(profile/update-profile-props cfg id {:welcome-file-id file-id})
|
||||
(db/exec-one! conn [sql:mark-file-object-thumbnails-deleted file-id])
|
||||
(db/exec-one! conn [sql:mark-file-thumbnail-deleted file-id]))))
|
||||
|
||||
(catch Throwable cause
|
||||
(l/error :hint "unexpected error on create welcome file " :cause cause))))
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
"This is the main event that is executed once we have logged in
|
||||
profile. The profile can proceed from standard login or from
|
||||
accepting invitation, or third party auth signup or singin."
|
||||
[{:keys [props] :as profile}]
|
||||
[profile]
|
||||
(letfn [(get-redirect-events [teams]
|
||||
(if-let [token (:invitation-token profile)]
|
||||
(rx/of (rt/nav :auth-verify-token {:token token}))
|
||||
@ -47,22 +47,16 @@
|
||||
(if (= redirect-href (rt/get-current-href))
|
||||
(rx/of (rt/reload true))
|
||||
(rx/of (rt/nav-raw :href redirect-href))))
|
||||
(if-let [file-id (get props :welcome-file-id)]
|
||||
(rx/of (dcm/go-to-workspace
|
||||
:file-id file-id
|
||||
:team-id (:default-team-id profile))
|
||||
(dp/update-profile-props {:welcome-file-id nil}))
|
||||
|
||||
(let [default-team-id (:default-team-id profile)
|
||||
team-ids (into #{} (map :id) teams)
|
||||
team-id (dtm/get-last-team-id)
|
||||
team-id (if (and team-id (contains? team-ids team-id))
|
||||
team-id
|
||||
default-team-id)]
|
||||
(->> (dtm/resolve-login-team-id {:team-id team-id
|
||||
:default-team-id default-team-id})
|
||||
(rx/mapcat (fn [team-id]
|
||||
(rx/of (dcm/go-to-dashboard-recent {:team-id team-id}))))))))))]
|
||||
(let [default-team-id (:default-team-id profile)
|
||||
team-ids (into #{} (map :id) teams)
|
||||
team-id (dtm/get-last-team-id)
|
||||
team-id (if (and team-id (contains? team-ids team-id))
|
||||
team-id
|
||||
default-team-id)]
|
||||
(->> (dtm/resolve-login-team-id {:team-id team-id
|
||||
:default-team-id default-team-id})
|
||||
(rx/mapcat (fn [team-id]
|
||||
(rx/of (dcm/go-to-dashboard-recent {:team-id team-id})))))))))]
|
||||
|
||||
(ptk/reify ::logged-in
|
||||
ptk/WatchEvent
|
||||
|
||||
@ -190,24 +190,12 @@
|
||||
file-id (some-> params :file-id uuid/parse*)
|
||||
page-id (some-> params :page-id uuid/parse*)
|
||||
layout (some-> params :layout keyword)]
|
||||
[:? {}
|
||||
(when (cf/external-feature-flag "onboarding-03" "test")
|
||||
(cond
|
||||
show-question-modal?
|
||||
[:& questions-modal]
|
||||
|
||||
show-team-modal?
|
||||
[:> onboarding-team-modal* {:go-to-team false}]
|
||||
|
||||
show-release-modal?
|
||||
[:& release-notes-modal {:version (:main cf/version)}]))
|
||||
|
||||
[:> team-container* {:team-id team-id}
|
||||
[:> workspace-page* {:team-id team-id
|
||||
:file-id file-id
|
||||
:page-id page-id
|
||||
:layout-name layout
|
||||
:key file-id}]]])
|
||||
[:> team-container* {:team-id team-id}
|
||||
[:> workspace-page* {:team-id team-id
|
||||
:file-id file-id
|
||||
:page-id page-id
|
||||
:layout-name layout
|
||||
:key file-id}]])
|
||||
|
||||
:viewer
|
||||
(let [params (get params :query)
|
||||
|
||||
@ -160,14 +160,7 @@
|
||||
(mf/deps on-success-callback)
|
||||
(fn [form _event]
|
||||
(reset! submitted? true)
|
||||
(let [create-welcome-file?
|
||||
(cf/external-feature-flag "onboarding-03" "test")
|
||||
|
||||
cdata
|
||||
(cond-> (:clean-data @form)
|
||||
create-welcome-file?
|
||||
(assoc :create-welcome-file true))]
|
||||
|
||||
(let [cdata (:clean-data @form)]
|
||||
(->> (rp/cmd! :prepare-register-profile cdata)
|
||||
(rx/subs! on-register-profile on-error #(reset! submitted? false))))))]
|
||||
|
||||
@ -326,13 +319,7 @@
|
||||
(mf/deps on-success on-error)
|
||||
(fn [form _]
|
||||
(reset! submitted? true)
|
||||
(let [create-welcome-file?
|
||||
(cf/external-feature-flag "onboarding-03" "test")
|
||||
|
||||
params
|
||||
(cond-> (:clean-data @form)
|
||||
create-welcome-file? (assoc :create-welcome-file true))]
|
||||
|
||||
(let [params (:clean-data @form)]
|
||||
(->> (rp/cmd! :register-profile params)
|
||||
(rx/finalize #(reset! submitted? false))
|
||||
(rx/subs! on-success on-error)))))]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user