diff --git a/backend/resources/app/email/invite-to-org/en.html b/backend/resources/app/email/invite-to-organization/en.html similarity index 100% rename from backend/resources/app/email/invite-to-org/en.html rename to backend/resources/app/email/invite-to-organization/en.html diff --git a/backend/resources/app/email/invite-to-org/en.subj b/backend/resources/app/email/invite-to-organization/en.subj similarity index 100% rename from backend/resources/app/email/invite-to-org/en.subj rename to backend/resources/app/email/invite-to-organization/en.subj diff --git a/backend/resources/app/email/invite-to-org/en.txt b/backend/resources/app/email/invite-to-organization/en.txt similarity index 100% rename from backend/resources/app/email/invite-to-org/en.txt rename to backend/resources/app/email/invite-to-organization/en.txt diff --git a/backend/src/app/auth/oidc.clj b/backend/src/app/auth/oidc.clj index d935ac26ff..09d34532b9 100644 --- a/backend/src/app/auth/oidc.clj +++ b/backend/src/app/auth/oidc.clj @@ -769,13 +769,13 @@ [value] (when-not (str/blank? value) value)) -(defn org-sso-discovery-uri - "Return the OIDC discovery URI from an org SSO config." +(defn organization-sso-discovery-uri + "Return the OIDC discovery URI from an organization SSO config." [sso] (non-blank-uri (:issuer sso))) -(defn prepare-org-sso-provider - "Build an OIDC provider map dynamically from the Nitrate org SSO config. +(defn prepare-organization-sso-provider + "Build an OIDC provider map dynamically from the Nitrate organization SSO config. Uses OIDC discovery via :issuer when token/auth/user URIs are absent." [cfg {:keys [client-id client-secret issuer]}] (prepare-oidc-provider cfg @@ -788,18 +788,18 @@ :scopes default-oidc-scopes :skip-ssrf-check? true})) -(defn build-org-sso-auth-redirect-uri +(defn build-organization-sso-auth-redirect-uri "Build the OIDC authorization redirect URI for an organization SSO config. Raises if the config is incomplete or OIDC discovery fails." [cfg sso & {:keys [dest-url organization-id provider]}] (let [organization-id (or organization-id (:organization-id sso)) - issuer (org-sso-discovery-uri sso) + issuer (organization-sso-discovery-uri sso) dest-url (or dest-url (str (cf/get :public-uri)))] (when-not issuer (ex/raise :type :validation :code :invalid-sso-config :hint "missing issuer")) - (let [oidc-provider (or provider (prepare-org-sso-provider cfg sso)) + (let [oidc-provider (or provider (prepare-organization-sso-provider cfg sso)) state-token (tokens/generate cfg {:iss "oidc" :dest-url dest-url :organization-id organization-id @@ -839,7 +839,7 @@ (and (= error "access_denied") (str/includes? description "unauthorized"))))) -(defn- probe-org-sso-client-credentials +(defn- probe-organization-sso-client-credentials "Probe the token endpoint with a dummy authorization code. Valid client credentials are expected to answer with `invalid_grant`." [cfg provider] @@ -864,10 +864,10 @@ and the client credentials are accepted by the token endpoint." [cfg sso] (try - (if (org-sso-discovery-uri sso) - (let [provider (prepare-org-sso-provider cfg sso)] - (and (build-org-sso-auth-redirect-uri cfg sso :provider provider) - (probe-org-sso-client-credentials cfg provider))) + (if (organization-sso-discovery-uri sso) + (let [provider (prepare-organization-sso-provider cfg sso)] + (and (build-organization-sso-auth-redirect-uri cfg sso :provider provider) + (probe-organization-sso-client-credentials cfg provider))) false) (catch Throwable _ false))) @@ -897,12 +897,12 @@ state (get params :state) state (tokens/verify cfg {:token state :iss "oidc"})] - ;; Org SSO flow: state carries :dest-url — exchange the authorization + ;; Organization SSO flow: state carries :dest-url — exchange the authorization ;; code with the OIDC provider to verify authentication actually occurred. (if-let [dest-url (:dest-url state)] (let [organization-id (:organization-id state) - sso (nitrate/call cfg :get-org-sso {:organization-id organization-id}) - provider (prepare-org-sso-provider cfg sso) + sso (nitrate/call cfg :get-organization-sso {:organization-id organization-id}) + provider (prepare-organization-sso-provider cfg sso) info (get-info cfg provider state code) session (session/get-session request) exp (or (:sso-token-exp info) (ct/in-future {:hours 48}))] diff --git a/backend/src/app/email.clj b/backend/src/app/email.clj index 3aad2cd852..e069b2908b 100644 --- a/backend/src/app/email.clj +++ b/backend/src/app/email.clj @@ -440,18 +440,18 @@ :id ::invite-to-team :schema schema:invite-to-team)) -(def ^:private schema:invite-to-org +(def ^:private schema:invite-to-organization [:map [:invited-by ::sm/text] [:user-name [:maybe ::sm/text]] [:token ::sm/text] [:organization schema:organization-data]]) -(def invite-to-org - "Org member invitation email." +(def invite-to-organization + "Organization member invitation email." (template-factory - :id ::invite-to-org - :schema schema:invite-to-org)) + :id ::invite-to-organization + :schema schema:invite-to-organization)) (def ^:private schema:organization-setup-sso [:map diff --git a/backend/src/app/http/session.clj b/backend/src/app/http/session.clj index e9f4b5679a..614942c072 100644 --- a/backend/src/app/http/session.clj +++ b/backend/src/app/http/session.clj @@ -226,19 +226,19 @@ (-> (db/exec-one! cfg [sql (:profile-id session) (:id session)]) (db/get-update-count)))) -(def ^:private sql:clear-org-sso-sessions +(def ^:private sql:clear-organization-sso-sessions (str "UPDATE http_session_v2 " "SET props = props #- ARRAY['~:sso', ?]::text[] " "WHERE props IS NOT NULL " "AND jsonb_exists(props -> '~:sso', ?)")) -(defn clear-org-sso-sessions! +(defn clear-organization-sso-sessions! "Remove the SSO entry for organization-id from the props of every session that currently holds it. The key is transit-encoded as the string '~u' under the '~:sso' path." [pool organization-id] - (let [org-key (str "~u" organization-id)] - (db/exec! pool [sql:clear-org-sso-sessions org-key org-key]))) + (let [organization-key (str "~u" organization-id)] + (db/exec! pool [sql:clear-organization-sso-sessions organization-key organization-key]))) (defn- renew-session? [{:keys [id modified-at] :as session}] diff --git a/backend/src/app/nitrate.clj b/backend/src/app/nitrate.clj index 78464da18f..70a072b7c0 100644 --- a/backend/src/app/nitrate.clj +++ b/backend/src/app/nitrate.clj @@ -162,7 +162,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(def ^:private schema:org-summary +(def ^:private schema:organization-summary [:map [:id ::sm/uuid] [:name ::sm/text] @@ -189,7 +189,7 @@ :decode/json ct/inst :encode/json inst-ms}})) -(def ^:private schema:profile-org +(def ^:private schema:profile-organization [:map [:is-member :boolean] [:organization-id {:optional true} [:maybe ::sm/uuid]] @@ -253,13 +253,13 @@ [:map [:licenses ::sm/boolean]]) -(defn- get-team-org-api +(defn- get-team-organization-api [cfg {:keys [team-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri "api/teams/" team-id) cto/schema:team-with-organization params)) -(defn- get-org-membership-api +(defn- get-organization-membership-api [cfg {:keys [profile-id organization-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri @@ -267,9 +267,9 @@ organization-id "members/" profile-id) - schema:profile-org params)) + schema:profile-organization params)) -(defn- get-org-membership-by-team-api +(defn- get-organization-membership-by-team-api [cfg {:keys [profile-id team-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri @@ -277,28 +277,28 @@ team-id "users/" profile-id) - schema:profile-org params)) + schema:profile-organization params)) -(defn- get-org-summary-api +(defn- get-organization-summary-api [cfg {:keys [organization-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" organization-id "summary") - schema:org-summary params)) + schema:organization-summary params)) -(defn- get-owned-orgs-api +(defn- get-owned-organizations-api [cfg {:keys [profile-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri "api/users/" profile-id "owned-organizations") - [:vector schema:org-summary] + [:vector schema:organization-summary] params)) -(def ^:private schema:org-summary-counts +(def ^:private schema:organization-summary-counts [:map [:id ::sm/uuid] [:name ::sm/text] @@ -308,20 +308,20 @@ [:avatar-bg-url {:optional true} [:maybe ::sm/uri]] [:logo-id {:optional true} [:maybe ::sm/uuid]]]) -(defn- get-owned-orgs-summary-api +(defn- get-owned-organizations-summary-api [cfg {:keys [profile-id] :as params}] - (let [orgs (request-to-nitrate cfg :get - (generate-nitrate-uri - "api/users/" - profile-id - "owned-organizations-summary") - [:vector schema:org-summary-counts] - params)] - (mapv (fn [org] - (if-let [logo-id (:logo-id org)] - (assoc org :custom-photo (generate-public-uri "assets/by-id/" logo-id)) - org)) - orgs))) + (let [organizations (request-to-nitrate cfg :get + (generate-nitrate-uri + "api/users/" + profile-id + "owned-organizations-summary") + [:vector schema:organization-summary-counts] + params)] + (mapv (fn [organization] + (if-let [logo-id (:logo-id organization)] + (assoc organization :custom-photo (generate-public-uri "assets/by-id/" logo-id)) + organization)) + organizations))) (defn- cleanup-deleted-penpot-user-api [cfg {:keys [profile-id] :as params}] @@ -332,7 +332,7 @@ "cleanup-after-deletion") nil params)) -(defn- set-team-org-api +(defn- set-team-organization-api [cfg {:keys [organization-id team-id is-default] :as params}] (let [params (assoc params :request-params {:team-id team-id :is-your-penpot (true? is-default)}) @@ -348,7 +348,7 @@ custom-photo (assoc-in [:organization :custom-photo] custom-photo)))) -(defn- add-profile-to-org-api +(defn- add-profile-to-organization-api [cfg {:keys [profile-id organization-id team-id email] :as params}] (let [request-params (cond-> {:user-id profile-id :team-id team-id} (some? email) (assoc :email email)) @@ -358,9 +358,9 @@ "api/organizations/" organization-id "add-user") - schema:profile-org params))) + schema:profile-organization params))) -(defn- remove-profile-from-org-api +(defn- remove-profile-from-organization-api [cfg {:keys [profile-id organization-id user-who-delete-member deleted-by-role] :as params}] (let [request-params (cond-> {:user-id profile-id} (some? user-who-delete-member) @@ -375,7 +375,7 @@ "remove-user") nil params))) -(defn- remove-team-from-org-api +(defn- remove-team-from-organization-api [cfg {:keys [team-id organization-id] :as params}] (let [params (assoc params :request-params {:team-id team-id})] (request-to-nitrate cfg :post @@ -433,7 +433,7 @@ [:map [:cancel-at [:maybe schema:timestamp]]]) -(defn- get-org-permissions-api +(defn- get-organization-permissions-api [cfg {:keys [organization-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri @@ -446,7 +446,7 @@ [:permissions [:map-of :keyword :string]]] params)) -(defn- get-org-sso-api +(defn- get-organization-sso-api "Fetches the SSO configuration for an organization from Nitrate." [cfg {:keys [organization-id] :as params}] (request-to-nitrate cfg :get @@ -457,14 +457,14 @@ schema:nitrate-sso params)) -(defn- get-org-sso-by-team-api +(defn- get-organization-sso-by-team-api [cfg {:keys [team-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri "api/teams/" team-id "sso") schema:nitrate-sso params)) -(defn- get-org-members-api +(defn- get-organization-members-api [cfg {:keys [organization-id] :as params}] (request-to-nitrate cfg :get (generate-nitrate-uri @@ -488,22 +488,22 @@ (defmethod ig/init-key ::client [_ cfg] (when (contains? cf/flags :nitrate) - {:get-team-org (partial get-team-org-api cfg) - :set-team-org (partial set-team-org-api cfg) - :get-org-membership (partial get-org-membership-api cfg) - :get-org-membership-by-team (partial get-org-membership-by-team-api cfg) - :get-org-summary (partial get-org-summary-api cfg) - :get-owned-orgs (partial get-owned-orgs-api cfg) - :get-owned-orgs-summary (partial get-owned-orgs-summary-api cfg) - :get-org-members (partial get-org-members-api cfg) + {:get-team-organization (partial get-team-organization-api cfg) + :set-team-organization (partial set-team-organization-api cfg) + :get-organization-membership (partial get-organization-membership-api cfg) + :get-organization-membership-by-team (partial get-organization-membership-by-team-api cfg) + :get-organization-summary (partial get-organization-summary-api cfg) + :get-owned-organizations (partial get-owned-organizations-api cfg) + :get-owned-organizations-summary (partial get-owned-organizations-summary-api cfg) + :get-organization-members (partial get-organization-members-api cfg) :cleanup-deleted-penpot-user (partial cleanup-deleted-penpot-user-api cfg) - :add-profile-to-org (partial add-profile-to-org-api cfg) - :remove-profile-from-org (partial remove-profile-from-org-api cfg) - :get-org-permissions (partial get-org-permissions-api cfg) - :get-org-sso-by-team (partial get-org-sso-by-team-api cfg) - :get-org-sso (partial get-org-sso-api cfg) + :add-profile-to-organization (partial add-profile-to-organization-api cfg) + :remove-profile-from-organization (partial remove-profile-from-organization-api cfg) + :get-organization-permissions (partial get-organization-permissions-api cfg) + :get-organization-sso-by-team (partial get-organization-sso-by-team-api cfg) + :get-organization-sso (partial get-organization-sso-api cfg) :delete-team (partial delete-team-api cfg) - :remove-team-from-org (partial remove-team-from-org-api cfg) + :remove-team-from-organization (partial remove-team-from-organization-api cfg) :get-subscription (partial get-subscription-api cfg) :get-subscription-warning (partial get-subscription-warning-api cfg) :connectivity (partial get-connectivity-api cfg) @@ -514,9 +514,9 @@ ;; UTILS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defonce ^:private team-org-owner-cache +(defonce ^:private team-organization-owner-cache ;; Short TTL: permission checks run on the read path, so we avoid an - ;; HTTP call to nitrate per check. The org owner of a team rarely + ;; HTTP call to nitrate per check. The organization owner of a team rarely ;; changes, and stale entries only grant read access for a few seconds. (cache/create :expire "30s" :max-size 2048)) @@ -526,24 +526,24 @@ [cfg] (and (map? cfg) (some? (get cfg ::client)))) -(def ^:private cache-miss ::no-org-owner) +(def ^:private cache-miss ::no-organization-owner) -(defn- get-team-org-owner-id +(defn- get-team-organization-owner-id "Returns the organization owner-id for `team-id`, or nil. Cached briefly, including negative results (teams with no organization) so repeated unauthorized probes don't each hit nitrate." [cfg team-id] - (let [owner-id (cache/get team-org-owner-cache team-id + (let [owner-id (cache/get team-organization-owner-cache team-id (fn [team-id] - (let [team-with-org (call cfg :get-team-org {:team-id team-id})] - (or (get-in team-with-org [:organization :owner-id]) + (let [team-with-organization (call cfg :get-team-organization {:team-id team-id})] + (or (get-in team-with-organization [:organization :owner-id]) cache-miss))))] (when-not (= owner-id cache-miss) owner-id))) (defn organization-owner-of-team? "True if `profile-id` is the owner of the organization that owns - `team-id`. Used to grant non-member org owners read-only access to the + `team-id`. Used to grant non-member organization owners read-only access to the teams of their organizations. `cfg` must be a config map with the nitrate client; raw db connections/pools yield false so internal callers are unaffected. Returns false when the :nitrate flag is off." @@ -553,17 +553,17 @@ (nitrate-client? cfg) (some? team-id) (some? profile-id)) - (= profile-id (get-team-org-owner-id cfg team-id))))) + (= profile-id (get-team-organization-owner-id cfg team-id))))) (defn sso-session-authorized? - "Fetches the org-SSO config for the given organization or team and checks + "Fetches the organization-SSO config for the given organization or team and checks whether the HTTP request has a valid session entry for it. Returns a map with :authorized and :sso keys." [cfg organization-id team-id request] (let [session (session/get-session request) sso (if organization-id - (call cfg :get-org-sso {:organization-id organization-id}) - (call cfg :get-org-sso-by-team {:team-id team-id}))] + (call cfg :get-organization-sso {:organization-id organization-id}) + (call cfg :get-organization-sso-by-team {:team-id team-id}))] (if-not (:active sso) {:authorized true :sso sso} (if-not (str/blank? (:issuer sso)) @@ -596,21 +596,21 @@ :cause cause) profile))))) -(defn add-org-info-to-team +(defn add-organization-info-to-team "Enriches a team map with organization information from Nitrate. Adds organization-id, organization-name, organization-slug, organization-owner-id, and your-penpot fields. - Returns the original team unchanged if the request fails or org data is nil. + Returns the original team unchanged if the request fails or organization data is nil. Propagates `:nitrate-unavailable` so the request is rejected when Nitrate is unreachable." [cfg team params] (try (let [params (assoc (or params {}) :team-id (:id team)) - team-with-org (call cfg :get-team-org params) - org (:organization team-with-org)] - (if (some? org) - (-> (cto/apply-organization team (assoc org :custom-photo - (when-let [logo-id (:logo-id org)] + team-with-organization (call cfg :get-team-organization params) + organization (:organization team-with-organization)] + (if (some? organization) + (-> (cto/apply-organization team (assoc organization :custom-photo + (when-let [logo-id (:logo-id organization)] (generate-public-uri "assets/by-id/" logo-id)))) - (assoc :is-default (or (:is-default team) (true? (:is-your-penpot team-with-org))))) + (assoc :is-default (or (:is-default team) (true? (:is-your-penpot team-with-organization))))) team)) (catch Throwable cause (if (= :nitrate-unavailable (-> cause ex-data :type)) @@ -630,10 +630,10 @@ :team-id (:id team) :organization-id (:organization-id params) :is-default (:is-default params)) - result (call cfg :set-team-org params)] + result (call cfg :set-team-organization params)] (when (nil? result) (ex/raise :type :internal - :code :failed-to-set-team-org + :code :failed-to-set-team-organization :context {:team-id (:id team) :organization-id (:organization-id params)})) team)) diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index 342b954123..3196b5c855 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -250,13 +250,13 @@ f)) -(defonce ^:private org-sso-auth-cache +(defonce ^:private organization-sso-auth-cache (cache/create :expire "15m" :max-size 1024)) -(defn invalidate-org-sso-cache-by-org! - "Invalidates all org-SSO authorization cache entries for the given organization-id." +(defn invalidate-organization-sso-cache-by-organization! + "Invalidates all organization-SSO authorization cache entries for the given organization-id." [organization-id] - (cache/invalidate-if org-sso-auth-cache #(= (:organization-id %) organization-id))) + (cache/invalidate-if organization-sso-auth-cache #(= (:organization-id %) organization-id))) (defn- wrap-nitrate-sso "Enforce Nitrate organization SSO authentication for RPC handlers. @@ -268,16 +268,16 @@ 4. Explicit :file-id param -> lookup file's team via join 5. :id param dispatched by ::rpc/id-type metadata (:team, :project, or :file) - Once the context is resolved, checks if the user is authorized within that org's + Once the context is resolved, checks if the user is authorized within that organization's SSO session using nitrate/sso-session-authorized?. Authorized results are cached by [profile-id cache-ref] for 15 minutes to avoid repeated lookups. Only activates when: - Nitrate flag is enabled - Endpoint requires authentication (::auth true by default) - - Endpoint is not marked with ::nitrate/org-sso false + - Endpoint is not marked with ::nitrate/organization-sso false - Raises :nitrate-sso-required error if user is not authorized in the org." + Raises :nitrate-sso-required error if user is not authorized in the organization." [_ f mdata] (if (and (contains? cf/flags :nitrate) (::auth mdata true) ;; only for endpoints that needs auth @@ -299,7 +299,7 @@ (let [cache-ref (or organization-id team-id project-id file-id) cache-key [profile-id cache-ref] - cached (cache/get org-sso-auth-cache cache-key) + cached (cache/get organization-sso-auth-cache cache-key) result (if (some? cached) cached (let [team-id (when-not organization-id @@ -314,7 +314,7 @@ entry {:authorized authorized :organization-id (:organization-id sso)}] (when authorized - (cache/get org-sso-auth-cache cache-key (constantly entry))) + (cache/get organization-sso-auth-cache cache-key (constantly entry))) entry))] (if (:authorized result) (f cfg params) diff --git a/backend/src/app/rpc/commands/nitrate.clj b/backend/src/app/rpc/commands/nitrate.clj index 02e44a404f..55b7a8e6d2 100644 --- a/backend/src/app/rpc/commands/nitrate.clj +++ b/backend/src/app/rpc/commands/nitrate.clj @@ -42,11 +42,11 @@ :code :cant-move-default-team)))) (defn assert-membership [cfg profile-id organization-id] - (let [membership (nitrate/call cfg :get-org-membership {:profile-id profile-id - :organization-id organization-id})] + (let [membership (nitrate/call cfg :get-organization-membership {:profile-id profile-id + :organization-id organization-id})] (when-not (:organization-id membership) (ex/raise :type :validation - :code :organization-doesnt-exists)) + :code :organization-does-not-exist)) (when-not (:is-member membership) (ex/raise :type :validation @@ -181,7 +181,7 @@ {}))) {})) -(defn- build-leave-org-plan +(defn- build-leave-organization-plan [{:keys [::db/conn]} default-team-id teams-to-delete keep-default-team-requested?] (let [all-teams (cond-> (set teams-to-delete) default-team-id (conj default-team-id)) files-counts (get-team-files-counts conn all-teams) @@ -194,18 +194,18 @@ {:deletable-team-ids deletable :keep-default-team? keep-default? :delete-default-team? (boolean (and default-team-id (not keep-default?))) - :detach-from-org-team-ids to-detach})) + :detach-from-organization-team-ids to-detach})) -(defn get-leave-org-summary +(defn get-leave-organization-summary [cfg default-team-id teams-to-delete teams-to-transfer-count teams-to-exit-count] - (let [{:keys [deletable-team-ids detach-from-org-team-ids]} - (build-leave-org-plan cfg default-team-id teams-to-delete nil)] + (let [{:keys [deletable-team-ids detach-from-organization-team-ids]} + (build-leave-organization-plan cfg default-team-id teams-to-delete nil)] {:teams-to-delete (count deletable-team-ids) :teams-to-transfer teams-to-transfer-count :teams-to-exit teams-to-exit-count - :teams-to-detach (count detach-from-org-team-ids)})) + :teams-to-detach (count detach-from-organization-team-ids)})) -(def ^:private schema:leave-org +(def ^:private schema:leave-organization [:map [:id ::sm/uuid] [:name ::sm/text] @@ -218,7 +218,7 @@ [:id ::sm/uuid] [:reassign-to {:optional true} ::sm/uuid]]]]]) -(def ^:private schema:get-leave-org-summary-result +(def ^:private schema:get-leave-organization-summary-result [:map [:teams-to-delete ::sm/int] [:teams-to-transfer ::sm/int] @@ -227,40 +227,40 @@ [:member-added-at [:maybe ct/schema:inst]] [:organization-member-count-before ::sm/int]]) -(def ^:private schema:get-leave-org-summary +(def ^:private schema:get-leave-organization-summary [:map [:id ::sm/uuid] [:default-team-id ::sm/uuid]]) (defn- get-organization-teams-for-user - [{:keys [::db/conn] :as cfg} org-summary profile-id] - (let [org-team-ids (->> (:teams org-summary) - (map :id)) - ids-array (db/create-array conn "uuid" org-team-ids)] + [{:keys [::db/conn] :as cfg} organization-summary profile-id] + (let [organization-team-ids (->> (:teams organization-summary) + (map :id)) + ids-array (db/create-array conn "uuid" organization-team-ids)] (db/exec! conn [sql:get-member-teams-info profile-id ids-array]))) (defn- calculate-valid-teams - ([org-teams default-team-id] + ([organization-teams default-team-id] (let [;; valid default team is the one which id is default-team-id - valid-default-team (d/seek #(= default-team-id (:id %)) org-teams) + valid-default-team (d/seek #(= default-team-id (:id %)) organization-teams) ;; Remove your-penpot for the rest of validations - org-teams (remove #(= default-team-id (:id %)) org-teams) + organization-teams (remove #(= default-team-id (:id %)) organization-teams) ;; valid teams to delete are those that the user is owner, and only have one member - valid-teams-to-delete-ids (->> org-teams + valid-teams-to-delete-ids (->> organization-teams (filter #(and (:is-owner %) (= (:num-members %) 1))) (map :id) (into #{})) ;; valid teams to transfer are those that the user is owner, and have more than one member - valid-teams-to-transfer (->> org-teams + valid-teams-to-transfer (->> organization-teams (filter #(and (:is-owner %) (> (:num-members %) 1)))) ;; valid teams to exit are those that the user isn't owner, and have more than one member - valid-teams-to-exit (->> org-teams + valid-teams-to-exit (->> organization-teams (filter #(and (not (:is-owner %)) (> (:num-members %) 1))))] {:valid-teams-to-delete-ids valid-teams-to-delete-ids @@ -269,17 +269,17 @@ :valid-default-team valid-default-team}))) (defn get-valid-teams [cfg organization-id profile-id default-team-id] - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id}) - org-teams (get-organization-teams-for-user cfg org-summary profile-id)] - (calculate-valid-teams org-teams default-team-id))) + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id}) + organization-teams (get-organization-teams-for-user cfg organization-summary profile-id)] + (calculate-valid-teams organization-teams default-team-id))) (defn- assert-valid-teams [cfg profile-id organization-id default-team-id teams-to-delete teams-to-leave] - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id}) - org-teams (get-organization-teams-for-user cfg org-summary profile-id) + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id}) + organization-teams (get-organization-teams-for-user cfg organization-summary profile-id) {:keys [valid-teams-to-delete-ids valid-teams-to-transfer valid-teams-to-exit - valid-default-team]} (calculate-valid-teams org-teams default-team-id) + valid-default-team]} (calculate-valid-teams organization-teams default-team-id) @@ -297,7 +297,7 @@ ;; - if it has a reassign-to, it belongs to valid-teams-to-transfer and ;; the reassign-to is a member of the team and not the current user; ;; - if it hasn't a reassign-to, check that it belongs to valid-teams-to-exit - teams-by-id (d/index-by :id org-teams) + teams-by-id (d/index-by :id organization-teams) valid-teams-to-leave? (and (= valid-teams-to-leave-ids (->> teams-to-leave (map :id) (into #{}))) (every? (fn [{:keys [id reassign-to]}] @@ -308,10 +308,10 @@ (contains? members reassign-to))) (contains? valid-teams-to-exit-ids id))) teams-to-leave))] - ;; the org owner cannot leave - (when (= (:owner-id org-summary) profile-id) + ;; the organization owner cannot leave + (when (= (:owner-id organization-summary) profile-id) (ex/raise :type :validation - :code :org-owner-cannot-leave)) + :code :organization-owner-cannot-leave)) (when (or (not valid-teams-to-delete?) @@ -322,14 +322,14 @@ -(defn leave-org +(defn leave-organization [{:keys [::db/conn] :as cfg} {:keys [profile-id id name default-team-id teams-to-delete teams-to-leave skip-validation keep-default-team-requested? user-who-delete-member deleted-by-role]}] - (let [org-prefix (str "[" (d/sanitize-string name) "] ") + (let [organization-prefix (str "[" (d/sanitize-string name) "] ") {:keys [deletable-team-ids keep-default-team? - detach-from-org-team-ids]} (build-leave-org-plan cfg default-team-id teams-to-delete keep-default-team-requested?)] + detach-from-organization-team-ids]} (build-leave-organization-plan cfg default-team-id teams-to-delete keep-default-team-requested?)] ;; assert that the received teams are valid, checking the different constraints (when-not skip-validation @@ -346,21 +346,21 @@ (doseq [{:keys [id reassign-to]} teams-to-leave] (teams/leave-team cfg {:profile-id profile-id :id id :reassign-to reassign-to})) - ;; Process org "Your Penpot" team: keep with prefix if needed, otherwise delete. + ;; Process organization "Your Penpot" team: keep with prefix if needed, otherwise delete. (when default-team-id (if keep-default-team? - (db/exec! conn [sql:prefix-team-name-and-unset-default org-prefix default-team-id]) + (db/exec! conn [sql:prefix-team-name-and-unset-default organization-prefix default-team-id]) (teams/delete-team cfg {:profile-id profile-id :team-id default-team-id}))) ;; Detach retained owned teams from the organization in Nitrate. - ;; Nitrate will rehome them to its fallback/default org. - (doseq [team-id detach-from-org-team-ids] - (nitrate/call cfg :remove-team-from-org {:team-id team-id - :organization-id id})) + ;; Nitrate will rehome them to its fallback/default organization. + (doseq [team-id detach-from-organization-team-ids] + (nitrate/call cfg :remove-team-from-organization {:team-id team-id + :organization-id id})) ;; Api call to nitrate - (nitrate/call cfg :remove-profile-from-org + (nitrate/call cfg :remove-profile-from-organization {:profile-id profile-id :organization-id id :user-who-delete-member user-who-delete-member @@ -369,33 +369,33 @@ nil)) -(sv/defmethod ::leave-org +(sv/defmethod ::leave-organization {::rpc/auth true ::doc/added "2.15" - ::sm/params schema:leave-org + ::sm/params schema:leave-organization ::db/transaction true} [cfg {:keys [::rpc/profile-id] :as params}] - (leave-org cfg (assoc params - :profile-id profile-id - :user-who-delete-member profile-id - :deleted-by-role "organization-member"))) + (leave-organization cfg (assoc params + :profile-id profile-id + :user-who-delete-member profile-id + :deleted-by-role "organization-member"))) -(sv/defmethod ::get-leave-org-summary +(sv/defmethod ::get-leave-organization-summary {::rpc/auth true ::doc/added "2.18" - ::sm/params schema:get-leave-org-summary - ::sm/result schema:get-leave-org-summary-result + ::sm/params schema:get-leave-organization-summary + ::sm/result schema:get-leave-organization-summary-result ::db/transaction true} [cfg {:keys [::rpc/profile-id id default-team-id]}] (let [{:keys [valid-teams-to-delete-ids valid-teams-to-transfer valid-teams-to-exit valid-default-team]} (get-valid-teams cfg id profile-id default-team-id) - membership (nitrate/call cfg :get-org-membership + membership (nitrate/call cfg :get-organization-membership {:profile-id profile-id :organization-id id}) - organization-members (nitrate/call cfg :get-org-members + organization-members (nitrate/call cfg :get-organization-members {:organization-id id}) teams-to-transfer-count (count valid-teams-to-transfer) teams-to-exit-count (count valid-teams-to-exit)] @@ -403,20 +403,20 @@ (ex/raise :type :validation :code :not-valid-teams)) (assoc - (get-leave-org-summary cfg default-team-id valid-teams-to-delete-ids teams-to-transfer-count teams-to-exit-count) + (get-leave-organization-summary cfg default-team-id valid-teams-to-delete-ids teams-to-transfer-count teams-to-exit-count) :member-added-at (:created-at membership) :organization-member-count-before (count organization-members)))) -(def ^:private schema:remove-team-from-org +(def ^:private schema:remove-team-from-organization [:map [:team-id ::sm/uuid] [:organization-id ::sm/uuid] [:organization-name ::sm/text]]) -(sv/defmethod ::remove-team-from-org +(sv/defmethod ::remove-team-from-organization {::doc/added "2.17" - ::sm/params schema:remove-team-from-org} + ::sm/params schema:remove-team-from-organization} [cfg {:keys [::rpc/profile-id team-id organization-id organization-name]}] (assert-is-owner cfg profile-id team-id) @@ -424,24 +424,24 @@ (assert-membership cfg profile-id organization-id) ;; Check moveTeams permission on the source organization (when (contains? cf/flags :nitrate) - (let [org-perms (nitrate/call cfg :get-org-permissions - {:organization-id organization-id})] - (if (nil? org-perms) + (let [organization-perms (nitrate/call cfg :get-organization-permissions + {:organization-id organization-id})] + (if (nil? organization-perms) (ex/raise :type :validation :code :not-allowed :hint "Unable to verify organization permissions") (when-not (nitrate-perms/allowed? :move-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id profile-id}) (ex/raise :type :validation :code :not-allowed :hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner."))))) ;; Api call to nitrate - (nitrate/call cfg :remove-team-from-org {:team-id team-id :organization-id organization-id}) + (nitrate/call cfg :remove-team-from-organization {:team-id team-id :organization-id organization-id}) ;; Notify connected users - (notifications/notify-team-change cfg {:id team-id :organization {:name organization-name}} "dashboard.team-no-longer-belong-org") + (notifications/notify-team-change cfg {:id team-id :organization {:name organization-name}} "dashboard.team-no-longer-belong-organization") nil) (def ^:private sql:delete-team-external-invitations @@ -457,12 +457,12 @@ AND deleted_at IS NULL") (defn- get-external-invitation-info - "Returns info about external (non-org-member) invitations pending for a team. - External invitations are those sent to users who are not members of the given org. + "Returns info about external (non-organization-member) invitations pending for a team. + External invitations are those sent to users who are not members of the given organization. Returns {:allows-anybody bool :external-emails [...]}" [{:keys [::db/conn] :as cfg} team-id organization-id] - (let [org-perms (nitrate/call cfg :get-org-permissions {:organization-id organization-id}) - allows-anybody (nitrate-perms/allowed? :add-anybody-to-team {:org-perms org-perms})] + (let [organization-perms (nitrate/call cfg :get-organization-permissions {:organization-id organization-id}) + allows-anybody (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms organization-perms})] (if allows-anybody {:allows-anybody true :external-emails []} (let [emails (map :email (noh/get-team-invitation-emails conn team-id))] @@ -470,9 +470,9 @@ {:allows-anybody false :external-emails []} (let [emails-array (db/create-array conn "text" (vec emails)) profiles (db/exec! conn [sql:get-profiles-by-emails emails-array]) - org-member-ids (into #{} (nitrate/call cfg :get-org-members {:organization-id organization-id})) + organization-member-ids (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id})) external-emails (->> profiles - (remove #(contains? org-member-ids (:id %))) + (remove #(contains? organization-member-ids (:id %))) (map :email) (vec))] {:allows-anybody false :external-emails external-emails})))))) @@ -494,60 +494,60 @@ (assert-membership cfg profile-id organization-id) (when (contains? cf/flags :nitrate) - (let [org-member-ids-before (into #{} (nitrate/call cfg :get-org-members {:organization-id organization-id})) - team-with-org (nitrate/call cfg :get-team-org {:team-id team-id}) - source-org-id (get-in team-with-org [:organization :id]) - source-org-perms (when source-org-id - (nitrate/call cfg :get-org-permissions - {:organization-id source-org-id})) - target-org-perms (nitrate/call cfg :get-org-permissions - {:organization-id organization-id}) - target-org-same-owner? (and (some? source-org-perms) - (some? target-org-perms) - (= (:owner-id source-org-perms) - (:owner-id target-org-perms)))] - (when (nil? target-org-perms) + (let [organization-member-ids-before (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id})) + team-with-organization (nitrate/call cfg :get-team-organization {:team-id team-id}) + source-organization-id (get-in team-with-organization [:organization :id]) + source-organization-perms (when source-organization-id + (nitrate/call cfg :get-organization-permissions + {:organization-id source-organization-id})) + target-organization-perms (nitrate/call cfg :get-organization-permissions + {:organization-id organization-id}) + target-organization-same-owner? (and (some? source-organization-perms) + (some? target-organization-perms) + (= (:owner-id source-organization-perms) + (:owner-id target-organization-perms)))] + (when (nil? target-organization-perms) (ex/raise :type :validation :code :not-allowed :hint "Unable to verify organization permissions")) - ;; Team already belongs to an organization: check move-teams on source org. - (when (some? source-org-id) - (when (nil? source-org-perms) + ;; Team already belongs to an organization: check move-teams on the source organization. + (when (some? source-organization-id) + (when (nil? source-organization-perms) (ex/raise :type :validation :code :not-allowed :hint "Unable to verify organization permissions")) (when-not (nitrate-perms/allowed? :move-team - {:org-perms source-org-perms + {:organization-perms source-organization-perms :profile-id profile-id - :target-org-same-owner? target-org-same-owner?}) + :target-organization-same-owner? target-organization-same-owner?}) (ex/raise :type :validation :code :not-allowed :hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner."))) ;; Always check target create-teams permission (new/add and move flows). (when-not (nitrate-perms/allowed? :create-team - {:org-perms target-org-perms + {:organization-perms target-organization-perms :profile-id profile-id}) (ex/raise :type :validation :code :not-allowed :hint "You are not allowed to add teams in this organization")) - ;; Add teammates to the org if needed + ;; Add teammates to the organization if needed (let [team-members (db/query cfg :team-profile-rel {:team-id team-id}) new-member-ids (->> team-members (map :profile-id) (remove #{profile-id}) - (remove org-member-ids-before))] + (remove organization-member-ids-before))] (doseq [member-id new-member-ids] - (teams/initialize-user-in-nitrate-org cfg member-id organization-id))) + (teams/initialize-user-in-nitrate-organization cfg member-id organization-id))) ;; Api call to nitrate - (let [team (nitrate/call cfg :set-team-org {:team-id team-id - :organization-id organization-id - :is-default false})] + (let [team (nitrate/call cfg :set-team-organization {:team-id team-id + :organization-id organization-id + :is-default false})] ;; Notify connected users - (notifications/notify-team-change cfg team "dashboard.team-belong-org")) + (notifications/notify-team-change cfg team "dashboard.team-belong-organization")) ;; Delete pending invitations for users who are not members of the target organization (let [{:keys [allows-anybody external-emails]} (get-external-invitation-info cfg team-id organization-id)] @@ -556,21 +556,21 @@ emails-array (db/create-array conn "text" external-emails)] (db/exec! conn [sql:delete-team-external-invitations team-id emails-array])))) - ;; Send warnings via email if the org has sso + ;; Send warnings via email if the organization has sso (neh/send-organization-setup-sso-emails-for-team! - cfg organization-id team-id org-member-ids-before))) + cfg organization-id team-id organization-member-ids-before))) nil) -(def ^:private schema:check-org-members-params - [:map {:title "CheckOrgMembersParams"} +(def ^:private schema:check-organization-members-params + [:map {:title "CheckOrganizationMembersParams"} [:organization-id ::sm/uuid] [:emails [:vector ::sm/email]]]) -(sv/defmethod ::check-org-members +(sv/defmethod ::check-organization-members {::rpc/auth true ::doc/added "2.17" - ::sm/params schema:check-org-members-params + ::sm/params schema:check-organization-members-params ::sm/result [:map-of :string :boolean] ::db/transaction true} [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id organization-id emails]}] @@ -579,23 +579,23 @@ (let [emails-array (db/create-array conn "text" emails) profiles (db/exec! conn [sql:get-profiles-by-emails emails-array]) email->id (into {} (map (fn [p] [(:email p) (:id p)])) profiles) - org-member-ids (into #{} (nitrate/call cfg :get-org-members {:organization-id organization-id}))] + organization-member-ids (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id}))] (into {} (map (fn [email] (let [pid (get email->id email)] - [email (boolean (and pid (contains? org-member-ids pid)))]))) + [email (boolean (and pid (contains? organization-member-ids pid)))]))) emails))) {})) -(def ^:private schema:all-org-members-in-team-params - [:map {:title "CheckOrgMembersInTeamParams"} +(def ^:private schema:all-organization-members-in-team-params + [:map {:title "CheckOrganizationMembersInTeamParams"} [:team-id ::sm/uuid] [:organization-id ::sm/uuid]]) -(sv/defmethod ::all-org-members-in-team +(sv/defmethod ::all-organization-members-in-team {::rpc/auth true ::doc/added "2.17" - ::sm/params schema:all-org-members-in-team-params + ::sm/params schema:all-organization-members-in-team-params ::sm/result ::sm/boolean} [cfg {:keys [::rpc/profile-id team-id organization-id]}] (if (contains? cf/flags :nitrate) @@ -604,22 +604,22 @@ (ex/raise :type :validation :code :insufficient-permissions)) (assert-membership cfg profile-id organization-id) - (let [org-members (nitrate/call cfg :get-org-members {:organization-id organization-id}) - org-member-ids (into #{} org-members) + (let [organization-members (nitrate/call cfg :get-organization-members {:organization-id organization-id}) + organization-member-ids (into #{} organization-members) team-members (db/query cfg :team-profile-rel {:team-id team-id}) team-member-ids (into #{} (map :profile-id team-members))] - (every? #(contains? team-member-ids %) org-member-ids))) + (every? #(contains? team-member-ids %) organization-member-ids))) false)) -(def ^:private schema:all-team-members-in-orgs-params - [:map {:title "CheckTeamMembersInOrgsParams"} +(def ^:private schema:all-team-members-in-organizations-params + [:map {:title "CheckTeamMembersInOrganizationsParams"} [:team-id ::sm/uuid] [:organization-ids [:vector ::sm/uuid]]]) -(sv/defmethod ::all-team-members-in-orgs +(sv/defmethod ::all-team-members-in-organizations {::rpc/auth true ::doc/added "2.17" - ::sm/params schema:all-team-members-in-orgs-params + ::sm/params schema:all-team-members-in-organizations-params ::sm/result [:map-of ::sm/uuid ::sm/boolean]} [cfg {:keys [::rpc/profile-id team-id organization-ids]}] (if (contains? cf/flags :nitrate) @@ -630,15 +630,15 @@ (let [team-members (db/query cfg :team-profile-rel {:team-id team-id}) team-member-ids (into #{} (map :profile-id team-members))] - ;; Validate requester membership in all orgs before fetching members. + ;; Validate requester membership in all organizations before fetching members. (run! #(assert-membership cfg profile-id %) organization-ids) (into {} (map (fn [organization-id] - (let [org-members (nitrate/call cfg :get-org-members {:organization-id organization-id}) - org-member-ids (into #{} org-members)] + (let [organization-members (nitrate/call cfg :get-organization-members {:organization-id organization-id}) + organization-member-ids (into #{} organization-members)] [organization-id - (every? #(contains? org-member-ids %) team-member-ids)]))) + (every? #(contains? organization-member-ids %) team-member-ids)]))) organization-ids))) {})) @@ -682,7 +682,7 @@ (sv/defmethod ::check-nitrate-sso "Check if a user needs to login into the organization SSO. - Accepts either team-id (to look up the org via the team) or organization-id directly. + Accepts either team-id (to look up the organization via the team) or organization-id directly. Returns {:authorized true} when SSO is not active or the user cannot access the team. Returns {:authorized false :redirect-uri } when SSO is active; the client must redirect there. The OIDC provider itself handles @@ -702,11 +702,11 @@ {:keys [authorized sso]} (nitrate/sso-session-authorized? cfg organization-id team-id request)] (if authorized {:authorized true} - (if (oidc/org-sso-discovery-uri sso) + (if (oidc/organization-sso-discovery-uri sso) {:authorized false - :redirect-uri (oidc/build-org-sso-auth-redirect-uri cfg sso - :dest-url url - :organization-id organization-id)} + :redirect-uri (oidc/build-organization-sso-auth-redirect-uri cfg sso + :dest-url url + :organization-id organization-id)} {:authorized false :redirect-uri nil})))) {:authorized true})) diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index 0d879ff02f..eb2612bad7 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -495,10 +495,10 @@ {:id profile-id}) ;; Delete owned organizations on the fly (no grace period). - ;; Nitrate iterates the user's owned orgs and, per org, calls + ;; Nitrate iterates the user's owned organizations and, per organization, calls ;; Penpot back through two paths: ::notify-user-organizations-deletion - ;; (during delete-owned-orgs) and ::notify-organization-deletion. - ;; Both preserve org teams unchanged and only prefix or delete + ;; (during delete-owned-organizations) and ::notify-organization-deletion. + ;; Both preserve organization teams unchanged and only prefix or delete ;; imported "Your Penpot" teams according to whether they still have files. ;; Let Nitrate clean up the data associated with the deleted Penpot user: ;; owned organizations, remaining memberships, and subscription cancellation. @@ -562,7 +562,7 @@ ::sm/result schema:get-owned-organizations-summary-result} [cfg {:keys [::rpc/profile-id]}] (if (contains? cf/flags :nitrate) - (or (nitrate/call cfg :get-owned-orgs-summary {:profile-id profile-id}) []) + (or (nitrate/call cfg :get-owned-organizations-summary {:profile-id profile-id}) []) [])) ;; --- HELPERS diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index aef8eb5b93..817f8f7848 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -198,7 +198,7 @@ (dm/with-open [conn (db/open pool)] (cond->> (get-teams conn profile-id) (contains? cf/flags :nitrate) - (map #(nitrate/add-org-info-to-team cfg % params)) + (map #(nitrate/add-organization-info-to-team cfg % params)) (contains? cf/flags :nitrate) (remove #(get-in % [:organization :expired-license]))))) @@ -245,10 +245,10 @@ [cfg {:keys [::rpc/profile-id id file-id] :as params}] (let [team (get-team cfg :profile-id profile-id :team-id id :file-id file-id)] (if (contains? cf/flags :nitrate) - (nitrate/add-org-info-to-team cfg team params) + (nitrate/add-organization-info-to-team cfg team params) team))) -(defn- get-org-owner-viewer-team +(defn- get-organization-owner-viewer-team "When `profile-id` is a non-member owner of the organization that owns the requested team, returns the team shaped with viewer permissions; otherwise nil. `cfg` must carry the nitrate client." @@ -305,7 +305,7 @@ (-> result (decode-row) (process-permissions)) - (or (get-org-owner-viewer-team cfg profile-id default-team-id params) + (or (get-organization-owner-viewer-team cfg profile-id default-team-id params) (ex/raise :type :not-found :code :team-does-not-exist))))) @@ -535,17 +535,17 @@ (quotes/check! cfg {::quotes/id ::quotes/teams-per-profile ::quotes/profile-id profile-id}) - ;; When creating inside an org, verify the user has permission to do so. - ;; Fail closed: if org permissions cannot be fetched, deny the operation. + ;; When creating inside an organization, verify the user has permission to do so. + ;; Fail closed: if organization permissions cannot be fetched, deny the operation. (when (and organization-id (contains? cf/flags :nitrate)) - (let [org-perms (nitrate/call cfg :get-org-permissions - {:organization-id organization-id})] - (if (nil? org-perms) + (let [organization-perms (nitrate/call cfg :get-organization-permissions + {:organization-id organization-id})] + (if (nil? organization-perms) (ex/raise :type :validation :code :not-allowed :hint "Unable to verify organization permissions") (when-not (nitrate-perms/allowed? :create-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id profile-id}) (ex/raise :type :validation :code :not-allowed @@ -563,7 +563,7 @@ {::audit/props {:id (:id team)}}))) -(defn create-default-org-team +(defn create-default-organization-team [cfg profile-id organization-id] (quotes/check! cfg {::quotes/id ::quotes/teams-per-profile ::quotes/profile-id profile-id}) @@ -579,11 +579,11 @@ team (create-team cfg params)] (select-keys team [:id]))) -(defn initialize-user-in-nitrate-org +(defn initialize-user-in-nitrate-organization "If needed, create a default team for the user on the organization, - and notify Nitrate that an user has been added to an org." + and notify Nitrate that a user has been added to an organization." ([cfg profile-id organization-id] - (initialize-user-in-nitrate-org cfg profile-id organization-id nil)) + (initialize-user-in-nitrate-organization cfg profile-id organization-id nil)) ([cfg profile-id organization-id email] (assert (db/connection-map? cfg) "expected cfg with valid connection") @@ -592,24 +592,24 @@ cfg (fn [{:keys [::db/conn] :as tx-cfg}] - (let [membership (nitrate/call cfg :get-org-membership {:profile-id profile-id - :organization-id organization-id})] + (let [membership (nitrate/call cfg :get-organization-membership {:profile-id profile-id + :organization-id organization-id})] ;; Only when the user doesn't belong to the organization yet (when (and (some? (:organization-id membership)) ;; the organization exists - (not (:is-member membership))) ;; the user is not a member of the org yet + (not (:is-member membership))) ;; the user is not a member of the organization yet (let [organization-id organization-id - default-team (create-default-org-team (assoc tx-cfg ::db/conn conn) profile-id organization-id) + default-team (create-default-organization-team (assoc tx-cfg ::db/conn conn) profile-id organization-id) default-team-id (:id default-team) - result (nitrate/call tx-cfg :add-profile-to-org (cond-> {:profile-id profile-id - :team-id default-team-id - :organization-id organization-id} - (some? email) (assoc :email email)))] + result (nitrate/call tx-cfg :add-profile-to-organization (cond-> {:profile-id profile-id + :team-id default-team-id + :organization-id organization-id} + (some? email) (assoc :email email)))] (when (not (:is-member result)) (ex/raise :type :internal - :code :failed-add-profile-org-nitrate + :code :failed-add-profile-organization-nitrate :context {:profile-id profile-id :organization-id organization-id :default-team-id default-team-id})) @@ -622,12 +622,12 @@ (assert (db/connection-map? cfg) "expected cfg with valid connection") (when (contains? cf/flags :nitrate) - (let [membership (nitrate/call cfg :get-org-membership-by-team {:profile-id profile-id :team-id team-id})] + (let [membership (nitrate/call cfg :get-organization-membership-by-team {:profile-id profile-id :team-id team-id})] ;; Only when the team belong to an organization and the user is not a member (when (and (some? (:organization-id membership)) ;; the team do belong to an organization - (not (:is-member membership))) ;; the user is not a member of the org yet - (initialize-user-in-nitrate-org cfg profile-id (:organization-id membership))))) + (not (:is-member membership))) ;; the user is not a member of the organization yet + (initialize-user-in-nitrate-organization cfg profile-id (:organization-id membership))))) (db/insert! conn :team-profile-rel (assoc params :id (uuid/next)) options))) (defn create-team @@ -804,16 +804,16 @@ (let [team (get-team conn :profile-id profile-id :team-id team-id) team (if (contains? cf/flags :nitrate) - (nitrate/add-org-info-to-team cfg team params) + (nitrate/add-organization-info-to-team cfg team params) team) perms (get team :permissions) - org (:organization team) - in-org? (and (contains? cf/flags :nitrate) org) + organization (:organization team) + in-organization? (and (contains? cf/flags :nitrate) organization) can-delete? - (if in-org? + (if in-organization? (nitrate-perms/allowed? :delete-team - {:org-perms {:owner-id (dm/get-in team [:organization :owner-id]) - :permissions (dm/get-in team [:organization :permissions])} + {:organization-perms {:owner-id (dm/get-in team [:organization :owner-id]) + :permissions (dm/get-in team [:organization :permissions])} :profile-id profile-id :team-perms perms}) (boolean (:is-owner perms)))] @@ -823,8 +823,8 @@ :code :only-owner-can-delete-team)) ;; Protect the user's personal default team from deletion. - ;; Org-scoped default teams ("Your Penpot") are allowed to be deleted when they have no files. - (when (and (:is-default team) (not in-org?)) + ;; Organization-scoped default teams ("Your Penpot") are allowed to be deleted when they have no files. + (when (and (:is-default team) (not in-organization?)) (ex/raise :type :validation :code :non-deletable-team :hint "impossible to delete default team")) diff --git a/backend/src/app/rpc/commands/teams_invitations.clj b/backend/src/app/rpc/commands/teams_invitations.clj index a101a278b1..679ea1dd10 100644 --- a/backend/src/app/rpc/commands/teams_invitations.clj +++ b/backend/src/app/rpc/commands/teams_invitations.clj @@ -44,7 +44,7 @@ update set role = ?, valid_until = ?, updated_at = now() returning *") -(def sql:upsert-org-invitation +(def sql:upsert-organization-invitation "insert into team_invitation(id, team_id, org_id, email_to, created_by, role, valid_until) values (?, null, ?, ?, ?, ?, ?) on conflict(org_id, email_to) where team_id is null do @@ -86,8 +86,8 @@ [:role types.team/schema:role] [:email ::sm/email]]) -(def ^:private schema:create-org-invitation - [:map {:title "params:create-org-invitation"} +(def ^:private schema:create-organization-invitation + [:map {:title "params:create-organization-invitation"} [::rpc/profile-id ::sm/uuid] [:organization [:map @@ -107,8 +107,8 @@ (def ^:private check-create-invitation-params (sm/check-fn schema:create-invitation)) -(def ^:private check-create-org-invitation-params - (sm/check-fn schema:create-org-invitation)) +(def ^:private check-create-organization-invitation-params + (sm/check-fn schema:create-organization-invitation)) (defn- allow-invitation-emails? [member] @@ -116,24 +116,24 @@ (not= :none (:email-invites notifications)))) (defn- assert-email-can-be-invited - "Asserts that member is an org member when the org + "Asserts that member is an organization member when the organization restricts who can be added to teams." - [member org-member-ids] - (when (some? org-member-ids) - (let [is-member? (and (some? member) (contains? org-member-ids (:id member)))] + [member organization-member-ids] + (when (some? organization-member-ids) + (let [is-member? (and (some? member) (contains? organization-member-ids (:id member)))] (when-not is-member? (ex/raise :type :validation - :code :email-not-org-member + :code :email-not-organization-member :hint "The invited email is not a member of the organization"))))) (defn- create-invitation [{:keys [::db/conn] :as cfg} - {:keys [team organization profile role email org-member-ids organization-member-ids] :as params}] + {:keys [team organization profile role email organization-member-ids all-organization-member-ids] :as params}] (assert (db/connection-map? cfg) "expected cfg with valid connection") (if organization - (assert (check-create-org-invitation-params params)) + (assert (check-create-organization-invitation-params params)) (assert (check-create-invitation-params params))) (let [email (profile/clean-email email) @@ -145,11 +145,11 @@ :code :email-domain-is-not-allowed :hint "email domain is in the blacklist")) - ;; When nitrate is active and the team belongs to an org, check that - ;; the email is already an org member unless the org explicitly allows adding anybody. + ;; When nitrate is active and the team belongs to an organization, check that + ;; the email is already an organization member unless the organization explicitly allows adding anybody. (when (and (contains? cf/flags :nitrate) (:organization team)) - (assert-email-can-be-invited member org-member-ids)) + (assert-email-can-be-invited member organization-member-ids)) ;; When we have email verification disabled and invitation user is @@ -165,9 +165,9 @@ (get types.team/permissions-for-role role))] (if organization - ;; Insert the invited member to the org + ;; Insert the invited member to the organization (when (contains? cf/flags :nitrate) - (teams/initialize-user-in-nitrate-org cfg (:id member) (:id organization) email)) + (teams/initialize-user-in-nitrate-organization cfg (:id member) (:id organization) email)) ;; Insert the invited member to the team (teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true})) @@ -190,7 +190,7 @@ (ct/in-future "876000h") ;; Organization invitations doesn't expire (ct/in-future "168h")) ;; 7 days invitation (db/exec-one! conn (if organization - [sql:upsert-org-invitation id + [sql:upsert-organization-invitation id (:id organization) (str/lower email) (:id profile) @@ -233,7 +233,7 @@ (boolean (and team-organization-id member - (contains? organization-member-ids (:id member)))))) + (contains? all-organization-member-ids (:id member)))))) itoken (create-invitation-token cfg tprops) ptoken (create-profile-identity-token cfg profile-id)] @@ -255,7 +255,7 @@ (if organization (when (contains? cf/flags :nitrate) (eml/send! {::eml/conn conn - ::eml/factory eml/invite-to-org + ::eml/factory eml/invite-to-organization :public-uri (cf/get :public-uri) :to email :invited-by (:fullname profile) @@ -275,7 +275,7 @@ itoken))))) -(defn create-org-invitation +(defn create-organization-invitation [cfg {:keys [::rpc/profile-id] :as params}] (let [profile (db/get-by-id cfg :profile profile-id)] (create-invitation cfg @@ -345,21 +345,21 @@ - emails (set) + role (single role for all emails) - invitations (vector of {:email :role} maps)" [{:keys [::db/conn] :as cfg} {:keys [profile team role emails invitations] :as params}] - (let [;; Enrich team with org info once for all invitations when nitrate is active + (let [;; Enrich team with organization info once for all invitations when nitrate is active team (if (contains? cf/flags :nitrate) - (nitrate/add-org-info-to-team cfg team {}) + (nitrate/add-organization-info-to-team cfg team {}) team) - org (:organization team) - org-id (:id org) - restricted? (and org-id (not (nitrate-perms/allowed? :add-anybody-to-team {:org-perms org}))) - organization-member-ids - (when org-id - (into #{} (nitrate/call cfg :get-org-members {:organization-id org-id}))) - org-member-ids (when restricted? organization-member-ids) + organization (:organization team) + organization-id (:id organization) + restricted? (and organization-id (not (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms organization}))) + all-organization-member-ids + (when organization-id + (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id}))) + organization-member-ids (when restricted? all-organization-member-ids) params (assoc params :team team - :org-member-ids org-member-ids - :organization-member-ids organization-member-ids) + :organization-member-ids organization-member-ids + :all-organization-member-ids all-organization-member-ids) ;; Normalize input to a consistent format: [{:email :role}] invitation-data (cond diff --git a/backend/src/app/rpc/commands/verify_token.clj b/backend/src/app/rpc/commands/verify_token.clj index 7d6125ccb4..99fabd2e8b 100644 --- a/backend/src/app/rpc/commands/verify_token.clj +++ b/backend/src/app/rpc/commands/verify_token.clj @@ -101,6 +101,17 @@ ;; --- Team Invitation +(def ^:private sql:get-organization-invitation + "SELECT * + FROM team_invitation + WHERE email_to = ? + AND org_id = ?") + +(def ^:private sql:delete-organization-invitation + "DELETE FROM team_invitation + WHERE email_to = ? + AND org_id = ?") + (defn- accept-invitation [{:keys [::db/conn] :as cfg} {:keys [team-id organization-id role member-email] :as claims} invitation member] @@ -124,9 +135,9 @@ (get types.team/permissions-for-role role)) accepted-team-id (if organization-id - ;; Insert the invited member to the org + ;; Insert the invited member to the organization (when (contains? cf/flags :nitrate) - (teams/initialize-user-in-nitrate-org cfg id-member organization-id member-email)) + (teams/initialize-user-in-nitrate-organization cfg id-member organization-id member-email)) ;; Insert the invited member to the team (do (teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true}) team-id))] @@ -145,10 +156,11 @@ {:id id-member})) ;; Delete the invitation - (db/delete! conn :team-invitation - (cond-> {:email-to member-email} - team-id (assoc :team-id team-id) - organization-id (assoc :org-id organization-id))) + (if organization-id + (db/exec-one! conn [sql:delete-organization-invitation member-email organization-id]) + (db/delete! conn :team-invitation + {:email-to member-email + :team-id team-id})) ;; Delete any request (only applicable for team invitations) (when team-id @@ -184,16 +196,17 @@ :code :invalid-invitation-token :hint "invitation token contains unexpected data")) - (let [invitation (db/get* conn :team-invitation - (cond-> {:email-to member-email} - team-id (assoc :team-id team-id) - organization-id (assoc :org-id organization-id))) + (let [invitation (if organization-id + (db/exec-one! conn [sql:get-organization-invitation member-email organization-id]) + (db/get* conn :team-invitation + {:email-to member-email + :team-id team-id})) profile (db/get* conn :profile {:id profile-id} {:columns [:id :email :default-team-id]}) registration-disabled? (not (contains? cf/flags :registration)) - org-invitation? (and (contains? cf/flags :nitrate) organization-id)] + organization-invitation? (and (contains? cf/flags :nitrate) organization-id)] (if profile (do @@ -219,12 +232,12 @@ (when (contains? cf/flags :nitrate) (cond organization-id - (nitrate/call cfg :get-org-membership {:profile-id profile-id - :organization-id organization-id}) + (nitrate/call cfg :get-organization-membership {:profile-id profile-id + :organization-id organization-id}) team-id - (nitrate/call cfg :get-org-membership-by-team {:profile-id profile-id - :team-id team-id}))) + (nitrate/call cfg :get-organization-membership-by-team {:profile-id profile-id + :team-id team-id}))) organization-id-on-add (when (and (:organization-id membership) @@ -246,19 +259,19 @@ organization-member-count-before (when organization-id-on-add (count - (nitrate/call cfg :get-org-members + (nitrate/call cfg :get-organization-members {:organization-id organization-id-on-add})))] (when (:is-member membership) - (when org-invitation? + (when organization-invitation? (ex/raise :type :validation - :code :already-an-org-member + :code :already-an-organization-member :team-id (:default-team-id membership) :hint "the user is already a member of the organization"))) - (when (and org-invitation? (not (:organization-id membership))) + (when (and organization-invitation? (not (:organization-id membership))) (ex/raise :type :validation - :code :org-not-found + :code :organization-not-found :team-id (:default-team-id profile) :hint "the organization doesn't exist")) @@ -299,10 +312,10 @@ (audit/clean-props)))))) (cond-> (assoc claims :state :created) - ;; when the invitation is to an org, instead of a team, add the - ;; accepted-team-id as :org-team-id + ;; when the invitation is to an organization, instead of a team, add the + ;; accepted-team-id as :organization-team-id (:organization-id claims) - (assoc :org-team-id accepted-team-id) + (assoc :organization-team-id accepted-team-id) organization-id-on-add (assoc :organization-invitation-audit @@ -320,7 +333,7 @@ ;; If the user is not logged-in and the invitation has been canceled ;; we return a specific error code so the frontend can redirect to ;; login with an appropriate message instead of showing the error page. - ;; This only applies to org invitations; team invitations keep the + ;; This only applies to organization invitations; team invitations keep the ;; existing :invalid-token behavior. (when (nil? invitation) (ex/raise :type :validation diff --git a/backend/src/app/rpc/management/nitrate.clj b/backend/src/app/rpc/management/nitrate.clj index ab286f7bba..adde1bde40 100644 --- a/backend/src/app/rpc/management/nitrate.clj +++ b/backend/src/app/rpc/management/nitrate.clj @@ -167,12 +167,12 @@ [:role ::sm/text]]) (sv/defmethod ::notify-user-added-to-organization - "Notify to Penpot that an user has joined an org from nitrate" + "Notify to Penpot that an user has joined an organization from nitrate" {::doc/added "2.14" ::sm/params schema:notify-user-added-to-organization ::rpc/auth false} [cfg {:keys [profile-id organization-id]}] - (db/tx-run! cfg teams/create-default-org-team profile-id organization-id)) + (db/tx-run! cfg teams/create-default-organization-team profile-id organization-id)) ;; ---- API: get-managed-profiles @@ -314,7 +314,7 @@ RETURNING id, deleted_at;") nil) (defn manage-deleted-organization-teams - "For a deleted organization, preserve org teams unchanged and only prefix or + "For a deleted organization, preserve organization teams unchanged and only prefix or delete member Your Penpot teams depending on whether they still contain files." [cfg {:keys [organization-id organization-name teams]}] (let [all-team-ids (->> teams @@ -329,7 +329,7 @@ RETURNING id, deleted_at;") distinct (into []))] (when (seq all-team-ids) - (let [org-prefix (str "[" (d/sanitize-string organization-name) "] ")] + (let [organization-prefix (str "[" (d/sanitize-string organization-name) "] ")] (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] @@ -343,11 +343,11 @@ RETURNING id, deleted_at;") teams-to-prefix (->> your-penpot-team-ids (filter teams-with-files) (into [])) teams-to-delete (->> your-penpot-team-ids (remove teams-with-files) (into []))] - ;; Org teams move to the fallback org unchanged. Only imported - ;; Your Penpot teams keep the org prefix when they still have files. + ;; Organization teams move to the fallback organization unchanged. Only imported + ;; Your Penpot teams keep the organization prefix when they still have files. (when (seq teams-to-prefix) (db/exec! conn [sql:prefix-teams-name-and-unset-default - org-prefix + organization-prefix (db/create-array conn "uuid" teams-to-prefix)])) ;; Empty imported Your Penpot teams disappear entirely. @@ -358,16 +358,16 @@ RETURNING id, deleted_at;") (sv/defmethod ::notify-organization-deletion - "For a deleted organization, preserve org teams and only prefix or delete + "For a deleted organization, preserve organization teams and only prefix or delete imported Your Penpot teams before notifying connected users." {::doc/added "2.15" ::sm/params schema:notify-organization-deletion ::rpc/auth false} [cfg {:keys [organization-id]}] - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id}) - teams (:teams org-summary)] - (manage-deleted-organization-teams cfg {:organization-name (:name org-summary) - :organization-id (:id org-summary) + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id}) + teams (:teams organization-summary)] + (manage-deleted-organization-teams cfg {:organization-name (:name organization-summary) + :organization-id (:id organization-summary) :teams teams}) nil)) @@ -378,18 +378,18 @@ RETURNING id, deleted_at;") [:profile-id ::sm/uuid]]) (sv/defmethod ::notify-user-organizations-deletion - "For a given user, find all owned organizations and apply the deleted-org + "For a given user, find all owned organizations and apply the deleted-organization transfer rules to their imported Your Penpot teams." {::doc/added "2.18" ::sm/params schema:notify-user-organizations-deletion ::nitrate/sso false} [cfg {:keys [profile-id]}] - (let [owned-orgs (nitrate/call cfg :get-owned-orgs {:profile-id profile-id})] - (doseq [org owned-orgs] - (let [organization-name (:name org) - teams (:teams org)] + (let [owned-organizations (nitrate/call cfg :get-owned-organizations {:profile-id profile-id})] + (doseq [organization owned-organizations] + (let [organization-name (:name organization) + teams (:teams organization)] (manage-deleted-organization-teams cfg {:organization-name organization-name - :organization-id (:id org) + :organization-id (:id organization) :teams teams})))) nil) @@ -496,7 +496,7 @@ RETURNING id, deleted_at;") [:organization schema:organization-with-avatar]] ::nitrate/sso false} [cfg params] - (db/tx-run! cfg ti/create-org-invitation params) + (db/tx-run! cfg ti/create-organization-invitation params) nil) @@ -524,9 +524,9 @@ RETURNING id, deleted_at;") ::sm/result schema:get-organization-invitations-result ::nitrate/sso false} [cfg {:keys [organization-id]}] - (let [team-ids (noh/get-org-team-ids cfg organization-id)] + (let [team-ids (noh/get-organization-team-ids cfg organization-id)] (db/run! cfg (fn [{:keys [::db/conn]}] - (->> (noh/get-org-invitations conn organization-id team-ids) + (->> (noh/get-organization-invitations conn organization-id team-ids) (mapv (fn [{:keys [photo-id] :as invitation}] (cond-> (dissoc invitation :photo-id) photo-id @@ -546,13 +546,13 @@ RETURNING id, deleted_at;") [:email ::sm/email]]) (sv/defmethod ::delete-organization-invitations - "Delete all invitations for one email in an organization scope (org + org teams)." + "Delete all invitations for one email in an organization scope (organization + organization teams)." {::doc/added "2.16" ::sm/params schema:delete-organization-invitations-params ::nitrate/sso false} [cfg {:keys [organization-id email]}] (let [clean-email (profile/clean-email email) - team-ids (noh/get-org-team-ids cfg organization-id)] + team-ids (noh/get-organization-team-ids cfg organization-id)] (db/run! cfg (fn [{:keys [::db/conn]}] (let [ids-array (db/create-array conn "uuid" team-ids)] (db/exec! conn [sql:delete-organization-invitations clean-email organization-id ids-array])))) @@ -571,14 +571,14 @@ RETURNING id, deleted_at;") [:organization-id ::sm/uuid]]) (sv/defmethod ::delete-all-organization-invitations - "Delete every pending invitation associated with an organization (org-level + team-level). + "Delete every pending invitation associated with an organization (organization-level + team-level). Called from Nitrate when an organization is about to be deleted, so users that click their invitation token hit the existing invalid-token landing page." {::doc/added "2.18" ::sm/params schema:delete-all-organization-invitations-params ::rpc/auth false} [cfg {:keys [organization-id]}] - (let [team-ids (noh/get-org-team-ids cfg organization-id)] + (let [team-ids (noh/get-organization-team-ids cfg organization-id)] (db/run! cfg (fn [{:keys [::db/conn]}] (let [ids-array (db/create-array conn "uuid" team-ids)] (db/exec! conn [sql:delete-all-organization-invitations organization-id ids-array])))) @@ -633,16 +633,16 @@ RETURNING id, deleted_at;") valid-teams-to-leave (into valid-teams-to-exit (map add-reassign-to valid-teams-to-transfer))] - (cnit/leave-org cfg (assoc params - :id organization-id - :name organization-name - :teams-to-delete valid-teams-to-delete-ids - :teams-to-leave valid-teams-to-leave - :skip-validation true - :user-who-delete-member actor-profile-id - :deleted-by-role (when actor-profile-id - "organization-owner"))) - (notifications/notify-user-org-change cfg profile-id organization-id organization-name "dashboard.user-no-longer-belong-org") + (cnit/leave-organization cfg (assoc params + :id organization-id + :name organization-name + :teams-to-delete valid-teams-to-delete-ids + :teams-to-leave valid-teams-to-leave + :skip-validation true + :user-who-delete-member actor-profile-id + :deleted-by-role (when actor-profile-id + "organization-owner"))) + (notifications/notify-user-organization-change cfg profile-id organization-id organization-name "dashboard.user-no-longer-belong-organization") nil)) ;; API: get-remove-from-organization-summary @@ -673,11 +673,11 @@ RETURNING id, deleted_at;") (when-not valid-default-team (ex/raise :type :validation :code :not-valid-teams)) - (cnit/get-leave-org-summary cfg - default-team-id - valid-teams-to-delete-ids - (count valid-teams-to-transfer) - (count valid-teams-to-exit)))) + (cnit/get-leave-organization-summary cfg + default-team-id + valid-teams-to-delete-ids + (count valid-teams-to-transfer) + (count valid-teams-to-exit)))) ;; API: send-renewal-email @@ -720,7 +720,7 @@ RETURNING id, deleted_at;") WHERE id = ANY(?) AND deleted_at IS NULL") -(def ^:private sql:exists-non-member-org-team-invitations +(def ^:private sql:exists-non-member-organization-team-invitations "SELECT EXISTS ( SELECT 1 FROM team_invitation @@ -728,13 +728,13 @@ RETURNING id, deleted_at;") AND email_to <> ALL(?) ) AS non_member") -(def ^:private sql:delete-non-member-org-team-invitations +(def ^:private sql:delete-non-member-organization-team-invitations "DELETE FROM team_invitation WHERE team_id = ANY(?) AND email_to <> ALL(?) RETURNING email_to") -(def ^:private schema:org-team-invitations-for-non-members-params +(def ^:private schema:organization-team-invitations-for-non-members-params [:map [:team-ids [:vector ::sm/uuid]] [:member-ids [:vector ::sm/uuid]]]) @@ -742,8 +742,8 @@ RETURNING id, deleted_at;") (def ^:private schema:exists-organization-team-invitations-for-non-members-result [:map [:exists ::sm/boolean]]) -(defn- org-team-invitations-for-non-members-arrays - "Member emails and PG arrays used by exists/delete org team invitation endpoints." +(defn- organization-team-invitations-for-non-members-arrays + "Member emails and PG arrays used by exists/delete organization team invitation endpoints." [conn {:keys [team-ids member-ids]}] (let [member-ids-array (db/create-array conn "uuid" member-ids) member-emails (->> (db/exec! conn [sql:get-profile-emails-by-ids member-ids-array]) @@ -752,11 +752,11 @@ RETURNING id, deleted_at;") {:emails-array (db/create-array conn "text" (vec member-emails)) :teams-array (db/create-array conn "uuid" team-ids)})) -(defn- non-member-org-team-invitations-exist? +(defn- non-member-organization-team-invitations-exist? [conn params] (let [{:keys [emails-array teams-array]} - (org-team-invitations-for-non-members-arrays conn params)] - (-> (db/exec-one! conn [sql:exists-non-member-org-team-invitations + (organization-team-invitations-for-non-members-arrays conn params)] + (-> (db/exec-one! conn [sql:exists-non-member-organization-team-invitations teams-array emails-array]) :non-member))) @@ -764,24 +764,24 @@ RETURNING id, deleted_at;") (sv/defmethod ::exists-organization-team-invitations-for-non-members "Return if there are any team invitations for emails that are not organization members." {::doc/added "2.18" - ::sm/params schema:org-team-invitations-for-non-members-params + ::sm/params schema:organization-team-invitations-for-non-members-params ::sm/result schema:exists-organization-team-invitations-for-non-members-result ::nitrate/sso false} [cfg params] (db/run! cfg (fn [{:keys [::db/conn]}] - {:exists (boolean (non-member-org-team-invitations-exist? conn params))}))) + {:exists (boolean (non-member-organization-team-invitations-exist? conn params))}))) (sv/defmethod ::delete-organization-team-invitations-for-non-members "Delete team invitations for emails that are not organization members." {::doc/added "2.18" - ::sm/params schema:org-team-invitations-for-non-members-params + ::sm/params schema:organization-team-invitations-for-non-members-params ::db/transaction true ::nitrate/sso false} [cfg params] (db/run! cfg (fn [{:keys [::db/conn]}] (let [{:keys [emails-array teams-array]} - (org-team-invitations-for-non-members-arrays conn params)] - (db/exec! conn [sql:delete-non-member-org-team-invitations + (organization-team-invitations-for-non-members-arrays conn params)] + (db/exec! conn [sql:delete-non-member-organization-team-invitations teams-array emails-array]) nil)))) @@ -917,8 +917,8 @@ RETURNING id, deleted_at;") ::sm/result schema:get-teams-detail-result ::nitrate/sso false} [cfg {:keys [organization-id]}] - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id}) - team-ids (into [] (comp d/xf:map-id (filter uuid?)) (:teams org-summary))] + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id}) + team-ids (into [] (comp d/xf:map-id (filter uuid?)) (:teams organization-summary))] (if (empty? team-ids) [] (db/run! cfg @@ -958,8 +958,8 @@ RETURNING id, deleted_at;") ::rpc/auth false} [{:keys [::db/pool] :as cfg} {:keys [organization-id updated-props announce-activation]}] (when updated-props - (rpc/invalidate-org-sso-cache-by-org! organization-id) - (session/clear-org-sso-sessions! pool organization-id)) + (rpc/invalidate-organization-sso-cache-by-organization! organization-id) + (session/clear-organization-sso-sessions! pool organization-id)) (notifications/notify-organization-change-sso cfg organization-id) (when announce-activation (neh/send-organization-setup-sso-emails! cfg organization-id)) diff --git a/backend/src/app/rpc/nitrate/emails_helper.clj b/backend/src/app/rpc/nitrate/emails_helper.clj index d0e506d53c..3a2286f3f5 100644 --- a/backend/src/app/rpc/nitrate/emails_helper.clj +++ b/backend/src/app/rpc/nitrate/emails_helper.clj @@ -28,11 +28,11 @@ WHERE email = ANY(?) AND deleted_at IS NULL") -(defn- org-sso-active? +(defn- organization-sso-active? "Return whether SSO is enabled for the organization." [cfg organization-id] (when (contains? cf/flags :nitrate) - (true? (:active (nitrate/call cfg :get-org-sso {:organization-id organization-id}))))) + (true? (:active (nitrate/call cfg :get-organization-sso {:organization-id organization-id}))))) (def ^:private xf:map-email (map :email)) @@ -61,44 +61,44 @@ :to email :organization-name organization-name}))) -(defn- get-org-sso-notify-recipients - "Unique org members and pending org/team invitees for SSO activation emails." - [conn cfg organization-id org-summary] - (let [member-ids (nitrate/call cfg :get-org-members {:organization-id organization-id}) - team-ids (neh/get-org-team-ids org-summary) +(defn- get-organization-sso-notify-recipients + "Unique organization members and pending organization/team invitees for SSO activation emails." + [conn cfg organization-id organization-summary] + (let [member-ids (nitrate/call cfg :get-organization-members {:organization-id organization-id}) + team-ids (neh/get-organization-team-ids organization-summary) member-emails (if (seq member-ids) (let [ids-array (db/create-array conn "uuid" member-ids)] (into #{} (map :email (db/exec! conn [sql:get-profile-emails-by-ids ids-array])))) #{}) invite-emails (into #{} (map :email - (neh/get-org-invitations conn organization-id team-ids))) + (neh/get-organization-invitations conn organization-id team-ids))) emails (into #{} (concat member-emails invite-emails))] (recipients-by-emails conn emails))) (defn- get-team-sso-notify-recipients - "Team members who are not in `org-member-ids`, plus pending team invitations." - [conn team-id org-member-ids] + "Team members who are not in `organization-member-ids`, plus pending team invitations." + [conn team-id organization-member-ids] (let [team-members (->> (teams/get-team-members conn team-id) - (remove #(contains? org-member-ids (:id %)))) + (remove #(contains? organization-member-ids (:id %)))) invitations (neh/get-team-invitation-emails conn team-id)] (->> (sequence xf:map-email (concat team-members invitations)) (recipients-by-emails conn)))) (defn send-organization-setup-sso-emails! - "Notify all org members and pending org/team invitees that SSO is active." + "Notify all organization members and pending organization/team invitees that SSO is active." [cfg organization-id] - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id})] + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id})] (db/tx-run! cfg (fn [{:keys [::db/conn]}] - (doseq [recipient (get-org-sso-notify-recipients conn cfg organization-id org-summary)] - (send-organization-setup-sso-email! conn (:name org-summary) recipient)))))) + (doseq [recipient (get-organization-sso-notify-recipients conn cfg organization-id organization-summary)] + (send-organization-setup-sso-email! conn (:name organization-summary) recipient)))))) (defn send-organization-setup-sso-emails-for-team! - "Notify team members who are not in `org-member-ids-before` and pending team invitees." - [cfg organization-id team-id org-member-ids-before] - (when (org-sso-active? cfg organization-id) - (let [org-summary (nitrate/call cfg :get-org-summary {:organization-id organization-id})] + "Notify team members who are not in `organization-member-ids-before` and pending team invitees." + [cfg organization-id team-id organization-member-ids-before] + (when (organization-sso-active? cfg organization-id) + (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id})] (db/tx-run! cfg (fn [{:keys [::db/conn]}] - (doseq [recipient (get-team-sso-notify-recipients conn team-id org-member-ids-before)] - (send-organization-setup-sso-email! conn (:name org-summary) recipient))))))) + (doseq [recipient (get-team-sso-notify-recipients conn team-id organization-member-ids-before)] + (send-organization-setup-sso-email! conn (:name organization-summary) recipient))))))) diff --git a/backend/src/app/rpc/nitrate/organization_helper.clj b/backend/src/app/rpc/nitrate/organization_helper.clj index 8627a4d10d..ed5d918e8b 100644 --- a/backend/src/app/rpc/nitrate/organization_helper.clj +++ b/backend/src/app/rpc/nitrate/organization_helper.clj @@ -10,7 +10,7 @@ [app.db :as db] [app.nitrate :as nitrate])) -(def ^:private sql:get-org-invitations +(def ^:private sql:get-organization-invitations "SELECT DISTINCT ON (email_to) ti.id, ti.org_id AS organization_id, @@ -35,24 +35,24 @@ LEFT JOIN profile AS p AND ti.valid_until >= now() ORDER BY ti.email_to, ti.valid_until DESC, ti.created_at DESC;") -(defn get-org-team-ids +(defn get-organization-team-ids "Return team ids for an organization. - Accepts either `cfg` and `organization-id` (fetches the org summary from - Nitrate) or an already-resolved org summary map." + Accepts either `cfg` and `organization-id` (fetches the organization summary from + Nitrate) or an already-resolved organization summary map." ([cfg organization-id] - (get-org-team-ids (nitrate/call cfg :get-org-summary {:organization-id organization-id}))) - ([org-summary] - (->> (:teams org-summary) + (get-organization-team-ids (nitrate/call cfg :get-organization-summary {:organization-id organization-id}))) + ([organization-summary] + (->> (:teams organization-summary) (map :id) (filter uuid?) (vec)))) -(defn get-org-invitations - "Fetch valid org-level and team-level invitations for an organization." +(defn get-organization-invitations + "Fetch valid organization-level and team-level invitations for an organization." [conn organization-id team-ids] (let [ids-array (db/create-array conn "uuid" team-ids)] - (db/exec! conn [sql:get-org-invitations organization-id ids-array]))) + (db/exec! conn [sql:get-organization-invitations organization-id ids-array]))) (defn get-team-invitation-emails "Return distinct valid team invitation recipient emails." diff --git a/backend/src/app/rpc/notifications.clj b/backend/src/app/rpc/notifications.clj index d2410092aa..ec3d7c89d9 100644 --- a/backend/src/app/rpc/notifications.clj +++ b/backend/src/app/rpc/notifications.clj @@ -16,17 +16,17 @@ ;;TODO There is a bug on dashboard with teams notifications. ;;For now we send it to uuid/zero instead of team-id :topic uuid/zero - :message {:type :team-org-change + :message {:type :team-organization-change :team team :notification notification}))) -(defn notify-user-org-change +(defn notify-user-organization-change [cfg profile-id organization-id organization-name notification] (let [msgbus (::mbus/msgbus cfg)] (mbus/pub! msgbus :topic profile-id - :message {:type :user-org-change + :message {:type :user-organization-change :topic profile-id :organization-id organization-id :organization-name organization-name diff --git a/backend/test/backend_tests/email_sending_test.clj b/backend/test/backend_tests/email_sending_test.clj index e96fd192ae..91d2848185 100644 --- a/backend/test/backend_tests/email_sending_test.clj +++ b/backend/test/backend_tests/email_sending_test.clj @@ -42,15 +42,15 @@ :token "test-token" :organization organization}) -(t/deftest invite-to-org-includes-sso-notice-when-active - (let [result (emails/render emails/invite-to-org +(t/deftest invite-to-organization-includes-sso-notice-when-active + (let [result (emails/render emails/invite-to-organization (invite-email-params {:name "Acme Inc" :sso-active true}))] (t/is (str/includes? (email-text-body result) sso-notice-snippet)) (t/is (str/includes? (get-in result [:body "text/html"]) sso-notice-snippet)))) -(t/deftest invite-to-org-omits-sso-notice-when-inactive - (let [result (emails/render emails/invite-to-org +(t/deftest invite-to-organization-omits-sso-notice-when-inactive + (let [result (emails/render emails/invite-to-organization (invite-email-params {:name "Acme Inc" :sso-active false}))] (t/is (not (str/includes? (email-text-body result) sso-notice-snippet))) diff --git a/backend/test/backend_tests/rpc_management_nitrate_test.clj b/backend/test/backend_tests/rpc_management_nitrate_test.clj index b2029c1e60..49a6605324 100644 --- a/backend/test/backend_tests/rpc_management_nitrate_test.clj +++ b/backend/test/backend_tests/rpc_management_nitrate_test.clj @@ -142,7 +142,7 @@ (t/is (= 1 (count @calls))) (t/is (= uuid/zero (-> @calls first :topic))) (let [msg (-> @calls first :message)] - (t/is (= :team-org-change (:type msg))) + (t/is (= :team-organization-change (:type msg))) (t/is (= nil (:notification msg))) (t/is (= team-id (-> msg :team :id))) (t/is (= false (-> msg :team :is-your-penpot))) @@ -152,11 +152,11 @@ (t/is (= (:owner-id organization) (-> msg :team :organization :owner-id))) (t/is (= (:avatar-bg-url organization) (str (-> msg :team :organization :avatar-bg-url))))))) -(t/deftest notify-user-added-to-organization-creates-default-org-team +(t/deftest notify-user-added-to-organization-creates-default-organization-team (with-mocks [nitrate-mock {:target 'app.nitrate/call :return (fn [_ m _] (case m - :set-team-org {:success true} + :set-team-organization {:success true} nil))}] (let [profile (th/create-profile* 1 {:is-active true}) before-teams (->> (th/db-query :team-profile-rel {:profile-id (:id profile) @@ -224,18 +224,18 @@ (->> out :result :teams (map :id) set)))))) (t/deftest get-teams-detail-last-activity-reflects-file-modifications - (let [org-summary-ref (atom nil)] + (let [organization-summary-ref (atom nil)] (with-mocks [nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary @org-summary-ref + :get-organization-summary @organization-summary-ref nil))}] (let [profile (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id profile)}) organization-id (uuid/random) - org-summary {:id organization-id - :teams [{:id (:id team)}]} - _ (reset! org-summary-ref org-summary) + organization-summary {:id organization-id + :teams [{:id (:id team)}]} + _ (reset! organization-summary-ref organization-summary) params {::th/type :get-teams-detail ::rpc/profile-id (:id profile) :organization-id organization-id} @@ -280,16 +280,16 @@ (t/is (= (:modified-at file-after-update) (:last-activity-at updated-team))) (t/is (not= (:last-activity-at with-file) (:last-activity-at updated-team))))))) -(t/deftest notify-organization-deletion-prefixes-teams-and-publishes-org-deleted-event - ;; --- Deferred org-summary: nil during setup, filled before RPC --- - (let [org-summary-ref (atom nil)] +(t/deftest notify-organization-deletion-prefixes-teams-and-publishes-organization-deleted-event + ;; --- Deferred organization-summary: nil during setup, filled before RPC --- + (let [organization-summary-ref (atom nil)] (with-mocks [;; --- Nitrate mock: return nil during profile/team creation, - ;; then serve the computed org-summary once available --- + ;; then serve the computed organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method params] - (if @org-summary-ref - @org-summary-ref + (if @organization-summary-ref + @organization-summary-ref nil))} ;; --- Worker mock: capture delete-task submission --- wrk-mock {:target 'app.worker/submit! :return nil} @@ -312,14 +312,14 @@ organization-name "Acme / Design" expected-start (str "[" (d/sanitize-string organization-name) "] ") ;; --- Org-summary that nitrate would return --- - org-summary {:id organization-id - :name organization-name - :teams [{:id (:id team-with-files) - :is-your-penpot true} - {:id (:id empty-team) - :is-your-penpot true}]} - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary {:id organization-id + :name organization-name + :teams [{:id (:id team-with-files) + :is-your-penpot true} + {:id (:id empty-team) + :is-your-penpot true}]} + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: notify Penpot that an organization is deleted --- out (th/management-command! {::th/type :notify-organization-deletion @@ -330,9 +330,9 @@ updated-with-files (th/db-get :team {:id (:id team-with-files)} {::db/remove-deleted false}) updated-empty (th/db-get :team {:id (:id empty-team)} {::db/remove-deleted false})] - ;; --- Verify: nitrate was queried for the org summary --- + ;; --- Verify: nitrate was queried for the organization summary --- (let [[_ method params] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) + (t/is (= :get-organization-summary method)) (t/is (= {:organization-id organization-id} params))) ;; --- Verify: RPC returns success with no result payload --- @@ -361,132 +361,132 @@ (t/is (= #{(:id empty-team)} (set (:deleted-teams (:message msg)))))))))) -(t/deftest notify-user-organizations-deletion-renames-or-deletes-teams-and-publishes-per-org-events - ;; --- Deferred owned-orgs: nil during setup, filled before RPC --- - (let [owned-orgs-ref (atom nil)] +(t/deftest notify-user-organizations-deletion-renames-or-deletes-teams-and-publishes-per-organization-events + ;; --- Deferred owned-organizations: nil during setup, filled before RPC --- + (let [owned-organizations-ref (atom nil)] (with-mocks [;; --- Nitrate mock: return nil during profile/team creation, - ;; then serve the computed owned-orgs once available --- + ;; then serve the computed owned-organizations once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-owned-orgs @owned-orgs-ref + :get-owned-organizations @owned-organizations-ref nil))} ;; --- Worker mock: capture delete-task submissions --- wrk-mock {:target 'app.worker/submit! :return nil} ;; --- Message bus mock: capture published events --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create a profile with teams across two orgs --- + ;; --- Setup: create a profile with teams across two organizations --- (let [profile (th/create-profile* 1 {:is-active true}) ;; --- Org-1: team with files: kept and renamed --- - org-1-team-files (th/db-get :team {:id (:default-team-id profile)}) - org-1-proj (th/create-project* 1 {:profile-id (:id profile) - :team-id (:id org-1-team-files)}) + organization-1-team-files (th/db-get :team {:id (:default-team-id profile)}) + organization-1-proj (th/create-project* 1 {:profile-id (:id profile) + :team-id (:id organization-1-team-files)}) _ (th/create-file* 1 {:profile-id (:id profile) - :project-id (:id org-1-proj)}) + :project-id (:id organization-1-proj)}) ;; --- Org-1: empty team: soft-deleted --- - org-1-team-empty (th/create-team* 1 {:profile-id (:id profile)}) + organization-1-team-empty (th/create-team* 1 {:profile-id (:id profile)}) ;; --- Org-2: team with files: kept and renamed --- - org-2-team-files (th/create-team* 2 {:profile-id (:id profile)}) - org-2-proj (th/create-project* 2 {:profile-id (:id profile) - :team-id (:id org-2-team-files)}) + organization-2-team-files (th/create-team* 2 {:profile-id (:id profile)}) + organization-2-proj (th/create-project* 2 {:profile-id (:id profile) + :team-id (:id organization-2-team-files)}) _ (th/create-file* 2 {:profile-id (:id profile) - :project-id (:id org-2-proj)}) + :project-id (:id organization-2-proj)}) ;; --- Org-2: empty team: soft-deleted --- - org-2-team-empty (th/create-team* 3 {:profile-id (:id profile)}) + organization-2-team-empty (th/create-team* 3 {:profile-id (:id profile)}) ;; --- Data needed by the RPC call --- - org-1-id (uuid/random) - org-2-id (uuid/random) - org-1-name "Org One / Design" - org-2-name "Org Two" - org-1-prefix (str "[" (d/sanitize-string org-1-name) "] ") - org-2-prefix (str "[" (d/sanitize-string org-2-name) "] ") - ;; --- Owned-orgs that nitrate would return --- - owned-orgs [{:id org-1-id - :name org-1-name - :teams [{:id (:id org-1-team-files) - :is-your-penpot true} - {:id (:id org-1-team-empty) - :is-your-penpot true}]} - {:id org-2-id - :name org-2-name - :teams [{:id (:id org-2-team-files) - :is-your-penpot true} - {:id (:id org-2-team-empty) - :is-your-penpot true}]}] - ;; --- Publish owned-orgs so the mock can serve it --- - _ (reset! owned-orgs-ref owned-orgs) + organization-1-id (uuid/random) + organization-2-id (uuid/random) + organization-1-name "Org One / Design" + organization-2-name "Org Two" + organization-1-prefix (str "[" (d/sanitize-string organization-1-name) "] ") + organization-2-prefix (str "[" (d/sanitize-string organization-2-name) "] ") + ;; --- Owned-organizations that nitrate would return --- + owned-organizations [{:id organization-1-id + :name organization-1-name + :teams [{:id (:id organization-1-team-files) + :is-your-penpot true} + {:id (:id organization-1-team-empty) + :is-your-penpot true}]} + {:id organization-2-id + :name organization-2-name + :teams [{:id (:id organization-2-team-files) + :is-your-penpot true} + {:id (:id organization-2-team-empty) + :is-your-penpot true}]}] + ;; --- Publish owned-organizations so the mock can serve it --- + _ (reset! owned-organizations-ref owned-organizations) - ;; --- Exercise: notify Penpot that the user's orgs are deleted --- + ;; --- Exercise: notify Penpot that the user's organizations are deleted --- out (th/management-command! {::th/type :notify-user-organizations-deletion ::rpc/profile-id (:id profile) :profile-id (:id profile)}) ;; --- Fetch teams post-deletion to verify mutations --- - org-1-updated-files (th/db-get :team {:id (:id org-1-team-files)} {::db/remove-deleted false}) - org-1-updated-empty (th/db-get :team {:id (:id org-1-team-empty)} {::db/remove-deleted false}) - org-2-updated-files (th/db-get :team {:id (:id org-2-team-files)} {::db/remove-deleted false}) - org-2-updated-empty (th/db-get :team {:id (:id org-2-team-empty)} {::db/remove-deleted false}) + organization-1-updated-files (th/db-get :team {:id (:id organization-1-team-files)} {::db/remove-deleted false}) + organization-1-updated-empty (th/db-get :team {:id (:id organization-1-team-empty)} {::db/remove-deleted false}) + organization-2-updated-files (th/db-get :team {:id (:id organization-2-team-files)} {::db/remove-deleted false}) + organization-2-updated-empty (th/db-get :team {:id (:id organization-2-team-empty)} {::db/remove-deleted false}) ;; --- Extract published messages from the message bus mock --- msgs (->> (:call-args-list @mbus-mock) (map #(apply hash-map (rest %))) (map :message) vec) - org-msg (fn [org-name] - (first (filter #(= org-name (:organization-name %)) msgs)))] + organization-msg (fn [organization-name] + (first (filter #(= organization-name (:organization-name %)) msgs)))] - ;; --- Verify: nitrate was queried for owned orgs with correct params --- + ;; --- Verify: nitrate was queried for owned organizations with correct params --- (let [[_ method params] (:call-args @nitrate-mock)] - (t/is (= :get-owned-orgs method)) + (t/is (= :get-owned-organizations method)) (t/is (= {:profile-id (:id profile)} params))) ;; --- Verify: RPC returns success with no result payload --- (t/is (th/success? out)) (t/is (nil? (:result out))) - ;; --- Verify: org-1 team with files kept, renamed, default flag removed --- - (t/is (false? (:is-default org-1-updated-files))) - (t/is (str/starts-with? (:name org-1-updated-files) org-1-prefix)) - (t/is (nil? (:deleted-at org-1-updated-files))) - ;; --- Verify: org-1 empty team soft-deleted --- - (t/is (some? (:deleted-at org-1-updated-empty))) + ;; --- Verify: organization-1 team with files kept, renamed, default flag removed --- + (t/is (false? (:is-default organization-1-updated-files))) + (t/is (str/starts-with? (:name organization-1-updated-files) organization-1-prefix)) + (t/is (nil? (:deleted-at organization-1-updated-files))) + ;; --- Verify: organization-1 empty team soft-deleted --- + (t/is (some? (:deleted-at organization-1-updated-empty))) - ;; --- Verify: org-2 team with files kept, renamed, default flag removed --- - (t/is (false? (:is-default org-2-updated-files))) - (t/is (str/starts-with? (:name org-2-updated-files) org-2-prefix)) - (t/is (nil? (:deleted-at org-2-updated-files))) - ;; --- Verify: org-2 empty team soft-deleted --- - (t/is (some? (:deleted-at org-2-updated-empty))) + ;; --- Verify: organization-2 team with files kept, renamed, default flag removed --- + (t/is (false? (:is-default organization-2-updated-files))) + (t/is (str/starts-with? (:name organization-2-updated-files) organization-2-prefix)) + (t/is (nil? (:deleted-at organization-2-updated-files))) + ;; --- Verify: organization-2 empty team soft-deleted --- + (t/is (some? (:deleted-at organization-2-updated-empty))) ;; --- Verify: two delete tasks submitted (one per empty team) --- (t/is (:called? @wrk-mock)) (t/is (= 2 (:call-count @wrk-mock))) - ;; --- Verify: one organization-deleted event per org, all on correct topic --- + ;; --- Verify: one organization-deleted event per organization, all on correct topic --- (t/is (= 2 (count msgs))) (t/is (every? #(= uuid/zero (:topic %)) (->> (:call-args-list @mbus-mock) (map #(apply hash-map (rest %)))))) (t/is (= #{:organization-deleted} (set (map :type msgs)))) - ;; --- Verify: each org-deleted event has correct org-specific payload --- - (let [m1 (org-msg org-1-name) - m2 (org-msg org-2-name)] + ;; --- Verify: each organization-deleted event has correct organization-specific payload --- + (let [m1 (organization-msg organization-1-name) + m2 (organization-msg organization-2-name)] (t/is (some? m1)) (t/is (some? m2)) - (t/is (= org-1-id (:organization-id m1))) - (t/is (= org-2-id (:organization-id m2))) - (t/is (= #{(:id org-1-team-files) (:id org-1-team-empty)} + (t/is (= organization-1-id (:organization-id m1))) + (t/is (= organization-2-id (:organization-id m2))) + (t/is (= #{(:id organization-1-team-files) (:id organization-1-team-empty)} (set (:teams m1)))) - (t/is (= #{(:id org-1-team-empty)} + (t/is (= #{(:id organization-1-team-empty)} (set (:deleted-teams m1)))) - (t/is (= #{(:id org-2-team-files) (:id org-2-team-empty)} + (t/is (= #{(:id organization-2-team-files) (:id organization-2-team-empty)} (set (:teams m2)))) - (t/is (= #{(:id org-2-team-empty)} + (t/is (= #{(:id organization-2-team-empty)} (set (:deleted-teams m2))))))))) (t/deftest get-profile-by-email-success-and-not-found @@ -528,15 +528,15 @@ (t/is (= :profile-not-found (th/ex-code (:error ko-out))))))) (t/deftest get-organization-invitations-returns-valid-deduped-by-email - ;; --- Deferred org-summary: nil during setup, filled before RPC --- - (let [org-summary-ref (atom nil)] + ;; --- Deferred organization-summary: nil during setup, filled before RPC --- + (let [organization-summary-ref (atom nil)] (with-mocks [;; --- Nitrate mock: return nil during profile/team creation, - ;; then serve the computed org-summary when the handler queries it --- + ;; then serve the computed organization-summary when the handler queries it --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary @org-summary-ref + :get-organization-summary @organization-summary-ref nil))}] ;; --- Setup: create profile, teams, and invitation data --- @@ -545,21 +545,21 @@ team-2 (th/create-team* 2 {:profile-id (:id profile)}) ;; --- Data needed by the RPC call --- - org-id (uuid/random) - org-summary {:id org-id - :teams [{:id (:id team-1)} - {:id (:id team-2)}]} + organization-id (uuid/random) + organization-summary {:id organization-id + :teams [{:id (:id team-1)} + {:id (:id team-2)}]} params {::th/type :get-organization-invitations ::rpc/profile-id (:id profile) - :organization-id org-id} - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + :organization-id organization-id} + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Insert invitation records --- - ;; Same email appears in org and team invitations; only one should be returned. + ;; Same email appears in organization and team invitations; only one should be returned. _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "dup@example.com" :created-by (:id profile) @@ -584,14 +584,14 @@ ;; Expired invitation should be ignored. _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "expired@example.com" :created-by (:id profile) :role "editor" :valid-until (ct/in-past "1h")}) - ;; --- Exercise: query org invitations --- + ;; --- Exercise: query organization invitations --- out (th/management-command! params) ;; --- Extract results --- @@ -601,10 +601,10 @@ (filter #(= "dup@example.com" (:email %))) first)] - ;; --- Verify: nitrate was queried for the org summary --- + ;; --- Verify: nitrate was queried for the organization summary --- (let [[_ method params'] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) - (t/is (= {:organization-id org-id} params'))) + (t/is (= :get-organization-summary method)) + (t/is (= {:organization-id organization-id} params'))) ;; --- Verify: RPC returns success with deduplicated invitations --- (t/is (th/success? out)) @@ -618,56 +618,56 @@ (t/is (nil? (:role dedup))) (t/is (nil? (:valid-until dedup))))))) -(t/deftest get-organization-invitations-includes-org-level-invitations-when-no-teams +(t/deftest get-organization-invitations-includes-organization-level-invitations-when-no-teams ;; --- Org-summary has no teams — computable before with-mocks, no deferral needed --- - (let [org-id (uuid/random) - org-summary {:id org-id :teams []} + (let [organization-id (uuid/random) + organization-summary {:id organization-id :teams []} params {::th/type :get-organization-invitations - :organization-id org-id}] + :organization-id organization-id}] (with-mocks - [;; --- Nitrate mock: return the org-summary when the handler queries it --- + [;; --- Nitrate mock: return the organization-summary when the handler queries it --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary org-summary + :get-organization-summary organization-summary nil))}] - ;; --- Setup: create profile and insert an org-level invitation --- + ;; --- Setup: create profile and insert an organization-level invitation --- (let [profile (th/create-profile* 1 {:is-active true}) _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil - :email-to "org-only@example.com" + :email-to "organization-only@example.com" :created-by (:id profile) :role "editor" :valid-until (ct/in-future "24h")}) - ;; --- Exercise: query org invitations --- + ;; --- Exercise: query organization invitations --- out (th/management-command! (assoc params ::rpc/profile-id (:id profile))) result (:result out)] - ;; --- Verify: nitrate was queried for the org summary --- + ;; --- Verify: nitrate was queried for the organization summary --- (let [[_ method params'] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) - (t/is (= {:organization-id org-id} params'))) + (t/is (= :get-organization-summary method)) + (t/is (= {:organization-id organization-id} params'))) - ;; --- Verify: the org-level invitation is returned --- + ;; --- Verify: the organization-level invitation is returned --- (t/is (th/success? out)) (t/is (= 1 (count result))) - (t/is (= "org-only@example.com" (-> result first :email))) + (t/is (= "organization-only@example.com" (-> result first :email))) (t/is (some? (-> result first :sent-at))))))) (t/deftest get-organization-invitations-returns-existing-profile-data ;; --- Org-summary has no teams — computable before with-mocks, no deferral needed --- - (let [org-id (uuid/random) - org-summary {:id org-id :teams []}] + (let [organization-id (uuid/random) + organization-summary {:id organization-id :teams []}] (with-mocks - [;; --- Nitrate mock: return the org-summary when the handler queries it --- + [;; --- Nitrate mock: return the organization-summary when the handler queries it --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary org-summary + :get-organization-summary organization-summary nil))}] ;; --- Setup: create profiles, set photo on invited user, insert invitation --- @@ -680,23 +680,23 @@ _ (th/db-update! :profile {:photo-id photo-id} {:id (:id invited)}) _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to (:email invited) :created-by (:id profile) :role "editor" :valid-until (ct/in-future "24h")}) - ;; --- Exercise: query org invitations --- + ;; --- Exercise: query organization invitations --- out (th/management-command! {::th/type :get-organization-invitations ::rpc/profile-id (:id profile) - :organization-id org-id}) + :organization-id organization-id}) invitation (-> out :result first)] - ;; --- Verify: nitrate was queried for the org summary --- + ;; --- Verify: nitrate was queried for the organization summary --- (let [[_ method params'] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) - (t/is (= {:organization-id org-id} params'))) + (t/is (= :get-organization-summary method)) + (t/is (= {:organization-id organization-id} params'))) ;; --- Verify: invitation includes the invited user's existing profile data --- (t/is (th/success? out)) @@ -705,16 +705,16 @@ (t/is (str/ends-with? (:photo-url invitation) (str "/assets/by-id/" photo-id))))))) -(t/deftest delete-organization-invitations-removes-org-and-org-team-invitations-for-email - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil) +(t/deftest delete-organization-invitations-removes-organization-and-organization-team-invitations-for-email + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil) target-email "target@example.com"] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary when handler queries it --- + [;; --- Nitrate mock: nil during setup, serve organization-summary when handler queries it --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary @org-summary-ref + :get-organization-summary @organization-summary-ref nil))}] ;; --- Setup: create profile, teams, and invitation data --- @@ -722,24 +722,24 @@ team-1 (th/create-team* 1 {:profile-id (:id profile)}) team-2 (th/create-team* 2 {:profile-id (:id profile)}) outside-team (th/create-team* 3 {:profile-id (:id profile)}) - org-id (uuid/random) - org-summary {:id org-id - :teams [{:id (:id team-1)} - {:id (:id team-2)}]} - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-id (uuid/random) + organization-summary {:id organization-id + :teams [{:id (:id team-1)} + {:id (:id team-2)}]} + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Insert invitation records --- - ;; Should be deleted: org-level invitation for same org+email. + ;; Should be deleted: organization-level invitation for same organization+email. _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to target-email :created-by (:id profile) :role "editor" :valid-until (ct/in-future "24h")}) - ;; Should be deleted: team-level invitation for teams in org summary. + ;; Should be deleted: team-level invitation for teams in organization summary. _ (th/db-insert! :team-invitation {:id (uuid/random) :team-id (:id team-1) @@ -757,7 +757,7 @@ :created-by (:id profile) :role "editor" :valid-until (ct/in-future "24h")}) - ;; Should remain: same email but outside org scope. + ;; Should remain: same email but outside organization scope. _ (th/db-insert! :team-invitation {:id (uuid/random) :team-id (:id outside-team) @@ -767,18 +767,18 @@ :role "editor" :valid-until (ct/in-future "24h")}) - ;; --- Exercise: delete org invitations for target email --- + ;; --- Exercise: delete organization invitations for target email --- out (th/management-command! {::th/type :delete-organization-invitations ::rpc/profile-id (:id profile) - :organization-id org-id + :organization-id organization-id :email "TARGET@example.com"}) remaining-target (th/db-query :team-invitation {:email-to target-email}) remaining-other (th/db-query :team-invitation {:email-to "other@example.com"})] - ;; --- Verify: nitrate was queried for the org summary --- + ;; --- Verify: nitrate was queried for the organization summary --- (let [[_ method params'] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) - (t/is (= {:organization-id org-id} params'))) + (t/is (= :get-organization-summary method)) + (t/is (= {:organization-id organization-id} params'))) ;; --- Verify: RPC returns success with no result payload --- (t/is (th/success? out)) @@ -789,15 +789,15 @@ ;; --- Verify: other-email invitation is untouched --- (t/is (= 1 (count remaining-other))))))) -(t/deftest delete-all-organization-invitations-removes-org-and-org-team-invitations - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] +(t/deftest delete-all-organization-invitations-removes-organization-and-organization-team-invitations + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary when handler queries it --- + [;; --- Nitrate mock: nil during setup, serve organization-summary when handler queries it --- nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary @org-summary-ref + :get-organization-summary @organization-summary-ref nil))}] ;; --- Setup: create profile, teams, and invitation data --- @@ -805,24 +805,24 @@ team-1 (th/create-team* 1 {:profile-id (:id profile)}) team-2 (th/create-team* 2 {:profile-id (:id profile)}) outside-team (th/create-team* 3 {:profile-id (:id profile)}) - org-id (uuid/random) - org-summary {:id org-id - :teams [{:id (:id team-1)} - {:id (:id team-2)}]} - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-id (uuid/random) + organization-summary {:id organization-id + :teams [{:id (:id team-1)} + {:id (:id team-2)}]} + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Insert invitation records --- - ;; Should be deleted: org-level invitation. + ;; Should be deleted: organization-level invitation. _ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "alice@example.com" :created-by (:id profile) :role "editor" :valid-until (ct/in-future "24h")}) - ;; Should be deleted: team-level invitation in team-1 (belongs to org). + ;; Should be deleted: team-level invitation in team-1 (belongs to organization). _ (th/db-insert! :team-invitation {:id (uuid/random) :team-id (:id team-1) @@ -831,7 +831,7 @@ :created-by (:id profile) :role "admin" :valid-until (ct/in-future "48h")}) - ;; Should be deleted: team-level invitation in team-2 (belongs to org), + ;; Should be deleted: team-level invitation in team-2 (belongs to organization), ;; even if expired. _ (th/db-insert! :team-invitation {:id (uuid/random) @@ -860,41 +860,41 @@ :role "editor" :valid-until (ct/in-future "24h")}) - ;; --- Exercise: delete all invitations in the org --- + ;; --- Exercise: delete all invitations in the organization --- out (th/management-command! {::th/type :delete-all-organization-invitations ::rpc/profile-id (:id profile) - :organization-id org-id}) + :organization-id organization-id}) present? (fn [email] (seq (th/db-query :team-invitation {:email-to email})))] - ;; --- Verify: the handler's nitrate call was :get-org-summary with correct params --- + ;; --- Verify: the handler's nitrate call was :get-organization-summary with correct params --- ;; (The mock also recorded setup-phase calls from add-profile-to-team!, ;; so :call-args reflects the LAST call — which is the handler's.) (let [[_ method params'] (:call-args @nitrate-mock)] - (t/is (= :get-org-summary method)) - (t/is (= {:organization-id org-id} params'))) + (t/is (= :get-organization-summary method)) + (t/is (= {:organization-id organization-id} params'))) ;; --- Verify: RPC returns success with no result payload --- (t/is (th/success? out)) (t/is (nil? (:result out))) - ;; --- Verify: org-level + team-in-org invitations are deleted --- + ;; --- Verify: organization-level + team-in-organization invitations are deleted --- (t/is (not (present? "alice@example.com"))) (t/is (not (present? "bob@example.com"))) (t/is (not (present? "carol@example.com"))) - ;; --- Verify: invitations outside the org survive --- + ;; --- Verify: invitations outside the organization survive --- (t/is (present? "dan@example.com")) (t/is (present? "erin@example.com")))))) -(t/deftest delete-all-organization-invitations-handles-org-with-no-teams - (let [org-id (uuid/random) +(t/deftest delete-all-organization-invitations-handles-organization-with-no-teams + (let [organization-id (uuid/random) params {::th/type :delete-all-organization-invitations - :organization-id org-id}] + :organization-id organization-id}] (with-mocks [audit-mock {:target 'app.loggers.audit/submit :return nil} nitrate-fn {:target 'app.nitrate/call :return (fn [_cfg method _params] (case method - :get-org-summary {:id org-id :teams []} + :get-organization-summary {:id organization-id :teams []} nil))}] (let [profile (th/create-profile* 1 {:is-active true})] @@ -902,7 +902,7 @@ ;; Org-level invitation should still be deleted. (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "alice@example.com" :created-by (:id profile) @@ -910,7 +910,7 @@ :valid-until (ct/in-future "24h")}) (let [out (th/management-command! params) - remaining (th/db-query :team-invitation {:org-id org-id})] + remaining (th/db-query :team-invitation {:org-id organization-id})] (t/is (th/success? out)) (t/is (nil? (:result out))) (t/is (empty? remaining))))))) @@ -922,10 +922,10 @@ team-1 (th/create-team* 1 {:profile-id (:id profile)}) team-2 (th/create-team* 2 {:profile-id (:id profile)}) outside-team (th/create-team* 3 {:profile-id (:id profile)}) - org-id (uuid/random) + organization-id (uuid/random) base-params {::th/type :exists-organization-team-invitations-for-non-members ::rpc/profile-id (:id profile) - :organization-id org-id + :organization-id organization-id :team-ids [(:id team-1) (:id team-2)] :member-ids [(:id member1)]} exist! (fn [] (-> (th/management-command! base-params) @@ -946,7 +946,7 @@ (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "pending@example.com" :created-by (:id profile) @@ -981,14 +981,14 @@ team-1 (th/create-team* 1 {:profile-id (:id profile)}) team-2 (th/create-team* 2 {:profile-id (:id profile)}) outside-team (th/create-team* 3 {:profile-id (:id profile)}) - org-id (uuid/random) + organization-id (uuid/random) params {::th/type :delete-organization-team-invitations-for-non-members ::rpc/profile-id (:id profile) - :organization-id org-id + :organization-id organization-id :team-ids [(:id team-1) (:id team-2)] :member-ids [(:id member1)]}] - ;; Should remain: member1 is an org member. + ;; Should remain: member1 is an organization member. (th/db-insert! :team-invitation {:id (uuid/random) :team-id (:id team-1) @@ -1001,7 +1001,7 @@ ;; Org-level invitation remains (out of team cleanup scope). (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to "pending@example.com" :created-by (:id profile) @@ -1038,7 +1038,7 @@ :role "editor" :valid-until (ct/in-past "1h")}) - ;; Should remain: outside org scope. + ;; Should remain: outside organization scope. (th/db-insert! :team-invitation {:id (uuid/random) :team-id (:id outside-team) @@ -1064,69 +1064,69 @@ ;; Tests: remove-from-organization ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- make-org-summary - [& {:keys [organization-id organization-name owner-id your-penpot-teams org-teams] - :or {your-penpot-teams [] org-teams []}}] +(defn- make-organization-summary + [& {:keys [organization-id organization-name owner-id your-penpot-teams organization-teams] + :or {your-penpot-teams [] organization-teams []}}] {:id organization-id :name organization-name :owner-id owner-id :teams (into (mapv (fn [id] {:id id :is-your-penpot true}) your-penpot-teams) - (mapv (fn [id] {:id id :is-your-penpot false}) org-teams))}) + (mapv (fn [id] {:id id :is-your-penpot false}) organization-teams))}) (defn- nitrate-call-mock - ([org-summary] - (nitrate-call-mock org-summary nil)) - ([org-summary remove-profile-params] + ([organization-summary] + (nitrate-call-mock organization-summary nil)) + ([organization-summary remove-profile-params] (fn [_cfg method params] (case method - :get-org-summary org-summary - :get-org-membership {:organization-id (:id org-summary) - :is-member true} - :remove-profile-from-org (when remove-profile-params - (reset! remove-profile-params params)) + :get-organization-summary organization-summary + :get-organization-membership {:organization-id (:id organization-summary) + :is-member true} + :remove-profile-from-organization (when remove-profile-params + (reset! remove-profile-params params)) nil)))) (t/deftest remove-from-organization-happy-path-no-extra-teams ;; User is only in its default team (which has files); it should be ;; kept, renamed and unset as default. A notification must be sent. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil) + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil) remove-profile-params (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref remove-profile-params) args))} + (apply (nitrate-call-mock @organization-summary-ref remove-profile-params) args))} ;; --- Message bus mock: capture published events --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, team with files, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, team with files, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) - org-team (th/create-team* 1 {:profile-id (:id user)}) + organization-team (th/create-team* 1 {:profile-id (:id user)}) project (th/create-project* 1 {:profile-id (:id user) - :team-id (:id org-team)}) + :team-id (:id organization-team)}) _ (th/create-file* 1 {:profile-id (:id user) :project-id (:id project)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams []) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams []) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: nitrate was called (via nitrate-call-mock delegating) --- (t/is (:called? @nitrate-mock)) @@ -1134,104 +1134,104 @@ ;; --- Verify: RPC returns success with no result payload --- (t/is (th/success? out)) (t/is (nil? (:result out))) - (t/is (= (:id org-owner) + (t/is (= (:id organization-owner) (:user-who-delete-member @remove-profile-params))) (t/is (= "organization-owner" (:deleted-by-role @remove-profile-params))) ;; --- Verify: default team preserved, renamed and unset as default --- - (let [team (th/db-get :team {:id (:id org-team)})] + (let [team (th/db-get :team {:id (:id organization-team)})] (t/is (false? (:is-default team))) (t/is (str/starts-with? (:name team) "[Acme Org] "))) ;; --- Verify: exactly one notification sent to the user --- (t/is (:called? @mbus-mock)) (let [msg (apply hash-map (rest (:call-args @mbus-mock)))] - (t/is (= :user-org-change (:type (:message msg)))) + (t/is (= :user-organization-change (:type (:message msg)))) (t/is (= (:id user) (:topic msg))) (t/is (= organization-id (:organization-id (:message msg)))) (t/is (= "Acme Org" (:organization-name (:message msg)))) - (t/is (= "dashboard.user-no-longer-belong-org" (:notification (:message msg))))))))) + (t/is (= "dashboard.user-no-longer-belong-organization" (:notification (:message msg))))))))) (t/deftest remove-from-organization-deletes-empty-default-team ;; When the default team has no files it should be soft-deleted. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil) + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil) remove-profile-params (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref remove-profile-params) args))} + (apply (nitrate-call-mock @organization-summary-ref remove-profile-params) args))} ;; --- Message bus mock: swallow notifications --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, empty default team, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, empty default team, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) - org-team (th/create-team* 2 {:profile-id (:id user)}) + organization-team (th/create-team* 2 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams []) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams []) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization ::rpc/profile-id uuid/zero :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success --- (t/is (th/success? out)) (t/is (nil? (:user-who-delete-member @remove-profile-params))) (t/is (nil? (:deleted-by-role @remove-profile-params))) ;; --- Verify: empty default team is soft-deleted --- - (let [team (th/db-get :team {:id (:id org-team)} {::db/remove-deleted false})] + (let [team (th/db-get :team {:id (:id organization-team)} {::db/remove-deleted false})] (t/is (some? (:deleted-at team)))))))) (t/deftest remove-from-organization-deletes-sole-owner-team - ;; When the user is the sole member of an org team it should be deleted. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; When the user is the sole member of an organization team it should be deleted. + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))} + (apply (nitrate-call-mock @organization-summary-ref) args))} ;; --- Message bus mock: swallow notifications --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) extra-team (th/create-team* 3 {:profile-id (:id user)}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success --- (t/is (th/success? out)) @@ -1242,43 +1242,43 @@ (t/deftest remove-from-organization-transfers-ownership-of-multi-member-team ;; When the user owns a team that has another non-owner member, ownership ;; is transferred to that member by the endpoint automatically. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))} + (apply (nitrate-call-mock @organization-summary-ref) args))} ;; --- Message bus mock: swallow notifications --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) candidate (th/create-profile* 3 {:is-active true}) extra-team (th/create-team* 4 {:profile-id (:id user)}) _ (th/create-team-role* {:team-id (:id extra-team) :profile-id (:id candidate) :role :editor}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success --- (t/is (th/success? out)) @@ -1290,43 +1290,43 @@ (t/is (true? (:is-owner rel)))))))) (t/deftest remove-from-organization-exits-non-owned-team - ;; When the user is a non-owner member of an org team, they simply leave. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; When the user is a non-owner member of an organization team, they simply leave. + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))} + (apply (nitrate-call-mock @organization-summary-ref) args))} ;; --- Message bus mock: swallow notifications --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) - extra-team (th/create-team* 5 {:profile-id (:id org-owner)}) + extra-team (th/create-team* 5 {:profile-id (:id organization-owner)}) _ (th/create-team-role* {:team-id (:id extra-team) :profile-id (:id user) :role :editor}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success --- (t/is (th/success? out)) @@ -1340,17 +1340,17 @@ (t/deftest remove-from-organization-error-nobody-to-reassign ;; When the user owns a multi-member team but every other member is ;; also an owner, the auto-selection query finds nobody and raises. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))} + (apply (nitrate-call-mock @organization-summary-ref) args))} ;; --- Message bus mock: swallow notifications --- mbus-mock {:target 'app.msgbus/pub! :return nil}] - ;; --- Setup: create profiles, teams, org-summary --- + ;; --- Setup: create profiles, teams, organization-summary --- (let [other-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) extra-team (th/create-team* 6 {:profile-id (:id user)}) @@ -1360,25 +1360,25 @@ _ (th/db-update! :team-profile-rel {:is-owner true :is-admin false} {:team-id (:id extra-team) :profile-id (:id other-owner)}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id other-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id other-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) - ;; --- Exercise: remove the user from the org --- + ;; --- Exercise: remove the user from the organization --- out (th/management-command! {::th/type :remove-from-organization ::rpc/profile-id (:id other-owner) :profile-id (:id user) :organization-id organization-id :organization-name "Acme Org" - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns error --- (t/is (not (th/success? out))) @@ -1390,35 +1390,35 @@ (t/deftest get-remove-from-organization-summary-no-extra-teams ;; User only has a default team — nothing to delete/transfer/exit. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))}] + (apply (nitrate-call-mock @organization-summary-ref) args))}] - ;; --- Setup: create profiles, team, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, team, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) - org-team (th/create-team* 1 {:profile-id (:id user)}) + organization-team (th/create-team* 1 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams []) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams []) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: get the summary --- out (th/management-command! {::th/type :get-remove-from-organization-summary - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success with all-zero summary --- (t/is (th/success? out)) @@ -1429,37 +1429,37 @@ (:result out))))))) (t/deftest get-remove-from-organization-summary-with-teams-to-delete - ;; User owns a sole-member extra org team → 1 to delete. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; User owns a sole-member extra organization team → 1 to delete. + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))}] + (apply (nitrate-call-mock @organization-summary-ref) args))}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) extra-team (th/create-team* 3 {:profile-id (:id user)}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: get the summary --- out (th/management-command! {::th/type :get-remove-from-organization-summary - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success with 1 team to delete --- (t/is (th/success? out)) @@ -1470,41 +1470,41 @@ (:result out))))))) (t/deftest get-remove-from-organization-summary-with-teams-to-transfer - ;; User owns a multi-member extra org team → 1 to transfer. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; User owns a multi-member extra organization team → 1 to transfer. + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))}] + (apply (nitrate-call-mock @organization-summary-ref) args))}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) candidate (th/create-profile* 3 {:is-active true}) extra-team (th/create-team* 4 {:profile-id (:id user)}) _ (th/create-team-role* {:team-id (:id extra-team) :profile-id (:id candidate) :role :editor}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: get the summary --- out (th/management-command! {::th/type :get-remove-from-organization-summary - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success with 1 team to transfer --- (t/is (th/success? out)) @@ -1515,40 +1515,40 @@ (:result out))))))) (t/deftest get-remove-from-organization-summary-with-teams-to-exit - ;; User is a non-owner member of an org team → 1 to exit. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; User is a non-owner member of an organization team → 1 to exit. + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))}] + (apply (nitrate-call-mock @organization-summary-ref) args))}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) - extra-team (th/create-team* 5 {:profile-id (:id org-owner)}) + extra-team (th/create-team* 5 {:profile-id (:id organization-owner)}) _ (th/create-team-role* {:team-id (:id extra-team) :profile-id (:id user) :role :editor}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: get the summary --- out (th/management-command! {::th/type :get-remove-from-organization-summary - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: RPC returns success with 1 team to exit --- (t/is (th/success? out)) @@ -1560,46 +1560,46 @@ (t/deftest get-remove-from-organization-summary-does-not-mutate ;; Calling the summary endpoint must not modify any teams. - ;; --- Deferred org-summary: depends on team IDs from setup --- - (let [org-summary-ref (atom nil)] + ;; --- Deferred organization-summary: depends on team IDs from setup --- + (let [organization-summary-ref (atom nil)] (with-mocks - [;; --- Nitrate mock: nil during setup, serve org-summary once available --- + [;; --- Nitrate mock: nil during setup, serve organization-summary once available --- nitrate-mock {:target 'app.nitrate/call :return (fn [& args] - (apply (nitrate-call-mock @org-summary-ref) args))}] + (apply (nitrate-call-mock @organization-summary-ref) args))}] - ;; --- Setup: create profiles, teams, org-summary --- - (let [org-owner (th/create-profile* 1 {:is-active true}) + ;; --- Setup: create profiles, teams, organization-summary --- + (let [organization-owner (th/create-profile* 1 {:is-active true}) user (th/create-profile* 2 {:is-active true}) extra-team (th/create-team* 6 {:profile-id (:id user)}) - org-team (th/create-team* 99 {:profile-id (:id user)}) + organization-team (th/create-team* 99 {:profile-id (:id user)}) organization-id (uuid/random) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Acme Org" - :owner-id (:id org-owner) - :your-penpot-teams [(:id org-team)] - :org-teams [(:id extra-team)]) - ;; --- Publish org-summary so the mock can serve it --- - _ (reset! org-summary-ref org-summary) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Acme Org" + :owner-id (:id organization-owner) + :your-penpot-teams [(:id organization-team)] + :organization-teams [(:id extra-team)]) + ;; --- Publish organization-summary so the mock can serve it --- + _ (reset! organization-summary-ref organization-summary) ;; --- Exercise: call the summary endpoint --- _ (th/management-command! {::th/type :get-remove-from-organization-summary - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :profile-id (:id user) :organization-id organization-id - :default-team-id (:id org-team)})] + :default-team-id (:id organization-team)})] ;; --- Verify: both teams still exist and are undeleted --- - (let [t1 (th/db-get :team {:id (:id org-team)})] + (let [t1 (th/db-get :team {:id (:id organization-team)})] (t/is (some? t1)) (t/is (nil? (:deleted-at t1)))) (let [t2 (th/db-get :team {:id (:id extra-team)})] (t/is (some? t2)) (t/is (nil? (:deleted-at t2)))) ;; --- Verify: user is still a member of both teams --- - (let [rel1 (th/db-get :team-profile-rel {:team-id (:id org-team) :profile-id (:id user)})] + (let [rel1 (th/db-get :team-profile-rel {:team-id (:id organization-team) :profile-id (:id user)})] (t/is (some? rel1))) (let [rel2 (th/db-get :team-profile-rel {:team-id (:id extra-team) :profile-id (:id user)})] (t/is (some? rel2))))))) @@ -1613,17 +1613,17 @@ nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (if-let [data @mock-data-ref] - (let [{:keys [owner-id member-id org-summary]} data] + (let [{:keys [owner-id member-id organization-summary]} data] (case method - :get-org-members [owner-id member-id] - :get-org-summary org-summary + :get-organization-members [owner-id member-id] + :get-organization-summary organization-summary nil)) nil))} ;; --- Email mock: capture sent emails --- email-mock {:target 'app.email/send! :return (fn [params] (swap! sent conj params) nil)}] - ;; --- Setup: create profiles, team, org-summary --- + ;; --- Setup: create profiles, team, organization-summary --- (let [owner (th/create-profile* 1 {:is-active true :fullname "Owner"}) member (th/create-profile* 2 {:is-active true :fullname "Member" @@ -1631,14 +1631,14 @@ invited (th/create-profile* 3 {:is-active true :fullname "Invited User" :email "invited@example.com"}) - org-id (uuid/random) - org-name "Acme Inc" + organization-id (uuid/random) + organization-name "Acme Inc" team (th/create-team* 1 {:profile-id (:id owner)}) - org-summary {:id org-id - :name org-name - :teams [{:id (:id team)}]} + organization-summary {:id organization-id + :name organization-name + :teams [{:id (:id team)}]} params {::th/type :notify-organization-sso-change - :organization-id org-id + :organization-id organization-id :updated-props false :announce-activation true}] @@ -1646,7 +1646,7 @@ ;; Member also has a pending invitation: should still receive only one email. (th/db-insert! :team-invitation {:id (uuid/random) - :org-id org-id + :org-id organization-id :team-id nil :email-to (:email member) :created-by (:id owner) @@ -1675,7 +1675,7 @@ ;; --- Publish mock data so the mock can serve it --- (reset! mock-data-ref {:owner-id (:id owner) :member-id (:id member) - :org-summary org-summary}) + :organization-summary organization-summary}) ;; --- Exercise: notify SSO change --- (th/management-command! params) @@ -1689,7 +1689,7 @@ "external@example.com"} emails)) (doseq [email-params @sent] - (t/is (= org-name (:organization-name email-params))) + (t/is (= organization-name (:organization-name email-params))) (t/is (= eml/organization-setup-sso (::eml/factory email-params))))))))) (t/deftest notify-organization-sso-change-skips-email-when-not-active @@ -1703,11 +1703,11 @@ (t/is (empty? @sent)))) (t/deftest check-organization-sso-returns-valid-true - (let [org-id (uuid/random) + (let [organization-id (uuid/random) out (with-redefs [oidc/is-organization-sso-config-valid? (constantly true)] (th/management-command! {::th/type :check-organization-sso - :organization-id org-id + :organization-id organization-id :client-id "test-client" :client-secret "test-secret" :issuer "https://idp.example.com"}))] @@ -1724,14 +1724,14 @@ (t/is (false? (-> out :result :valid))))) (t/deftest check-organization-sso-passes-issuer-to-validation - (let [org-id (uuid/random) + (let [organization-id (uuid/random) out (with-redefs [oidc/is-organization-sso-config-valid? (fn [_cfg sso] (and (= "test-client" (:client-id sso)) (= "https://idp.example.com/" (:issuer sso))))] (th/management-command! {::th/type :check-organization-sso - :organization-id org-id + :organization-id organization-id :client-id "test-client" :client-secret "test-secret" :issuer "https://idp.example.com/"}))] diff --git a/backend/test/backend_tests/rpc_nitrate_test.clj b/backend/test/backend_tests/rpc_nitrate_test.clj index 4a5777bba9..4bfed06abb 100644 --- a/backend/test/backend_tests/rpc_nitrate_test.clj +++ b/backend/test/backend_tests/rpc_nitrate_test.clj @@ -31,55 +31,55 @@ ;; Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- make-org-summary - [& {:keys [organization-id organization-name owner-id your-penpot-teams org-teams] - :or {your-penpot-teams [] org-teams []}}] +(defn- make-organization-summary + [& {:keys [organization-id organization-name owner-id your-penpot-teams organization-teams] + :or {your-penpot-teams [] organization-teams []}}] {:id organization-id :name organization-name :owner-id owner-id :teams (into (mapv (fn [id] {:id id :is-your-penpot true}) your-penpot-teams) - (mapv (fn [id] {:id id :is-your-penpot false}) org-teams))}) + (mapv (fn [id] {:id id :is-your-penpot false}) organization-teams))}) (defn- nitrate-call-mock - "Creates a mock for nitrate/call that returns the given org-summary for - :get-org-summary, a valid membership for :get-org-membership, and nil for + "Creates a mock for nitrate/call that returns the given organization-summary for + :get-organization-summary, a valid membership for :get-organization-membership, and nil for any other method." - ([org-summary] - (nitrate-call-mock org-summary nil)) - ([org-summary remove-profile-params] + ([organization-summary] + (nitrate-call-mock organization-summary nil)) + ([organization-summary remove-profile-params] (fn [_cfg method params] (case method - :get-org-summary org-summary - :get-org-membership {:is-member true - :organization-id (:id org-summary)} - :remove-profile-from-org (when remove-profile-params - (reset! remove-profile-params params)) + :get-organization-summary organization-summary + :get-organization-membership {:is-member true + :organization-id (:id organization-summary)} + :remove-profile-from-organization (when remove-profile-params + (reset! remove-profile-params params)) nil)))) -(defn- nitrate-org-summary-only-mock - [org-summary] +(defn- nitrate-organization-summary-only-mock + [organization-summary] (fn [_cfg method _params] (case method - :get-org-summary org-summary - :get-org-membership {:is-member true - :organization-id (:id org-summary) - :created-at (ct/inst "2026-07-17T12:00:00Z")} - :get-org-members [(:owner-id org-summary) - (uuid/random)] + :get-organization-summary organization-summary + :get-organization-membership {:is-member true + :organization-id (:id organization-summary) + :created-at (ct/inst "2026-07-17T12:00:00Z")} + :get-organization-members [(:owner-id organization-summary) + (uuid/random)] nil))) (defn- active-sso-call-mock [team-id organization-id organization-owner-id] (fn [_cfg method params] (case method - :get-team-org + :get-team-organization (when (= team-id (:team-id params)) {:id team-id :organization {:id organization-id :owner-id organization-owner-id}}) - :get-org-sso-by-team + :get-organization-sso-by-team {:active true :issuer "https://idp.example.com" :organization-id organization-id} @@ -107,7 +107,7 @@ (:id team) organization-id (:id team-owner)) - oidc/build-org-sso-auth-redirect-uri + oidc/build-organization-sso-auth-redirect-uri (constantly "https://idp.example.com/authorize")] (let [out (th/command! params)] (t/is (th/success? out)) @@ -130,7 +130,7 @@ (:id team) organization-id (:id team-owner)) - oidc/build-org-sso-auth-redirect-uri + oidc/build-organization-sso-auth-redirect-uri (constantly redirect-uri)] (let [out (th/command! params)] (t/is (th/success? out)) @@ -138,29 +138,29 @@ :redirect-uri redirect-uri} (:result out)))))))) -(t/deftest check-nitrate-sso-keeps-gate-for-non-member-org-owner +(t/deftest check-nitrate-sso-keeps-gate-for-non-member-organization-owner (let [team-owner (th/create-profile* 1 {:is-active true}) - org-owner (th/create-profile* 2 {:is-active true}) + organization-owner (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id team-owner)}) organization-id (uuid/random) redirect-uri "https://idp.example.com/authorize" params (with-meta {::th/type :check-nitrate-sso - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :team-id (:id team) :url "https://penpot.example.com/#/workspace"} {::http/request {}})] (binding [cf/flags (conj cf/flags :nitrate)] (with-redefs [nitrate/organization-owner-of-team? (fn [_cfg profile-id team-id] - (and (= (:id org-owner) profile-id) + (and (= (:id organization-owner) profile-id) (= (:id team) team-id))) nitrate/call (active-sso-call-mock (:id team) organization-id - (:id org-owner)) - oidc/build-org-sso-auth-redirect-uri + (:id organization-owner)) + oidc/build-organization-sso-auth-redirect-uri (constantly redirect-uri)] (let [out (th/command! params)] (t/is (th/success? out)) @@ -168,30 +168,30 @@ :redirect-uri redirect-uri} (:result out)))))))) -(t/deftest leave-org-happy-path-no-extra-teams +(t/deftest leave-organization-happy-path-no-extra-teams (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) project (th/create-project* 99 {:profile-id (:id profile-user) - :team-id (:id org-default-team)}) + :team-id (:id organization-default-team)}) _ (th/create-file* 99 {:profile-id (:id profile-user) :project-id (:id project)}) organization-id (uuid/random) - ;; The user's personal penpot team in the org context - your-penpot-id (:id org-default-team) + ;; The user's personal penpot team in the organization context + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams []) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams []) remove-profile-params (atom nil)] - (with-redefs [nitrate/call (nitrate-call-mock org-summary remove-profile-params)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary remove-profile-params)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -204,7 +204,7 @@ (t/is (th/success? out)) (t/is (nil? (:result out))) - ;; The personal team must be renamed with the org prefix and + ;; The personal team must be renamed with the organization prefix and ;; unset as a default team. (let [team (th/db-get :team {:id your-penpot-id})] (t/is (str/starts-with? (:name team) "[Test Org] ")) @@ -215,23 +215,23 @@ (t/is (= "organization-member" (:deleted-by-role @remove-profile-params))))))) -(t/deftest leave-org-deletes-org-default-team-when-empty +(t/deftest leave-organization-deletes-organization-default-team-when-empty (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 98 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 98 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -242,31 +242,31 @@ (t/is (th/success? out)) - ;; Empty org default team should be soft-deleted. + ;; Empty organization default team should be soft-deleted. (let [team (th/db-get :team {:id your-penpot-id} {::db/remove-deleted false})] (t/is (some? (:deleted-at team)))))))) -(t/deftest leave-org-keeps-and-renames-org-default-team-when-has-files +(t/deftest leave-organization-keeps-and-renames-organization-default-team-when-has-files (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 97 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 97 {:profile-id (:id profile-user)}) project (th/create-project* 97 {:profile-id (:id profile-user) - :team-id (:id org-default-team)}) + :team-id (:id organization-default-team)}) _ (th/create-file* 97 {:profile-id (:id profile-user) :project-id (:id project)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -277,31 +277,31 @@ (t/is (th/success? out)) - ;; Non-empty org default team should remain and be renamed. + ;; Non-empty organization default team should remain and be renamed. (let [team (th/db-get :team {:id your-penpot-id})] (t/is (str/starts-with? (:name team) "[Test Org] ")) (t/is (false? (:is-default team))) (t/is (nil? (:deleted-at team)))))))) -(t/deftest leave-org-with-teams-to-delete +(t/deftest leave-organization-with-teams-to-delete (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-user is the sole owner/member of team1 team1 (th/create-team* 1 {:profile-id (:id profile-user)}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -317,7 +317,7 @@ (let [team (th/db-get :team {:id (:id team1)} {::db/remove-deleted false})] (t/is (some? (:deleted-at team)))))))) -(t/deftest leave-org-with-ownership-transfer +(t/deftest leave-organization-with-ownership-transfer (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-user owns team1; profile-owner is also a member @@ -325,20 +325,20 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-owner) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -362,7 +362,7 @@ :profile-id (:id profile-owner)})] (t/is (true? (:is-owner rel)))))))) -(t/deftest leave-org-exit-as-non-owner +(t/deftest leave-organization-exit-as-non-owner (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-owner owns team1; profile-user is a non-owner member @@ -370,20 +370,20 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-user) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -405,22 +405,22 @@ (let [team (th/db-get :team {:id (:id team1)})] (t/is (nil? (:deleted-at team)))))))) -(t/deftest get-leave-org-summary-counts-default-team-as-delete-when-empty +(t/deftest get-leave-organization-summary-counts-default-team-as-delete-when-empty (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 97 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 97 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [])] + your-penpot-id (:id organization-default-team) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [])] - (with-redefs [nitrate/call (nitrate-org-summary-only-mock org-summary)] - (let [out (th/command! {::th/type :get-leave-org-summary + (with-redefs [nitrate/call (nitrate-organization-summary-only-mock organization-summary)] + (let [out (th/command! {::th/type :get-leave-organization-summary ::rpc/profile-id (:id profile-user) :id organization-id :default-team-id your-penpot-id})] @@ -433,27 +433,27 @@ :organization-member-count-before 2} (:result out))))))) -(t/deftest get-leave-org-summary-counts-default-team-as-keep-when-has-files +(t/deftest get-leave-organization-summary-counts-default-team-as-keep-when-has-files (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 96 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 96 {:profile-id (:id profile-user)}) project (th/create-project* 96 {:profile-id (:id profile-user) - :team-id (:id org-default-team)}) + :team-id (:id organization-default-team)}) _ (th/create-file* 96 {:profile-id (:id profile-user) :project-id (:id project)}) extra-team (th/create-team* 95 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id extra-team)])] + your-penpot-id (:id organization-default-team) + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id extra-team)])] - (with-redefs [nitrate/call (nitrate-org-summary-only-mock org-summary)] - (let [out (th/command! {::th/type :get-leave-org-summary + (with-redefs [nitrate/call (nitrate-organization-summary-only-mock organization-summary)] + (let [out (th/command! {::th/type :get-leave-organization-summary ::rpc/profile-id (:id profile-user) :id organization-id :default-team-id your-penpot-id})] @@ -467,22 +467,22 @@ :organization-member-count-before 2} (:result out))))))) -(t/deftest leave-org-error-org-owner-cannot-leave +(t/deftest leave-organization-error-organization-owner-cannot-leave (let [profile-owner (th/create-profile* 1 {:is-active true}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-owner)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-owner)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - ;; profile-owner IS the org owner in the org-summary - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [])] + ;; profile-owner IS the organization owner in the organization-summary + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-owner) :id organization-id :name "Test Org" @@ -493,25 +493,25 @@ (t/is (not (th/success? out))) (t/is (= :validation (th/ex-type (:error out)))) - (t/is (= :org-owner-cannot-leave (th/ex-code (:error out)))))))) + (t/is (= :organization-owner-cannot-leave (th/ex-code (:error out)))))))) -(t/deftest leave-org-error-invalid-default-team-id +(t/deftest leave-organization-error-invalid-default-team-id (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; Pass a random UUID that is not in the your-penpot-teams list - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -538,7 +538,7 @@ :or {is-owner false num-members 1 member-ids []}}] {:id id :is-owner is-owner :num-members num-members :member-ids member-ids}) -(t/deftest calculate-valid-teams-no-org-teams +(t/deftest calculate-valid-teams-no-organization-teams (let [default-id (uuid/random) default-team (make-team default-id) result (calculate-valid-teams [default-team] default-id)] @@ -551,7 +551,7 @@ (let [default-id (uuid/random) other-id (uuid/random) other-team (make-team other-id) - ;; default-id is not in org-teams at all + ;; default-id is not in organization-teams at all result (calculate-valid-teams [other-team] default-id)] (t/is (nil? (:valid-default-team result))))) @@ -606,7 +606,7 @@ ;; Integration: combined delete + leave ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(t/deftest leave-org-combined-delete-and-leave +(t/deftest leave-organization-combined-delete-and-leave (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; team1: profile-user is sole owner — must delete @@ -621,20 +621,20 @@ _ (th/create-team-role* {:team-id (:id team3) :profile-id (:id profile-user) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1) (:id team2) (:id team3)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1) (:id team2) (:id team3)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] - (let [data {::th/type :leave-org + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -665,27 +665,27 @@ ;; team3 itself should still exist (profile-owner is still there) (let [team (th/db-get :team {:id (:id team3)})] (t/is (some? team))))))) -(t/deftest leave-org-error-teams-to-delete-incomplete +(t/deftest leave-organization-error-teams-to-delete-incomplete (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-user is the sole owner/member of both team1 and team2 team1 (th/create-team* 1 {:profile-id (:id profile-user)}) team2 (th/create-team* 2 {:profile-id (:id profile-user)}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1) (:id team2)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1) (:id team2)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; Only team1 is listed; team2 is also a sole-owner team and must be included - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -698,7 +698,7 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(t/deftest leave-org-error-cannot-delete-multi-member-team +(t/deftest leave-organization-error-cannot-delete-multi-member-team (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; team1 has two members: profile-user (owner) and profile-owner (editor) @@ -706,21 +706,21 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-owner) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; team1 has 2 members so it is not a valid deletion candidate - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -733,7 +733,7 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(t/deftest leave-org-error-teams-to-leave-incomplete +(t/deftest leave-organization-error-teams-to-leave-incomplete (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-user owns team1, which also has profile-owner as editor @@ -741,21 +741,21 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-owner) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; team1 must be transferred (owner + multiple members) but is absent - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -768,28 +768,28 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(t/deftest leave-org-error-reassign-to-self +(t/deftest leave-organization-error-reassign-to-self (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) team1 (th/create-team* 1 {:profile-id (:id profile-user)}) _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-owner) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; reassign-to points to the profile that is leaving — not allowed - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -802,7 +802,7 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(t/deftest leave-org-error-reassign-to-non-member +(t/deftest leave-organization-error-reassign-to-non-member (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) profile-other (th/create-profile* 3 {:is-active true}) @@ -811,21 +811,21 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-owner) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; profile-other is not a member of team1 - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -838,7 +838,7 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(t/deftest all-team-members-in-orgs-returns-org-id->boolean-map +(t/deftest all-team-members-in-organizations-returns-organization-id->boolean-map (let [profile-user (th/create-profile* 201 {:is-active true}) profile-other (th/create-profile* 202 {:is-active true}) team (th/create-team* 201 {:profile-id (:id profile-user)}) @@ -848,62 +848,62 @@ team-member-ids (->> (th/db-query :team-profile-rel {:team-id (:id team)}) (map :profile-id) (into #{})) - org-id-1 (uuid/random) - org-id-2 (uuid/random) + organization-id-1 (uuid/random) + organization-id-2 (uuid/random) calls (atom [])] (with-redefs [cf/flags (conj cf/flags :nitrate) nitrate/call (fn [_cfg method params] (swap! calls conj [method params]) (case method - :get-org-membership {:is-member true - :organization-id (:organization-id params)} - :get-org-members (get {org-id-1 (vec team-member-ids) - org-id-2 [(:id profile-user)]} - (:organization-id params) - []) + :get-organization-membership {:is-member true + :organization-id (:organization-id params)} + :get-organization-members (get {organization-id-1 (vec team-member-ids) + organization-id-2 [(:id profile-user)]} + (:organization-id params) + []) nil))] - (let [out (th/command! {::th/type :all-team-members-in-orgs + (let [out (th/command! {::th/type :all-team-members-in-organizations ::rpc/profile-id (:id profile-user) :team-id (:id team) - :organization-ids [org-id-1 org-id-2]}) + :organization-ids [organization-id-1 organization-id-2]}) methods (map first @calls) - membership-calls (count (filter #(= :get-org-membership %) methods)) - get-members-calls (count (filter #(= :get-org-members %) methods))] + membership-calls (count (filter #(= :get-organization-membership %) methods)) + get-members-calls (count (filter #(= :get-organization-members %) methods))] (t/is (th/success? out)) - (t/is (= {org-id-1 true - org-id-2 false} + (t/is (= {organization-id-1 true + organization-id-2 false} (:result out))) (t/is (= 2 membership-calls)) (t/is (= 2 get-members-calls)))))) -(t/deftest all-team-members-in-orgs-fails-before-fetching-org-members +(t/deftest all-team-members-in-organizations-fails-before-fetching-organization-members (let [profile-user (th/create-profile* 203 {:is-active true}) team (th/create-team* 203 {:profile-id (:id profile-user)}) - org-id-1 (uuid/random) - org-id-2 (uuid/random) + organization-id-1 (uuid/random) + organization-id-2 (uuid/random) calls (atom [])] (with-redefs [cf/flags (conj cf/flags :nitrate) nitrate/call (fn [_cfg method params] (swap! calls conj [method params]) (case method - :get-org-membership (if (= (:organization-id params) org-id-2) - {:is-member false - :organization-id (:organization-id params)} - {:is-member true - :organization-id (:organization-id params)}) - :get-org-members [] + :get-organization-membership (if (= (:organization-id params) organization-id-2) + {:is-member false + :organization-id (:organization-id params)} + {:is-member true + :organization-id (:organization-id params)}) + :get-organization-members [] nil))] - (let [out (th/command! {::th/type :all-team-members-in-orgs + (let [out (th/command! {::th/type :all-team-members-in-organizations ::rpc/profile-id (:id profile-user) :team-id (:id team) - :organization-ids [org-id-1 org-id-2]}) + :organization-ids [organization-id-1 organization-id-2]}) methods (map first @calls)] (t/is (not (th/success? out))) (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :user-doesnt-belong-organization (th/ex-code (:error out)))) - (t/is (= 0 (count (filter #(= :get-org-members %) methods)))))))) + (t/is (= 0 (count (filter #(= :get-organization-members %) methods)))))))) -(t/deftest leave-org-error-reassign-on-non-owned-team +(t/deftest leave-organization-error-reassign-on-non-owned-team (let [profile-owner (th/create-profile* 1 {:is-active true}) profile-user (th/create-profile* 2 {:is-active true}) ;; profile-owner owns team1; profile-user is just a non-owner member @@ -911,21 +911,21 @@ _ (th/create-team-role* {:team-id (:id team1) :profile-id (:id profile-user) :role :editor}) - org-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) + organization-default-team (th/create-team* 99 {:profile-id (:id profile-user)}) organization-id (uuid/random) - your-penpot-id (:id org-default-team) + your-penpot-id (:id organization-default-team) - org-summary (make-org-summary - :organization-id organization-id - :organization-name "Test Org" - :owner-id (:id profile-owner) - :your-penpot-teams [your-penpot-id] - :org-teams [(:id team1)])] + organization-summary (make-organization-summary + :organization-id organization-id + :organization-name "Test Org" + :owner-id (:id profile-owner) + :your-penpot-teams [your-penpot-id] + :organization-teams [(:id team1)])] - (with-redefs [nitrate/call (nitrate-call-mock org-summary)] + (with-redefs [nitrate/call (nitrate-call-mock organization-summary)] ;; profile-user is not the owner so providing reassign-to is invalid - (let [data {::th/type :leave-org + (let [data {::th/type :leave-organization ::rpc/profile-id (:id profile-user) :id organization-id :name "Test Org" @@ -938,23 +938,23 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :not-valid-teams (th/ex-code (:error out)))))))) -(defn- add-team-to-org-nitrate-mock - [{:keys [org-id org-summary org-perms owner-id team-id sso-active? set-team-params]}] +(defn- add-team-to-organization-nitrate-mock + [{:keys [organization-id organization-summary organization-perms owner-id team-id sso-active? set-team-params]}] (fn [_cfg method params] (case method - :get-org-membership (if (= (:profile-id params) owner-id) - {:is-member true :organization-id org-id} - {:is-member false :organization-id org-id}) - :get-org-members [owner-id] - :get-team-org {:organization nil} - :get-org-permissions org-perms - :set-team-org (do - (when set-team-params - (reset! set-team-params params)) - {:id team-id}) - :get-org-sso {:active sso-active?} - :get-org-summary (assoc org-summary :teams [{:id team-id}]) - :add-profile-to-org {:is-member true} + :get-organization-membership (if (= (:profile-id params) owner-id) + {:is-member true :organization-id organization-id} + {:is-member false :organization-id organization-id}) + :get-organization-members [owner-id] + :get-team-organization {:organization nil} + :get-organization-permissions organization-perms + :set-team-organization (do + (when set-team-params + (reset! set-team-params params)) + {:id team-id}) + :get-organization-sso {:active sso-active?} + :get-organization-summary (assoc organization-summary :teams [{:id team-id}]) + :add-profile-to-organization {:is-member true} nil))) (t/deftest add-team-to-organization-sends-sso-emails-to-new-members-and-invitees @@ -968,16 +968,16 @@ _ (th/create-team-role* {:team-id (:id team) :profile-id (:id member) :role :editor}) - org-id (uuid/random) - org-name "SSO Org" - org-summary {:id org-id - :name org-name - :owner-id (:id owner) - :teams []} - org-perms {:owner-id (:id owner) - :permissions {:create-teams "any" - :move-teams "always" - :new-team-members "members"}} + organization-id (uuid/random) + organization-name "SSO Org" + organization-summary {:id organization-id + :name organization-name + :owner-id (:id owner) + :teams []} + organization-perms {:owner-id (:id owner) + :permissions {:create-teams "any" + :move-teams "always" + :new-team-members "members"}} sent (atom []) set-team-params (atom nil)] @@ -991,24 +991,24 @@ :valid-until (ct/in-future "48h")}) (with-redefs [cf/flags (conj cf/flags :nitrate) - nitrate/call (add-team-to-org-nitrate-mock - {:org-id org-id - :org-summary org-summary - :org-perms org-perms + nitrate/call (add-team-to-organization-nitrate-mock + {:organization-id organization-id + :organization-summary organization-summary + :organization-perms organization-perms :owner-id (:id owner) :team-id (:id team) :sso-active? true :set-team-params set-team-params}) - teams/initialize-user-in-nitrate-org (fn [& _] nil) + teams/initialize-user-in-nitrate-organization (fn [& _] nil) eml/send! (fn [params] (swap! sent conj params))] (let [out (th/command! {::th/type :add-team-to-organization ::rpc/profile-id (:id owner) :team-id (:id team) - :organization-id org-id})] + :organization-id organization-id})] (t/is (th/success? out)))) (t/is (= {:team-id (:id team) - :organization-id org-id + :organization-id organization-id :is-default false} @set-team-params)) @@ -1016,7 +1016,7 @@ (t/is (= 2 (count @sent))) (t/is (= #{"member302@example.com" "external301@example.com"} emails)) (doseq [email-params @sent] - (t/is (= org-name (:organization-name email-params))) + (t/is (= organization-name (:organization-name email-params))) (t/is (= eml/organization-setup-sso (::eml/factory email-params))))))) (t/deftest create-team-in-organization-passes-association-to-nitrate @@ -1025,7 +1025,7 @@ :created-at (ct/now)} params* (atom nil)] (with-redefs [nitrate/call (fn [_cfg method params] - (when (= method :set-team-org) + (when (= method :set-team-organization) (reset! params* params)) {:id (:id team)})] (nitrate/set-team-organization @@ -1046,15 +1046,15 @@ _ (th/create-team-role* {:team-id (:id team) :profile-id (:id member) :role :editor}) - org-id (uuid/random) - org-summary {:id org-id - :name "No SSO Org" - :owner-id (:id owner) - :teams []} - org-perms {:owner-id (:id owner) - :permissions {:create-teams "any" - :move-teams "always" - :new-team-members "members"}} + organization-id (uuid/random) + organization-summary {:id organization-id + :name "No SSO Org" + :owner-id (:id owner) + :teams []} + organization-perms {:owner-id (:id owner) + :permissions {:create-teams "any" + :move-teams "always" + :new-team-members "members"}} sent (atom [])] (th/db-insert! :team-invitation @@ -1067,19 +1067,19 @@ :valid-until (ct/in-future "48h")}) (with-redefs [cf/flags (conj cf/flags :nitrate) - nitrate/call (add-team-to-org-nitrate-mock - {:org-id org-id - :org-summary org-summary - :org-perms org-perms + nitrate/call (add-team-to-organization-nitrate-mock + {:organization-id organization-id + :organization-summary organization-summary + :organization-perms organization-perms :owner-id (:id owner) :team-id (:id team) :sso-active? false}) - teams/initialize-user-in-nitrate-org (fn [& _] nil) + teams/initialize-user-in-nitrate-organization (fn [& _] nil) eml/send! (fn [params] (swap! sent conj params))] (let [out (th/command! {::th/type :add-team-to-organization ::rpc/profile-id (:id owner) :team-id (:id team) - :organization-id org-id})] + :organization-id organization-id})] (t/is (th/success? out)) (t/is (empty? @sent)))))) diff --git a/backend/test/backend_tests/rpc_org_owner_permissions_test.clj b/backend/test/backend_tests/rpc_organization_owner_permissions_test.clj similarity index 69% rename from backend/test/backend_tests/rpc_org_owner_permissions_test.clj rename to backend/test/backend_tests/rpc_organization_owner_permissions_test.clj index c3200ad3e7..8ac1a8fd95 100644 --- a/backend/test/backend_tests/rpc_org_owner_permissions_test.clj +++ b/backend/test/backend_tests/rpc_organization_owner_permissions_test.clj @@ -4,7 +4,7 @@ ;; ;; Copyright (c) KALEIDOS INC Sucursal en España SL -(ns backend-tests.rpc-org-owner-permissions-test +(ns backend-tests.rpc-organization-owner-permissions-test (:require [app.common.uuid :as uuid] [app.config :as cf] @@ -17,37 +17,37 @@ (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) -(defn- org-data - [org-id owner-id] - {:id org-id +(defn- organization-data + [organization-id owner-id] + {:id organization-id :name "Acme" :slug "acme" :owner-id owner-id :avatar-bg-url "http://example.com/avatar.png" :permissions {}}) -(defn- with-org-owner-access - [{:keys [org-owner-id org-id team-id]} f] +(defn- with-organization-owner-access + [{:keys [organization-owner-id organization-id team-id]} f] (with-redefs [cf/flags (conj cf/flags :nitrate) nitrate/organization-owner-of-team? (fn [_cfg profile-id candidate-team-id] - (and (= org-owner-id profile-id) + (and (= organization-owner-id profile-id) (= team-id candidate-team-id))) nitrate/call (fn [_cfg method params] (case method - :get-owned-orgs - [{:id org-id + :get-owned-organizations + [{:id organization-id :name "Acme" - :owner-id org-owner-id + :owner-id organization-owner-id :teams [{:id team-id :is-your-penpot false}]}] - :get-team-org + :get-team-organization (if (= team-id (:team-id params)) {:id team-id :is-your-penpot false - :organization (org-data org-id org-owner-id)} + :organization (organization-data organization-id organization-owner-id)} {:id (:team-id params) :is-your-penpot false :organization nil})))] @@ -67,41 +67,41 @@ (filter #(= profile-id (:topic %))) (mapv :message))) -(t/deftest org-owner-access-disabled-without-nitrate-flag +(t/deftest organization-owner-access-disabled-without-nitrate-flag (let [team-owner (th/create-profile* 1) - org-owner (th/create-profile* 2) + organization-owner (th/create-profile* 2) target-team (th/create-team* 1 {:profile-id (:id team-owner)})] (let [out (th/command! {::th/type :get-projects - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :team-id (:id target-team)}) error (:error out)] (t/is (th/ex-info? error)) (t/is (th/ex-of-type? error :not-found))))) -(t/deftest non-member-org-owner-gets-viewer-access-to-org-team +(t/deftest non-member-organization-owner-gets-viewer-access-to-organization-team (let [team-owner (th/create-profile* 1) - org-owner (th/create-profile* 2) + organization-owner (th/create-profile* 2) target-team (th/create-team* 1 {:profile-id (:id team-owner)}) project (th/create-project* 1 {:profile-id (:id team-owner) :team-id (:id target-team)}) file (th/create-file* 1 {:profile-id (:id team-owner) :project-id (:id project)}) - org-id (uuid/next)] + organization-id (uuid/next)] - (with-org-owner-access {:org-owner-id (:id org-owner) - :org-id org-id - :team-id (:id target-team)} + (with-organization-owner-access {:organization-owner-id (:id organization-owner) + :organization-id organization-id + :team-id (:id target-team)} (fn [] - ;; The team is not listed for a non-member, even though the org + ;; The team is not listed for a non-member, even though the organization ;; owner can access it directly. (let [out (th/command! {::th/type :get-teams - ::rpc/profile-id (:id org-owner)})] + ::rpc/profile-id (:id organization-owner)})] (t/is (nil? (:error out))) (t/is (not-any? #(= (:id target-team) (:id %)) (:result out)))) (let [out (th/command! {::th/type :get-team - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :id (:id target-team)}) team (:result out)] (t/is (nil? (:error out))) @@ -109,54 +109,54 @@ (t/is (false? (get-in team [:permissions :is-owner]))) (t/is (false? (get-in team [:permissions :is-admin]))) (t/is (false? (get-in team [:permissions :can-edit]))) - (t/is (= org-id (get-in team [:organization :id]))) + (t/is (= organization-id (get-in team [:organization :id]))) (t/is (= "Acme" (get-in team [:organization :name])))) (let [out (th/command! {::th/type :get-team-members - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :team-id (:id target-team)}) members (:result out)] (t/is (nil? (:error out))) (t/is (some #(= (:id team-owner) (:id %)) members)) - (t/is (not-any? #(= (:id org-owner) (:id %)) members))) + (t/is (not-any? #(= (:id organization-owner) (:id %)) members))) (let [out (th/command! {::th/type :get-projects - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :team-id (:id target-team)})] (t/is (nil? (:error out))) (t/is (some #(= (:id project) (:id %)) (:result out)))) (let [out (th/command! {::th/type :get-file - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :id (:id file)})] (t/is (nil? (:error out))) (t/is (= (:id file) (get-in out [:result :id]))) (t/is (false? (get-in out [:result :permissions :can-edit])))) (let [out (th/command! {::th/type :rename-project - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :id (:id project) :name "Nope"}) error (:error out)] (t/is (th/ex-info? error)) (t/is (th/ex-of-type? error :not-found))))))) -(t/deftest org-owner-member-keeps-team-role +(t/deftest organization-owner-member-keeps-team-role (let [team-owner (th/create-profile* 1) - org-owner (th/create-profile* 2) + organization-owner (th/create-profile* 2) target-team (th/create-team* 1 {:profile-id (:id team-owner)}) - org-id (uuid/next)] + organization-id (uuid/next)] (th/create-team-role* {:team-id (:id target-team) - :profile-id (:id org-owner) + :profile-id (:id organization-owner) :role :editor}) - (with-org-owner-access {:org-owner-id (:id org-owner) - :org-id org-id - :team-id (:id target-team)} + (with-organization-owner-access {:organization-owner-id (:id organization-owner) + :organization-id organization-id + :team-id (:id target-team)} (fn [] (let [out (th/command! {::th/type :get-team - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :id (:id target-team)}) team (:result out)] (t/is (nil? (:error out))) @@ -164,31 +164,31 @@ (t/is (false? (get-in team [:permissions :is-admin]))) (t/is (true? (get-in team [:permissions :can-edit])))))))) -(t/deftest removed-org-owner-is-degraded-to-viewer +(t/deftest removed-organization-owner-is-degraded-to-viewer (let [team-owner (th/create-profile* 1) - org-owner (th/create-profile* 2) + organization-owner (th/create-profile* 2) target-team (th/create-team* 1 {:profile-id (:id team-owner)}) - org-id (uuid/next) + organization-id (uuid/next) messages (atom [])] (th/create-team-role* {:team-id (:id target-team) - :profile-id (:id org-owner) + :profile-id (:id organization-owner) :role :editor}) - (with-org-owner-access {:org-owner-id (:id org-owner) - :org-id org-id - :team-id (:id target-team)} + (with-organization-owner-access {:organization-owner-id (:id organization-owner) + :organization-id organization-id + :team-id (:id target-team)} (fn [] (let [out (with-captured-messages messages #(th/command! {::th/type :delete-team-member ::rpc/profile-id (:id team-owner) :team-id (:id target-team) - :member-id (:id org-owner)}))] + :member-id (:id organization-owner)}))] (t/is (nil? (:error out)))) - ;; The org owner keeps read-only access, so they are notified + ;; The organization owner keeps read-only access, so they are notified ;; with a role change instead of being kicked out of the team. - (let [notified (messages-for messages (:id org-owner))] + (let [notified (messages-for messages (:id organization-owner))] (t/is (= 1 (count notified))) (t/is (= :team-role-change (:type (first notified)))) (t/is (= :viewer (:role (first notified)))) @@ -196,7 +196,7 @@ (t/is (not-any? #(= :team-membership-change (:type %)) notified))) (let [out (th/command! {::th/type :get-team - ::rpc/profile-id (:id org-owner) + ::rpc/profile-id (:id organization-owner) :id (:id target-team)}) team (:result out)] (t/is (nil? (:error out))) @@ -206,19 +206,19 @@ (t/deftest removed-regular-member-is-still-kicked-out (let [team-owner (th/create-profile* 1) - org-owner (th/create-profile* 2) + organization-owner (th/create-profile* 2) member (th/create-profile* 3) target-team (th/create-team* 1 {:profile-id (:id team-owner)}) - org-id (uuid/next) + organization-id (uuid/next) messages (atom [])] (th/create-team-role* {:team-id (:id target-team) :profile-id (:id member) :role :editor}) - (with-org-owner-access {:org-owner-id (:id org-owner) - :org-id org-id - :team-id (:id target-team)} + (with-organization-owner-access {:organization-owner-id (:id organization-owner) + :organization-id organization-id + :team-id (:id target-team)} (fn [] (let [out (with-captured-messages messages #(th/command! {::th/type :delete-team-member diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index 3fc7747030..a590f93a32 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -110,24 +110,24 @@ audit-mock {:target 'app.loggers.audit/submit :return nil}] (let [owner (th/create-profile* 101 {:is-active true}) invitee (th/create-profile* 102 {:is-active true}) - org-team (th/create-team* 101 {:profile-id (:id owner)}) + organization-team (th/create-team* 101 {:profile-id (:id owner)}) plain-team (th/create-team* 102 {:profile-id (:id owner)}) - org-id (uuid/random) - org {:id org-id - :name "Acme" - :slug "acme" - :owner-id (:id owner) - :avatar-bg-url "https://example.com/avatar.svg" - :permissions {:new-team-members "anyone"}} + organization-id (uuid/random) + organization {:id organization-id + :name "Acme" + :slug "acme" + :owner-id (:id owner) + :avatar-bg-url "https://example.com/avatar.svg" + :permissions {:new-team-members "anyone"}} nitrate-call (fn [_cfg method params] (case method - :get-team-org - (if (= (:team-id params) (:id org-team)) - {:organization org :is-your-penpot false} + :get-team-organization + (if (= (:team-id params) (:id organization-team)) + {:organization organization :is-your-penpot false} {:organization nil :is-your-penpot false}) - :get-org-members + :get-organization-members [(:id invitee)] nil)) @@ -139,21 +139,21 @@ :emails [email]}))] (with-redefs [cf/flags (conj cf/flags :nitrate :email-verification) nitrate/call nitrate-call] - (t/is (th/success? (invite! org-team (:email invitee)))) - (t/is (th/success? (invite! org-team (:email invitee)))) + (t/is (th/success? (invite! organization-team (:email invitee)))) + (t/is (th/success? (invite! organization-team (:email invitee)))) (t/is (th/success? (invite! plain-team "external@example.com")))) (let [events (mapv second (:call-args-list @audit-mock)) - create-org (first (filter #(and (= "create-team-invitation" (:name %)) - (= (:email invitee) - (get-in % [:props :member-email]))) - events)) - update-org (first (filter #(= "update-team-invitation" (:name %)) events)) + create-organization (first (filter #(and (= "create-team-invitation" (:name %)) + (= (:email invitee) + (get-in % [:props :member-email]))) + events)) + update-organization (first (filter #(= "update-team-invitation" (:name %)) events)) create-plain (first (filter #(and (= "create-team-invitation" (:name %)) (= "external@example.com" (get-in % [:props :member-email]))) events))] - (doseq [event [create-org update-org]] + (doseq [event [create-organization update-organization]] (t/is (true? (get-in event [:props :team-belongs-to-organization]))) (t/is (true? (get-in event [:props :adds-invitee-to-organization]))) (t/is (true? (get-in event [:props :invitee-already-organization-member])))) @@ -486,11 +486,11 @@ nitrate/call (fn [_cfg method _params] (case method - :get-org-membership {:organization-id organization-id - :is-member false} - :get-org-members [(:id inviter) (uuid/random) (uuid/random)] + :get-organization-membership {:organization-id organization-id + :is-member false} + :get-organization-members [(:id inviter) (uuid/random) (uuid/random)] nil)) - teams/initialize-user-in-nitrate-org + teams/initialize-user-in-nitrate-organization (fn [& _] default-team-id)] (let [out (verify! direct-token)] (t/is (th/success? out)) @@ -531,10 +531,10 @@ nitrate/call (fn [_cfg method _params] (case method - :get-org-membership-by-team {:organization-id organization-id - :is-member false} - :get-org-members (into [(:id inviter)] - (repeatedly 4 uuid/random)) + :get-organization-membership-by-team {:organization-id organization-id + :is-member false} + :get-organization-members (into [(:id inviter)] + (repeatedly 4 uuid/random)) nil)) teams/add-profile-to-team! (fn [& _] nil)] (let [out (verify! team-token)] @@ -574,9 +574,9 @@ nitrate/call (fn [_cfg method _params] (case method - :get-org-membership-by-team {:organization-id organization-id - :is-member true} - :get-org-members (throw (ex-info "unexpected member count" {})) + :get-organization-membership-by-team {:organization-id organization-id + :is-member true} + :get-organization-members (throw (ex-info "unexpected member count" {})) nil)) teams/add-profile-to-team! (fn [& _] nil)] (let [out (verify! team-token)] diff --git a/common/src/app/common/types/nitrate_permissions.cljc b/common/src/app/common/types/nitrate_permissions.cljc index a395da6709..fc47f2ed7c 100644 --- a/common/src/app/common/types/nitrate_permissions.cljc +++ b/common/src/app/common/types/nitrate_permissions.cljc @@ -14,29 +14,29 @@ :new-team-members "anyone"}) (defn- can-create-team? - [{:keys [is-org-owner? permission-value]}] - (or is-org-owner? + [{:keys [is-organization-owner? permission-value]}] + (or is-organization-owner? (= permission-value "any"))) (defn- can-delete-team? - [{:keys [is-org-owner? permission-value team-perms]}] + [{:keys [is-organization-owner? permission-value team-perms]}] (cond - ;; Org owners can always delete teams inside their organizations. - is-org-owner? + ;; Organization owners can always delete teams inside their organizations. + is-organization-owner? true (= permission-value "onlyOwners") (boolean (:is-owner team-perms)) :else false)) (defn- can-move-team? - [{:keys [permission-value target-org-same-owner?]}] + [{:keys [permission-value target-organization-same-owner?]}] (cond (= permission-value "never") false (= permission-value "always") true (= permission-value "myOrganizations") - (true? target-org-same-owner?) + (true? target-organization-same-owner?) :else false)) (defn- can-invite-to-team? @@ -67,36 +67,36 @@ :add-anybody-to-team {:permission-key :new-team-members :check-fn can-add-anybody-to-team?}}) -(defn- normalize-org-permissions - [org-perms] - (merge defaults (or (:permissions org-perms) {}))) +(defn- normalize-organization-permissions + [organization-perms] + (merge defaults (or (:permissions organization-perms) {}))) (defn- owner? - [org-perms profile-id] - (= profile-id (:owner-id org-perms))) + [organization-perms profile-id] + (= profile-id (:owner-id organization-perms))) (defn allowed? "Returns true only for explicitly allowed actions (fail-closed)." - [action {:keys [org-perms profile-id team-perms target-org-same-owner?]}] + [action {:keys [organization-perms profile-id team-perms target-organization-same-owner?]}] (let [{:keys [permission-key check-fn] :as rule} (get action-rules action) - permissions (normalize-org-permissions org-perms) - is-org-owner? (owner? org-perms profile-id) + permissions (normalize-organization-permissions organization-perms) + is-organization-owner? (owner? organization-perms profile-id) permission-value (get permissions permission-key)] (cond (nil? rule) false - :else (boolean (check-fn {:is-org-owner? is-org-owner? + :else (boolean (check-fn {:is-organization-owner? is-organization-owner? :permission-value permission-value :team-perms team-perms - :target-org-same-owner? target-org-same-owner?}))))) + :target-organization-same-owner? target-organization-same-owner?}))))) (defn can-send-invitations? [{:keys [nitrate-enabled? organization profile-id team-permissions]}] - (let [in-org? (and nitrate-enabled? organization)] - (if in-org? + (let [in-organization? (and nitrate-enabled? organization)] + (if in-organization? (allowed? :send-invitations - {:org-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms {:owner-id (:owner-id organization) + :permissions (:permissions organization)} :profile-id profile-id :team-perms team-permissions}) (or (boolean (:is-owner team-permissions)) diff --git a/common/test/common_tests/types/nitrate_permissions_test.cljc b/common/test/common_tests/types/nitrate_permissions_test.cljc index 795b94cf74..eeab8099ac 100644 --- a/common/test/common_tests/types/nitrate_permissions_test.cljc +++ b/common/test/common_tests/types/nitrate_permissions_test.cljc @@ -9,7 +9,7 @@ [app.common.types.nitrate-permissions :as nitrate-perms] [clojure.test :as t])) -(def org-perms +(def organization-perms {:owner-id :owner :permissions {:create-teams "any" :delete-teams "onlyOwners" @@ -17,162 +17,162 @@ (t/deftest unknown-action-is-denied (t/is (false? (nitrate-perms/allowed? :unknown - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :member :team-perms {:is-admin true}})))) -(t/deftest org-owner-is-allowed-for-create-and-delete +(t/deftest organization-owner-is-allowed-for-create-and-delete (t/is (true? (nitrate-perms/allowed? :create-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :owner :team-perms {:is-admin false}}))) (t/is (true? (nitrate-perms/allowed? :delete-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :owner :team-perms {:is-admin false}})))) (t/deftest create-team-permission-rules (t/is (true? (nitrate-perms/allowed? :create-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :member :team-perms {:is-admin false}}))) (t/is (false? (nitrate-perms/allowed? :create-team - {:org-perms (assoc org-perms :permissions {:create-teams "none" - :delete-teams "onlyOwners"}) + {:organization-perms (assoc organization-perms :permissions {:create-teams "none" + :delete-teams "onlyOwners"}) :profile-id :member :team-perms {:is-admin false}})))) (t/deftest delete-team-onlyowners-allows-only-team-owners (t/is (true? (nitrate-perms/allowed? :delete-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :member :team-perms {:is-owner true :is-admin true}}))) (t/is (false? (nitrate-perms/allowed? :delete-team - {:org-perms org-perms + {:organization-perms organization-perms :profile-id :member :team-perms {:is-admin true}}))) (t/is (false? (nitrate-perms/allowed? :delete-team - {:org-perms (assoc org-perms :permissions {:create-teams "any" - :delete-teams "invalid-value"}) + {:organization-perms (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "invalid-value"}) :profile-id :member :team-perms {:is-admin true}})))) -(t/deftest delete-team-onlyme-still-allows-org-owner - (let [only-me-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyMe"})] +(t/deftest delete-team-onlyme-still-allows-organization-owner + (let [only-me-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyMe"})] (t/is (true? (nitrate-perms/allowed? :delete-team - {:org-perms only-me-org + {:organization-perms only-me-organization :profile-id :owner :team-perms {:is-owner false :is-admin false}}))) (t/is (false? (nitrate-perms/allowed? :delete-team - {:org-perms only-me-org + {:organization-perms only-me-organization :profile-id :member :team-perms {:is-owner true :is-admin true}}))))) -(t/deftest move-team-always-allows-any-org-owner-or-all-users - (let [always-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners" - :move-teams "always"})] - ;; Org owner should always be allowed +(t/deftest move-team-always-allows-any-organization-owner-or-all-users + (let [always-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :move-teams "always"})] + ;; Organization owner should always be allowed (t/is (true? (nitrate-perms/allowed? :move-team - {:org-perms always-org + {:organization-perms always-organization :profile-id :owner :team-perms {}}))) ;; Regular member should be allowed when move-teams is "always" (t/is (true? (nitrate-perms/allowed? :move-team - {:org-perms always-org + {:organization-perms always-organization :profile-id :member :team-perms {}}))))) (t/deftest move-team-myorganizations-allows-only-within-same-owner - (let [my-orgs (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners" - :move-teams "myOrganizations"})] - ;; Org owner must also stay within same-owner organizations + (let [my-organizations (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :move-teams "myOrganizations"})] + ;; Organization owner must also stay within same-owner organizations (t/is (false? (nitrate-perms/allowed? :move-team - {:org-perms my-orgs + {:organization-perms my-organizations :profile-id :owner :team-perms {} - :target-org-same-owner? false}))) + :target-organization-same-owner? false}))) (t/is (true? (nitrate-perms/allowed? :move-team - {:org-perms my-orgs + {:organization-perms my-organizations :profile-id :owner :team-perms {} - :target-org-same-owner? true}))) + :target-organization-same-owner? true}))) ;; Regular member should be allowed only if target has same owner (t/is (true? (nitrate-perms/allowed? :move-team - {:org-perms my-orgs + {:organization-perms my-organizations :profile-id :member :team-perms {} - :target-org-same-owner? true}))) + :target-organization-same-owner? true}))) (t/is (false? (nitrate-perms/allowed? :move-team - {:org-perms my-orgs + {:organization-perms my-organizations :profile-id :member :team-perms {} - :target-org-same-owner? false}))))) + :target-organization-same-owner? false}))))) (t/deftest move-team-never-denies-all - (let [never-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners" - :move-teams "never"})] - ;; Even org owner should be denied + (let [never-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :move-teams "never"})] + ;; Even organization owner should be denied (t/is (false? (nitrate-perms/allowed? :move-team - {:org-perms never-org + {:organization-perms never-organization :profile-id :owner :team-perms {}}))) ;; Regular member should be denied (t/is (false? (nitrate-perms/allowed? :move-team - {:org-perms never-org + {:organization-perms never-organization :profile-id :member :team-perms {}}))))) (t/deftest move-team-defaults-to-always - (let [default-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners"})] + (let [default-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners"})] ;; Should default to "always" when not specified (t/is (true? (nitrate-perms/allowed? :move-team - {:org-perms default-org + {:organization-perms default-organization :profile-id :member :team-perms {}}))))) (t/deftest send-invitations-defaults-to-owners-and-admins - (let [default-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners"})] + (let [default-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners"})] (t/is (true? (nitrate-perms/allowed? :send-invitations - {:org-perms default-org + {:organization-perms default-organization :profile-id :owner :team-perms {:is-owner true :is-admin false}}))) (t/is (true? (nitrate-perms/allowed? :send-invitations - {:org-perms default-org + {:organization-perms default-organization :profile-id :member :team-perms {:is-owner false :is-admin true}}))) (t/is (false? (nitrate-perms/allowed? :send-invitations - {:org-perms default-org + {:organization-perms default-organization :profile-id :member :team-perms {:is-owner false :is-admin false}}))))) (t/deftest send-invitations-owners-allows-only-team-owners - (let [only-owners-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners" - :send-invitations "owners"})] + (let [only-owners-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :send-invitations "owners"})] (t/is (true? (nitrate-perms/allowed? :send-invitations - {:org-perms only-owners-org + {:organization-perms only-owners-organization :profile-id :member :team-perms {:is-owner true :is-admin true}}))) (t/is (false? (nitrate-perms/allowed? :send-invitations - {:org-perms only-owners-org + {:organization-perms only-owners-organization :profile-id :owner :team-perms {:is-owner false :is-admin false}}))) (t/is (false? (nitrate-perms/allowed? :send-invitations - {:org-perms only-owners-org + {:organization-perms only-owners-organization :profile-id :member :team-perms {:is-owner false :is-admin true}}))))) (t/deftest send-invitations-invalid-value-is-denied - (let [invalid-org (assoc org-perms :permissions {:create-teams "any" - :delete-teams "onlyOwners" - :send-invitations "invalid-value"})] + (let [invalid-organization (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :send-invitations "invalid-value"})] (t/is (false? (nitrate-perms/allowed? :send-invitations - {:org-perms invalid-org + {:organization-perms invalid-organization :profile-id :member :team-perms {:is-owner true :is-admin true}}))))) diff --git a/frontend/src/app/main/data/common.cljs b/frontend/src/app/main/data/common.cljs index 949a5a84ec..4f5af3d0fe 100644 --- a/frontend/src/app/main/data/common.cljs +++ b/frontend/src/app/main/data/common.cljs @@ -245,11 +245,11 @@ (rx/of (rt/nav-raw :uri (str redirect-uri))) (rx/empty)))))))) -(defn handle-change-team-org - "Handle :team-org-change websocket messages on dashboard and workspace. - Updates local team org data and redirects to SSO when required." +(defn handle-change-team-organization + "Handle :team-organization-change websocket messages on dashboard and workspace. + Updates local team organization data and redirects to SSO when required." [{:keys [team notification]}] - (ptk/reify ::handle-change-team-org + (ptk/reify ::handle-change-team-organization ptk/WatchEvent (watch [_ state _] (let [current-team-id (:current-team-id state) @@ -279,7 +279,7 @@ (defn handle-organization-change-sso "Handle :organization-change-sso websocket messages on dashboard and workspace. - Redirects to the org SSO login when the current team belongs to that org." + Redirects to the organization SSO login when the current team belongs to that organization." [{:keys [organization-id]}] (ptk/reify ::handle-organization-change-sso ptk/WatchEvent @@ -287,8 +287,8 @@ (when (contains? cf/flags :nitrate) (let [team-id (:current-team-id state) team (dm/get-in state [:teams team-id]) - org-id (dm/get-in team [:organization :id])] - (when (= organization-id org-id) + team-organization-id (dm/get-in team [:organization :id])] + (when (= organization-id team-organization-id) (check-team-sso team-id))))))) diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index a573033183..470ade6bed 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -685,9 +685,9 @@ (rx/of (dcm/change-team-role params) (modal/hide))))) -(defn- handle-user-org-change +(defn- handle-user-organization-change [{:keys [organization-id organization-name notification]}] - (ptk/reify ::handle-user-org-change + (ptk/reify ::handle-user-organization-change ptk/WatchEvent (watch [_ state _] (when (and notification (contains? cf/flags :nitrate)) @@ -698,7 +698,7 @@ :level :info :timeout nil}) (dtm/fetch-teams) - ;; When the user is currently on a team of the org + ;; When the user is currently on a team of the organization (when (= organization-id (dm/get-in team [:organization :id])) (dcm/go-to-dashboard-recent {:team-id :default})))))))) @@ -711,22 +711,22 @@ (when (contains? cf/flags :nitrate) (let [team-id (:current-team-id state) current-team (dm/get-in state [:teams team-id]) - current-org-id (dm/get-in current-team [:organization :id]) + current-organization-id (dm/get-in current-team [:organization :id]) teams-set (set teams) notify? (contains? teams-set team-id) fetch? (some (:teams state) teams) go-to-default? (or (some #{team-id} deleted-teams) - (= organization-id current-org-id))] + (= organization-id current-organization-id))] (rx/concat (when go-to-default? ;; If the user is currently on one of the deleted teams (rx/of (dcm/go-to-dashboard-recent {:team-id :default}))) - (when notify? ;; If the user is currently on one of the org teams - (rx/of (ntf/show {:content (tr "dashboard.org-deleted" organization-name) + (when notify? ;; If the user is currently on one of the organization teams + (rx/of (ntf/show {:content (tr "dashboard.organization-deleted" organization-name) :type :toast :level :info :timeout nil}))) - (when fetch? ;; If the user belonged to the org + (when fetch? ;; If the user belonged to the organization (rx/of (dtm/fetch-teams))))))))) (defn- process-message @@ -735,8 +735,8 @@ :notification (dcm/handle-notification msg) :team-role-change (handle-change-team-role msg) :team-membership-change (dcm/team-membership-change msg) - :team-org-change (dcm/handle-change-team-org msg) - :user-org-change (handle-user-org-change msg) + :team-organization-change (dcm/handle-change-team-organization msg) + :user-organization-change (handle-user-organization-change msg) :organization-deleted (handle-organization-deleted msg) :organization-change-sso (dcm/handle-organization-change-sso msg) nil)) diff --git a/frontend/src/app/main/data/helpers.cljs b/frontend/src/app/main/data/helpers.cljs index 1a3cd54509..3681e821de 100644 --- a/frontend/src/app/main/data/helpers.cljs +++ b/frontend/src/app/main/data/helpers.cljs @@ -212,7 +212,7 @@ (defn lookup-team "The team identified by `team-id`, looked up first in the membership `:teams` map and falling back to the directly-opened `:current-team`. - The fallback covers org-owner access to teams the profile is not a + The fallback covers organization-owner access to teams the profile is not a member of, which are kept out of `:teams` so they don't leak into the teams listing." ([state] diff --git a/frontend/src/app/main/data/nitrate.cljs b/frontend/src/app/main/data/nitrate.cljs index 10326ca290..780aa51d4e 100644 --- a/frontend/src/app/main/data/nitrate.cljs +++ b/frontend/src/app/main/data/nitrate.cljs @@ -161,7 +161,7 @@ (contains? #{"active" "past_due" "trialing"} (dm/get-in profile [:subscription :status])))) -(defn leave-org +(defn leave-organization [{:keys [id name default-team-id @@ -171,7 +171,7 @@ organization-member-count-before on-error]}] - (ptk/reify ::leave-org + (ptk/reify ::leave-organization ptk/WatchEvent (watch [_ state _] (let [profile-id (dm/get-in state [:profile :id]) @@ -191,29 +191,29 @@ :subscription-status subscription-status})] (rx/concat (rx/of audit-event) - (->> (rp/cmd! ::leave-org {:id id - :name name - :default-team-id default-team-id - :teams-to-delete teams-to-delete - :teams-to-leave teams-to-leave}) + (->> (rp/cmd! ::leave-organization {:id id + :name name + :default-team-id default-team-id + :teams-to-delete teams-to-delete + :teams-to-leave teams-to-leave}) (rx/mapcat (fn [_] (rx/of (dt/fetch-teams) (dcm/go-to-dashboard-recent :team-id profile-team-id) (modal/hide) - (ntf/show {:content (tr "dasboard.leave-org.toast" name) + (ntf/show {:content (tr "dashboard.leave-organization.toast" name) :type :toast :level :success})))) (rx/catch on-error))))))) -(defn show-leave-org-modal +(defn show-leave-organization-modal [{:keys [organization profile default-team-id leave-fn teams-to-transfer on-error]}] - (ptk/reify ::show-leave-org-modal + (ptk/reify ::show-leave-organization-modal ptk/WatchEvent (watch [_ _ _] - (->> (rp/cmd! ::get-leave-org-summary {:id (:id organization) - :default-team-id default-team-id}) + (->> (rp/cmd! ::get-leave-organization-summary {:id (:id organization) + :default-team-id default-team-id}) (rx/mapcat (fn [summary] (let [num-teams-to-delete (:teams-to-delete summary) @@ -231,7 +231,7 @@ (pos? num-teams-to-transfer) (rx/of (modal/show - {:type :leave-and-reassign-org + {:type :leave-and-reassign-organization :profile profile :teams-to-transfer teams-to-transfer :num-teams-to-delete num-teams-to-delete @@ -242,28 +242,28 @@ (pos? num-teams-to-detach)) (rx/of (modal/show {:type :confirm - :title (tr "modals.before-leave-org.title" (:name organization)) - :message (tr "modals.before-leave-org.message") - :accept-label (tr "modals.leave-org-confirm.accept") + :title (tr "modals.before-leave-organization.title" (:name organization)) + :message (tr "modals.before-leave-organization.message") + :accept-label (tr "modals.leave-organization-confirm.accept") :on-accept leave-fn - :error-msg (tr "modals.before-leave-org.warning")})) + :error-msg (tr "modals.before-leave-organization.warning")})) :else (rx/of (modal/show {:type :confirm - :title (tr "modals.leave-org-confirm.title" (:name organization)) - :message (tr "modals.leave-org-confirm.message") - :accept-label (tr "modals.leave-org-confirm.accept") + :title (tr "modals.leave-organization-confirm.title" (:name organization)) + :message (tr "modals.leave-organization-confirm.message") + :accept-label (tr "modals.leave-organization-confirm.accept") :on-accept leave-fn})))))) (rx/catch on-error))))) -(defn remove-team-from-org +(defn remove-team-from-organization [{:keys [team-id organization-id organization-name] :as params}] - (ptk/reify ::remove-team-from-org + (ptk/reify ::remove-team-from-organization ptk/WatchEvent (watch [_ _ _] - (->> (rp/cmd! ::remove-team-from-org {:team-id team-id :organization-id organization-id :organization-name organization-name}) + (->> (rp/cmd! ::remove-team-from-organization {:team-id team-id :organization-id organization-id :organization-name organization-name}) (rx/mapcat (fn [_] (rx/of (dt/fetch-teams) @@ -272,40 +272,40 @@ (fn [cause] (let [code (-> cause ex-data :code)] (if (= code :not-allowed) - (rx/of (modal/show :no-permission-modal {:type :no-orgs-change})) + (rx/of (modal/show :no-permission-modal {:type :no-organizations-change})) (rx/throw cause))))))))) -(defn show-remove-team-from-org-modal - "Fetches fresh team/org data, then shows the remove-from-org confirmation +(defn show-remove-team-from-organization-modal + "Fetches fresh team/organization data, then shows the remove-from-organization confirmation modal or the no-permission modal if the move-team permission blocks it." [{:keys [team-id]}] - (ptk/reify ::show-remove-team-from-org-modal + (ptk/reify ::show-remove-team-from-organization-modal ptk/WatchEvent (watch [_ state _] (let [profile-id (dm/get-in state [:profile :id])] (dt/with-refreshed-team team-id (fn [team] - (let [source-org (:organization team) + (let [source-organization (:organization team) can-move? (nitrate-perms/allowed? :move-team - {:org-perms {:owner-id (:owner-id source-org) - :permissions (:permissions source-org)} + {:organization-perms {:owner-id (:owner-id source-organization) + :permissions (:permissions source-organization)} :profile-id profile-id :team-perms (:permissions team) - :target-org-same-owner? false})] + :target-organization-same-owner? false})] (rx/of (if can-move? (modal/show {:type :confirm - :title (tr "modals.remove-team-org.title") - :message (tr "modals.remove-team-org.text" (:name team) (:name source-org)) - :hint (tr "modals.remove-team-org.info") + :title (tr "modals.remove-team-organization.title") + :message (tr "modals.remove-team-organization.text" (:name team) (:name source-organization)) + :hint (tr "modals.remove-team-organization.info") :hint-level :default - :accept-label (tr "modals.remove-team-org.accept") - :on-accept #(st/emit! (remove-team-from-org {:team-id team-id - :organization-id (:id source-org) - :organization-name (:name source-org)})) + :accept-label (tr "modals.remove-team-organization.accept") + :on-accept #(st/emit! (remove-team-from-organization {:team-id team-id + :organization-id (:id source-organization) + :organization-name (:name source-organization)})) :accept-style :danger}) - (modal/show :no-permission-modal {:type :no-orgs-change})))))))))) + (modal/show :no-permission-modal {:type :no-organizations-change})))))))))) (defn add-team-to-organization @@ -359,24 +359,24 @@ (rx/empty))))))))))) -(defn- fetch-orgs-allowed - "Returns an rx observable of an `orgs-allowed` map (org-id -> boolean). - Orgs where :add-anybody-to-team is permitted are pre-approved; - the rest are verified via :all-team-members-in-orgs." - [team-id orgs] - (let [add-anybody-orgs (filterv #(nitrate-perms/allowed? :add-anybody-to-team {:org-perms %}) orgs) - orgs-to-check (filterv #(not (nitrate-perms/allowed? :add-anybody-to-team {:org-perms %})) orgs) - org-ids-to-check (mapv :id orgs-to-check)] - (if (empty? org-ids-to-check) - (rx/of (into {} (map (fn [org] [(:id org) true])) orgs)) - (->> (rp/cmd! :all-team-members-in-orgs {:team-id team-id :organization-ids org-ids-to-check}) - (rx/map (fn [checked-orgs] - (merge (into {} (map (fn [org] [(:id org) true])) add-anybody-orgs) - checked-orgs))))))) +(defn- fetch-organizations-allowed + "Returns an rx observable of an `organizations-allowed` map (organization-id -> boolean). + Organizations where :add-anybody-to-team is permitted are pre-approved; + the rest are verified via :all-team-members-in-organizations." + [team-id organizations] + (let [add-anybody-organizations (filterv #(nitrate-perms/allowed? :add-anybody-to-team {:organization-perms %}) organizations) + organizations-to-check (filterv #(not (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms %})) organizations) + organization-ids-to-check (mapv :id organizations-to-check)] + (if (empty? organization-ids-to-check) + (rx/of (into {} (map (fn [organization] [(:id organization) true])) organizations)) + (->> (rp/cmd! :all-team-members-in-organizations {:team-id team-id :organization-ids organization-ids-to-check}) + (rx/map (fn [checked-organizations] + (merge (into {} (map (fn [organization] [(:id organization) true])) add-anybody-organizations) + checked-organizations))))))) (defn show-add-team-to-organization-modal - "Fetches fresh team/org data, then shows the add-to-org modal - restricted to orgs where the user has permission, or the no-permission + "Fetches fresh team/organization data, then shows the add-to-organization modal + restricted to organizations where the user has permission, or the no-permission modal if none qualify." [{:keys [team-id]}] (ptk/reify ::show-add-team-to-organization-modal @@ -386,114 +386,114 @@ (->> (rp/cmd! :get-teams) (rx/mapcat (fn [teams] - (let [all-orgs (map dt/team->organization - (filter #(and (:is-default %) (:organization %)) teams)) - orgs (filter (fn [org] - (let [perm (dm/get-in org [:permissions :create-teams]) - is-own? (= profile-id (:owner-id org))] - (or (= perm "any") is-own?))) all-orgs) + (let [all-organizations (map dt/team->organization + (filter #(and (:is-default %) (:organization %)) teams)) + organizations (filter (fn [organization] + (let [perm (dm/get-in organization [:permissions :create-teams]) + is-own? (= profile-id (:owner-id organization))] + (or (= perm "any") is-own?))) all-organizations) team (first (filter #(= (:id %) team-id) teams)) on-confirm (fn [organization-id] (st/emit! (add-team-to-organization {:team-id team-id :organization-id organization-id}))) show-select-modal - (fn [orgs-allowed] - (let [has-filtered? (< (count orgs) (count all-orgs)) + (fn [organizations-allowed] + (let [has-filtered? (< (count organizations) (count all-organizations)) extra-props (when has-filtered? - {:info-message-key "dashboard.select-org-modal.permission-info"})] + {:info-message-key "dashboard.select-organization-modal.permission-info"})] (modal/show :select-organization-modal - (merge {:organizations orgs - :orgs-allowed orgs-allowed + (merge {:organizations organizations + :organizations-allowed organizations-allowed :current-organization-id (dm/get-in team [:organization :id]) :on-confirm on-confirm :team-id team-id - :title-key "dashboard.select-org-modal.title" - :choose-key "dashboard.select-org-modal.choose" - :placeholder-key "dashboard.select-org-modal.select" - :accept-key "dashboard.select-org-modal.accept" + :title-key "dashboard.select-organization-modal.title" + :choose-key "dashboard.select-organization-modal.choose" + :placeholder-key "dashboard.select-organization-modal.select" + :accept-key "dashboard.select-organization-modal.accept" :cancel-key "labels.cancel"} extra-props))))] - (if (empty? orgs) + (if (empty? organizations) (rx/of (dt/teams-fetched teams) - (modal/show :no-permission-modal {:type :no-orgs-create})) - (->> (fetch-orgs-allowed team-id orgs) + (modal/show :no-permission-modal {:type :no-organizations-create})) + (->> (fetch-organizations-allowed team-id organizations) (rx/mapcat - (fn [orgs-allowed] - (let [valid-orgs (filterv #(true? (get orgs-allowed (:id %))) orgs)] + (fn [organizations-allowed] + (let [valid-organizations (filterv #(true? (get organizations-allowed (:id %))) organizations)] (rx/of (dt/teams-fetched teams) - (if (empty? valid-orgs) + (if (empty? valid-organizations) (modal/show {:type :alert :hide-actions? true - :message (tr "dashboard.team-organization.add.no-valid-orgs") - :title (tr "dashboard.select-org-modal.title")}) - (show-select-modal orgs-allowed)))))))))))))))) + :message (tr "dashboard.team-organization.add.no-valid-organizations") + :title (tr "dashboard.select-organization-modal.title")}) + (show-select-modal organizations-allowed)))))))))))))))) -(defn show-change-team-org-modal - "Fetches fresh team/org data, then shows the change-org modal - restricted to orgs where the user has permission, or the no-permission +(defn show-change-team-organization-modal + "Fetches fresh team/organization data, then shows the change-organization modal + restricted to organizations where the user has permission, or the no-permission modal if none qualify." [{:keys [team-id]}] - (ptk/reify ::show-change-team-org-modal + (ptk/reify ::show-change-team-organization-modal ptk/WatchEvent (watch [_ state _] (let [profile-id (dm/get-in state [:profile :id])] (->> (rp/cmd! :get-teams) (rx/mapcat (fn [teams] - (let [all-orgs (map dt/team->organization - (filter #(and (:is-default %) (:organization %)) teams)) + (let [all-organizations (map dt/team->organization + (filter #(and (:is-default %) (:organization %)) teams)) team (first (filter #(= (:id %) team-id) teams)) - source-org (:organization team) - current-org-id (:id source-org) - move-perm (dm/get-in source-org [:permissions :move-teams]) - source-owner-id (:owner-id source-org) - can-create? (fn [org] - (let [perm (dm/get-in org [:permissions :create-teams]) - is-own? (= profile-id (:owner-id org))] + source-organization (:organization team) + current-organization-id (:id source-organization) + move-perm (dm/get-in source-organization [:permissions :move-teams]) + source-owner-id (:owner-id source-organization) + can-create? (fn [organization] + (let [perm (dm/get-in organization [:permissions :create-teams]) + is-own? (= profile-id (:owner-id organization))] (or (= perm "any") is-own?))) - orgs-by-move (case move-perm - "never" - [] + organizations-by-move (case move-perm + "never" + [] - "myOrganizations" - (filter #(= source-owner-id (:owner-id %)) all-orgs) + "myOrganizations" + (filter #(= source-owner-id (:owner-id %)) all-organizations) - ;; Default to always-allowed behavior. - all-orgs) - orgs (filter can-create? orgs-by-move) - selectable-orgs (remove #(= current-org-id (:id %)) orgs) + ;; Default to always-allowed behavior. + all-organizations) + organizations (filter can-create? organizations-by-move) + selectable-organizations (remove #(= current-organization-id (:id %)) organizations) on-confirm (fn [organization-id] (st/emit! (add-team-to-organization {:team-id team-id :organization-id organization-id})))] - (if (empty? selectable-orgs) + (if (empty? selectable-organizations) (rx/of (dt/teams-fetched teams) - (modal/show :no-permission-modal {:type :no-orgs-change})) - (->> (fetch-orgs-allowed team-id selectable-orgs) + (modal/show :no-permission-modal {:type :no-organizations-change})) + (->> (fetch-organizations-allowed team-id selectable-organizations) (rx/mapcat - (fn [orgs-allowed] - (let [valid-orgs (filterv #(true? (get orgs-allowed (:id %))) selectable-orgs) - has-filtered? (< (count orgs) (count all-orgs)) + (fn [organizations-allowed] + (let [valid-organizations (filterv #(true? (get organizations-allowed (:id %))) selectable-organizations) + has-filtered? (< (count organizations) (count all-organizations)) extra-props (when has-filtered? - {:info-message-key "dashboard.select-org-modal.permission-info"})] + {:info-message-key "dashboard.select-organization-modal.permission-info"})] (rx/of (dt/teams-fetched teams) - (if (empty? valid-orgs) + (if (empty? valid-organizations) (modal/show {:type :alert :hide-actions? true - :message (tr "dashboard.team-organization.add.no-valid-orgs") - :title (tr "dashboard.change-org-modal.title")}) + :message (tr "dashboard.team-organization.add.no-valid-organizations") + :title (tr "dashboard.change-organization-modal.title")}) (modal/show :select-organization-modal - (merge {:organizations selectable-orgs - :orgs-allowed orgs-allowed - :current-organization-id current-org-id + (merge {:organizations selectable-organizations + :organizations-allowed organizations-allowed + :current-organization-id current-organization-id :on-confirm on-confirm :team-id team-id - :title-key "dashboard.change-org-modal.title" - :choose-key "dashboard.change-org-modal.choose" - :placeholder-key "dashboard.change-org-modal.select" - :accept-key "dashboard.change-org-modal.accept" + :title-key "dashboard.change-organization-modal.title" + :choose-key "dashboard.change-organization-modal.choose" + :placeholder-key "dashboard.change-organization-modal.select" + :accept-key "dashboard.change-organization-modal.accept" :cancel-key "labels.cancel"} extra-props))))))))))))))))) diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index a47ce5127f..1a85fc86cb 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -93,7 +93,7 @@ state)) (defn with-refreshed-team - "Fetches fresh team data from the server to ensure up-to-date org + "Fetches fresh team data from the server to ensure up-to-date organization permissions, updates the app state, and calls f with the fresh team data. Returns an observable of events." [team-id f] @@ -106,7 +106,7 @@ (f team))))))) (defn check-and-create-team - "Fetches fresh team data from the server to ensure up-to-date org + "Fetches fresh team data from the server to ensure up-to-date organization permissions, then shows the team-form modal or a no-permission modal." [team-id] (ptk/reify ::check-and-create-team @@ -116,23 +116,23 @@ (with-refreshed-team team-id (fn [team] (let [organization (:organization team) - in-org? (and (contains? cf/flags :nitrate) organization) - can-create? (if in-org? + in-organization? (and (contains? cf/flags :nitrate) organization) + can-create? (if in-organization? (nitrate-perms/allowed? :create-team - {:org-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms {:owner-id (:owner-id organization) + :permissions (:permissions organization)} :profile-id profile-id :team-perms (:permissions team)}) true)] (rx/of (if can-create? - (modal/show :team-form (if in-org? + (modal/show :team-form (if in-organization? {:organization-id (:id organization) :organization-name (:name organization)} {})) (modal/show :no-permission-modal {:type :create-team})))))))))) (defn check-and-delete-team - "Fetches fresh team data from the server to ensure up-to-date org + "Fetches fresh team data from the server to ensure up-to-date organization permissions, then shows the confirmation modal or a no-permission modal." [{:keys [team-id delete-fn]}] (ptk/reify ::check-and-delete-team @@ -141,17 +141,17 @@ (let [profile-id (dm/get-in state [:profile :id])] (with-refreshed-team team-id (fn [team] - (let [org (:organization team) - in-org? (and (contains? cf/flags :nitrate) org) - can-delete? (if in-org? + (let [organization (:organization team) + in-organization? (and (contains? cf/flags :nitrate) organization) + can-delete? (if in-organization? (nitrate-perms/allowed? :delete-team - {:org-perms {:owner-id (:owner-id org) - :permissions (:permissions org)} + {:organization-perms {:owner-id (:owner-id organization) + :permissions (:permissions organization)} :profile-id profile-id :team-perms (:permissions team)}) (boolean (dm/get-in team [:permissions :is-owner]))) - message (if in-org? - (tr "modals.delete-org-team-confirm.message" (:name org)) + message (if in-organization? + (tr "modals.delete-organization-team-confirm.message" (:name organization)) (tr "modals.delete-team-confirm.message"))] (rx/of (if can-delete? (modal/show @@ -163,7 +163,7 @@ (modal/show :no-permission-modal {:type :delete-team})))))))))) (defn- check-new-team-members-permission-and-show-invite-members - "Receives refreshed team data with up-to-date org + "Receives refreshed team data with up-to-date organization permissions, then shows the invite members modal or an appropriate alert." [{:keys [team invite-email origin]}] (ptk/reify ::check-new-team-members-permission-and-show-invite-members @@ -175,16 +175,16 @@ :invite-email invite-email}))] (if (and (contains? cf/flags :nitrate) (not (nitrate-perms/allowed? :add-anybody-to-team - {:org-perms (:organization team)}))) - (->> (rp/cmd! :all-org-members-in-team + {:organization-perms (:organization team)}))) + (->> (rp/cmd! :all-organization-members-in-team {:team-id (:id team) :organization-id (get-in team [:organization :id])}) (rx/mapcat - (fn [all-org-members-in-team?] - (if all-org-members-in-team? + (fn [all-organization-members-in-team?] + (if all-organization-members-in-team? (rx/of (modal/show {:type :alert - :message (tr "modals.invite-restricted-members.all-org-members-in-team" (get-in team [:organization :name])) + :message (tr "modals.invite-restricted-members.all-organization-members-in-team" (get-in team [:organization :name])) :accept-label (tr "labels.accept") :accept-style :primary :title (tr "modals.invite-team-member.title")})) @@ -192,7 +192,7 @@ show-invite))))) (defn check-and-invite-members - "Fetches fresh team data from the server to ensure up-to-date org + "Fetches fresh team data from the server to ensure up-to-date organization permissions, then shows invite-members modal or a permission error." [{:keys [team-id origin invite-email] :or {origin :team}}] @@ -202,10 +202,10 @@ (let [profile-id (dm/get-in state [:profile :id])] (with-refreshed-team team-id (fn [team] - (let [org (:organization team) + (let [organization (:organization team) can-invite? (nitrate-perms/can-send-invitations? {:nitrate-enabled? (contains? cf/flags :nitrate) - :organization org + :organization organization :profile-id profile-id :team-permissions (:permissions team)})] (rx/of (if can-invite? @@ -609,7 +609,7 @@ (rx/catch on-error)))))) (defn check-and-submit-invite-members - "Fetches fresh team data from the server to ensure up-to-date org + "Fetches fresh team data from the server to ensure up-to-date organization permissions, then submits member invitations or shows a restriction modal." [{:keys [team-id] :as params} origin do-invite-members] (ptk/reify ::check-and-submit-invite-members @@ -619,9 +619,9 @@ (with-refreshed-team team-id (fn [team] (if (not (nitrate-perms/allowed? :add-anybody-to-team - {:org-perms (:organization team)})) - (->> (rp/cmd! :check-org-members {:organization-id (get-in team [:organization :id]) - :emails (vec (:emails params))}) + {:organization-perms (:organization team)})) + (->> (rp/cmd! :check-organization-members {:organization-id (get-in team [:organization :id]) + :emails (vec (:emails params))}) (rx/mapcat (fn [result] (let [blocked (into [] (comp (filter (fn [[_ v]] (not v))) @@ -830,5 +830,5 @@ (defn team->organization [team] - (when-let [org (:organization team)] - (assoc org :default-team-id (:id team)))) + (when-let [organization (:organization team)] + (assoc organization :default-team-id (:id team)))) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 440b2496e3..d2b41889d8 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -137,7 +137,7 @@ :notification (dc/handle-notification msg) :team-role-change (handle-change-team-role msg) :team-membership-change (dc/team-membership-change msg) - :team-org-change (dc/handle-change-team-org msg) + :team-organization-change (dc/handle-change-team-organization msg) :organization-change-sso (dc/handle-organization-change-sso msg) nil)) diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index 3b1e38476f..52a17761c6 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -44,7 +44,7 @@ (st/emit! (da/login-from-token tdata))) (defmethod handle-token :team-invitation - [{:keys [state team-id org-team-id organization-name invitation-token] :as tdata}] + [{:keys [state team-id organization-team-id organization-name invitation-token] :as tdata}] (when-let [{:keys [origin props]} (:organization-invitation-audit tdata)] (st/emit! (ev/event @@ -54,11 +54,11 @@ (case state :created - (if org-team-id + (if organization-team-id (st/emit! (du/refresh-profile) - (dcm/go-to-dashboard-recent :team-id org-team-id) - (ntf/success (tr "auth.notifications.org-invitation-accepted" organization-name))) + (dcm/go-to-dashboard-recent :team-id organization-team-id) + (ntf/success (tr "auth.notifications.organization-invitation-accepted" organization-name))) (st/emit! (du/refresh-profile) (dcm/go-to-dashboard-recent :team-id team-id) @@ -99,10 +99,10 @@ (st/emit! (rt/nav :dashboard-recent {:team-id team-id})) - (= :org-not-found code) + (= :organization-not-found code) (st/emit! (rt/nav :dashboard-recent {:team-id team-id}) - (ntf/error (tr "errors.org-not-found"))) + (ntf/error (tr "errors.organization-not-found"))) (= :canceled-invitation code) (let [profile (:profile @st/state) diff --git a/frontend/src/app/main/ui/components/org_avatar.cljs b/frontend/src/app/main/ui/components/org_avatar.cljs deleted file mode 100644 index 4a9829de22..0000000000 --- a/frontend/src/app/main/ui/components/org_avatar.cljs +++ /dev/null @@ -1,41 +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 INC - -(ns app.main.ui.components.org-avatar - (:require-macros [app.main.style :as stl]) - (:require - [app.common.data :as d] - [rumext.v2 :as mf])) - -(mf/defc org-avatar* - [{:keys [org size]}] - (let [name (:name org) - custom-photo (:custom-photo org) - avatar-bg (:avatar-bg-url org) - initials (d/get-initials name)] - - (if custom-photo - [:img {:src custom-photo - :class (stl/css-case :org-avatar true - :org-avatar-custom true - :org-avatar-xxxl (= size "xxxl") - :org-avatar-xxl (= size "xxl") - :org-avatar-xl (= size "xl")) - :alt name}] - [:div {:class (stl/css-case :org-avatar true - :org-avatar-xxxl (= size "xxxl") - :org-avatar-xxl (= size "xxl") - :org-avatar-xl (= size "xl")) - :aria-hidden "true"} - [:img {:src avatar-bg - :class (stl/css :org-avatar-bg) - :alt ""}] - (when (seq initials) - [:span {:class (stl/css-case :org-avatar-initials true - :size-initials-xxxl (= size "xxxl") - :size-initials-xxl (= size "xxl") - :size-initials-xxl (= size "xl"))} ;; Keep the initials as xxl to make them legible - initials])]))) diff --git a/frontend/src/app/main/ui/components/organization_avatar.cljs b/frontend/src/app/main/ui/components/organization_avatar.cljs new file mode 100644 index 0000000000..8b883ca56d --- /dev/null +++ b/frontend/src/app/main/ui/components/organization_avatar.cljs @@ -0,0 +1,41 @@ +;; 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 + +(ns app.main.ui.components.organization-avatar + (:require-macros [app.main.style :as stl]) + (:require + [app.common.data :as d] + [rumext.v2 :as mf])) + +(mf/defc organization-avatar* + [{:keys [organization size]}] + (let [name (:name organization) + custom-photo (:custom-photo organization) + avatar-bg (:avatar-bg-url organization) + initials (d/get-initials name)] + + (if custom-photo + [:img {:src custom-photo + :class (stl/css-case :organization-avatar true + :organization-avatar-custom true + :organization-avatar-xxxl (= size "xxxl") + :organization-avatar-xxl (= size "xxl") + :organization-avatar-xl (= size "xl")) + :alt name}] + [:div {:class (stl/css-case :organization-avatar true + :organization-avatar-xxxl (= size "xxxl") + :organization-avatar-xxl (= size "xxl") + :organization-avatar-xl (= size "xl")) + :aria-hidden "true"} + [:img {:src avatar-bg + :class (stl/css :organization-avatar-bg) + :alt ""}] + (when (seq initials) + [:span {:class (stl/css-case :organization-avatar-initials true + :size-initials-xxxl (= size "xxxl") + :size-initials-xxl (= size "xxl") + :size-initials-xxl (= size "xl"))} ;; Keep the initials as xxl to make them legible + initials])]))) diff --git a/frontend/src/app/main/ui/components/org_avatar.scss b/frontend/src/app/main/ui/components/organization_avatar.scss similarity index 84% rename from frontend/src/app/main/ui/components/org_avatar.scss rename to frontend/src/app/main/ui/components/organization_avatar.scss index 4e9367c13c..4bc1d991d1 100644 --- a/frontend/src/app/main/ui/components/org_avatar.scss +++ b/frontend/src/app/main/ui/components/organization_avatar.scss @@ -8,35 +8,35 @@ @use "ds/colors.scss" as *; @use "ds/_utils.scss" as *; -.org-avatar { +.organization-avatar { position: relative; overflow: hidden; flex-shrink: 0; } -.org-avatar-custom { +.organization-avatar-custom { object-fit: cover; } -.org-avatar-xxxl { +.organization-avatar-xxxl { width: px2rem(30); height: px2rem(30); border-radius: px2rem(6); } -.org-avatar-xxl { +.organization-avatar-xxl { width: var(--sp-xxl); height: var(--sp-xxl); border-radius: px2rem(5); } -.org-avatar-xl { +.organization-avatar-xl { width: px2rem(22); height: px2rem(22); border-radius: var(--sp-xs); } -.org-avatar-bg { +.organization-avatar-bg { position: absolute; inset: 0; width: 100%; @@ -44,7 +44,7 @@ object-fit: cover; } -.org-avatar-initials { +.organization-avatar-initials { display: flex; justify-content: center; align-items: center; diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs index f39413a939..05d9990b69 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.cljs +++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs @@ -97,20 +97,20 @@ :form form :default default-member-id}])])) -(defn- make-leave-org-modal-form-schema [teams] +(defn- make-leave-organization-modal-form-schema [teams] (into [:map {:title "LeaveOrgModalForm"}] (for [team teams] [(keyword (str "member-id-" (:id team))) ::sm/text]))) -(mf/defc leave-and-reassign-org-modal +(mf/defc leave-and-reassign-organization-modal {::mf/register modal/components - ::mf/register-as :leave-and-reassign-org + ::mf/register-as :leave-and-reassign-organization ::mf/wrap [mf/memo]} [{:keys [profile teams-to-transfer num-teams-to-delete accept] :as props}] (let [schema (mf/with-memo [teams-to-transfer] - (make-leave-org-modal-form-schema teams-to-transfer)) + (make-leave-organization-modal-form-schema teams-to-transfer)) ;; Compute initial values for each team select team-fields (mf/with-memo [teams-to-transfer] (for [team teams-to-transfer] @@ -146,21 +146,21 @@ team-fields)] (accept {:teams-to-transfer teams-to-transfer})))] [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-org-container)} + [:div {:class (stl/css :modal-organization-container)} [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-org-title)} (tr "modals.before-leave-org.title")] + [:h2 {:class (stl/css :modal-organization-title)} (tr "modals.before-leave-organization.title")] [:button {:class (stl/css :modal-close-btn) :on-click modal/hide!} deprecated-icon/close]] [:div {:class (stl/css :modal-content)} (if (zero? num-teams-to-delete) - [:p {:class (stl/css :modal-org-msg)} - (tr "modals.leave-org-and-reassign.hint")] + [:p {:class (stl/css :modal-organization-msg)} + (tr "modals.leave-organization-and-reassign.hint")] [:* - [:p {:class (stl/css :modal-org-msg)} - (tr "modals.leave-org-and-reassign.hint-delete")] - [:p {:class (stl/css :modal-org-msg)} - (tr "modals.leave-org-and-reassign.hint-promote")]]) + [:p {:class (stl/css :modal-organization-msg)} + (tr "modals.leave-organization-and-reassign.hint-delete")] + [:p {:class (stl/css :modal-organization-msg)} + (tr "modals.leave-organization-and-reassign.hint-promote")]]) [:& fm/form {:form form} [:div {:class (stl/css :teams-container)} (for [{:keys [team field-name default-member-id]} team-fields] diff --git a/frontend/src/app/main/ui/dashboard/change_owner.scss b/frontend/src/app/main/ui/dashboard/change_owner.scss index a2497f77ed..01c8d875bd 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.scss +++ b/frontend/src/app/main/ui/dashboard/change_owner.scss @@ -78,20 +78,20 @@ width: 100%; } -.modal-org-container { +.modal-organization-container { @extend %modal-container-base; overflow-y: auto; max-height: $sz-512; } -.modal-org-title { +.modal-organization-title { @include t.use-typography("headline-large"); color: var(--modal-title-foreground-color); } -.modal-org-msg { +.modal-organization-msg { @include t.use-typography("body-large"); color: var(--modal-text-foreground-color); diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 297cbd134f..cd17b7b3bc 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -25,7 +25,7 @@ [app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]] [app.main.ui.components.link :refer [link*]] - [app.main.ui.components.org-avatar :refer [org-avatar*]] + [app.main.ui.components.organization-avatar :refer [organization-avatar*]] [app.main.ui.dashboard.comments :refer [comments-icon* comments-section]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] [app.main.ui.dashboard.project-menu :refer [project-menu*]] @@ -74,11 +74,11 @@ (def ^:private menu-icon (deprecated-icon/icon-xref :menu (stl/css :menu-icon))) -(def ^:private org-menu-icon - (deprecated-icon/icon-xref :menu (stl/css :org-menu-icon))) +(def ^:private organization-menu-icon + (deprecated-icon/icon-xref :menu (stl/css :organization-menu-icon))) -(def ^:private org-menu-icon-open - (deprecated-icon/icon-xref :menu (stl/css :org-menu-icon-open))) +(def ^:private organization-menu-icon-open + (deprecated-icon/icon-xref :menu (stl/css :organization-menu-icon-open))) (def ^:private pin-icon (deprecated-icon/icon-xref :pin (stl/css :pin-icon))) @@ -86,8 +86,8 @@ (def ^:private exit-icon (deprecated-icon/icon-xref :exit (stl/css :exit-icon))) -(def ^:private add-org-icon - (deprecated-icon/icon-xref :add (stl/css :add-org-icon))) +(def ^:private add-organization-icon + (deprecated-icon/icon-xref :add (stl/css :add-organization-icon))) (def ^:private arrow-up-right-icon (deprecated-icon/icon-xref :arrow-up-right (stl/css :arrow-up-right-icon))) @@ -312,7 +312,7 @@ account-age-days (dnt/account-age-days profile) - on-org-click + on-organization-click (mf/use-fn (fn [event] (let [team-id (-> (dom/get-current-target event) @@ -320,7 +320,7 @@ (uuid/parse))] (st/emit! (dcm/go-to-dashboard-recent :team-id team-id))))) - on-create-org-click + on-create-organization-click (mf/use-fn (mf/deps account-age-days profile subscription-type team-count) (fn [] @@ -348,15 +348,15 @@ (mf/use-fn (mf/deps organization profile) (fn [] - ;; Navigate to active org if user owns it, otherwise to last visited org + ;; Navigate to active organization if user owns it, otherwise to last visited organization (if (and (:id organization) (= (:id profile) (:owner-id organization))) (dnt/go-to-nitrate-ac {:organization-id (:id organization) :organization-slug (:slug organization)}) (dnt/go-to-nitrate-ac)))) - empty-org (d/seek #(nil? (:id %)) organizations) - default-team-id (or (:default-team-id empty-org) + empty-organization (d/seek #(nil? (:id %)) organizations) + default-team-id (or (:default-team-id empty-organization) (:default-team-id profile)) organizations (filter :id organizations) @@ -365,9 +365,9 @@ [:> dropdown-menu* props - [:> dropdown-menu-item* {:on-click on-org-click + [:> dropdown-menu-item* {:on-click on-organization-click :data-value default-team-id - :class (stl/css :org-dropdown-item)} + :class (stl/css :organization-dropdown-item)} [:span {:class (stl/css :my-teams-icon)} [:> raw-svg* {:id penpot-logo-icon-subtle}]] [:span {:class (stl/css :team-text) @@ -378,28 +378,28 @@ (when (seq organizations) [:* [:hr {:role "separator" :class (stl/css :team-separator)}] - [:li {:role "presentation" :class (stl/css :org-section-label)} + [:li {:role "presentation" :class (stl/css :organization-section-label)} (tr "dashboard.section.organizations")]]) - (for [org-item organizations] - [:> dropdown-menu-item* {:on-click on-org-click - :data-value (:default-team-id org-item) - :class (stl/css :org-dropdown-item) - :key (str (:default-team-id org-item))} - [:> org-avatar* {:org org-item :size "xxl"}] + (for [organization-item organizations] + [:> dropdown-menu-item* {:on-click on-organization-click + :data-value (:default-team-id organization-item) + :class (stl/css :organization-dropdown-item) + :key (str (:default-team-id organization-item))} + [:> organization-avatar* {:organization organization-item :size "xxl"}] [:span {:class (stl/css :team-text) - :title (:name org-item)} (:name org-item)] - (when (= (:default-team-id org-item) (:default-team-id organization)) + :title (:name organization-item)} (:name organization-item)] + (when (= (:default-team-id organization-item) (:default-team-id organization)) tick-icon)]) [:hr {:role "separator" :class (stl/css :team-separator)}] - [:> dropdown-menu-item* {:on-click on-create-org-click - :class (stl/css :org-dropdown-item :action)} - [:span {:class (stl/css :icon-wrapper)} add-org-icon] - [:span {:class (stl/css :team-text)} (tr "dashboard.create-new-org")]] + [:> dropdown-menu-item* {:on-click on-create-organization-click + :class (stl/css :organization-dropdown-item :action)} + [:span {:class (stl/css :icon-wrapper)} add-organization-icon] + [:span {:class (stl/css :team-text)} (tr "dashboard.create-new-organization")]] (when is-valid-license? [:> dropdown-menu-item* {:on-click on-go-to-cc-click - :class (stl/css :org-dropdown-item :action)} + :class (stl/css :organization-dropdown-item :action)} [:span {:class (stl/css :icon-wrapper)} arrow-up-right-icon] [:span {:class (stl/css :team-text)} (tr "dashboard.go-to-admin-console")]])])) @@ -446,7 +446,7 @@ (seq (remove :is-default (vals teams)))) [:* [:hr {:role "separator" :class (stl/css :team-separator)}] - [:li {:role "presentation" :class (stl/css :org-section-label)} + [:li {:role "presentation" :class (stl/css :organization-section-label)} (tr "dashboard.section.teams")]]) (for [team-item (remove :is-default (vals teams))] @@ -624,7 +624,7 @@ :data-testid "delete-team"} (tr "dashboard.delete-team")])])) -(mf/defc org-options-dropdown* +(mf/defc organization-options-dropdown* {::mf/private true} [{:keys [organization profile teams] :rest props}] (let [default-team-id (mf/with-memo [teams] @@ -649,8 +649,8 @@ (fn [error] (let [code (-> error ex-data :code) ;; Map error codes to their translation keys - error-map {:not-valid-teams "errors.org-leave.no-valid-teams" - :org-owner-cannot-leave "errors.org-leave.org-owner-cannot-leave" + error-map {:not-valid-teams "errors.organization-leave.no-valid-teams" + :organization-owner-cannot-leave "errors.organization-leave.organization-owner-cannot-leave" :only-owner-can-delete-team "errors.team-leave.only-owner-can-delete" :no-enough-members-for-leave "errors.team-leave.insufficient-members" :member-does-not-exist "errors.team-leave.member-does-not-exists" @@ -678,27 +678,27 @@ (map :id))] - (st/emit! (dnt/leave-org {:id (:id organization) - :name (:name organization) - :default-team-id default-team-id - :teams-to-delete teams-to-delete - :teams-to-leave teams-to-leave - :member-added-at member-added-at - :organization-member-count-before - organization-member-count-before - :on-error on-error}))))) + (st/emit! (dnt/leave-organization {:id (:id organization) + :name (:name organization) + :default-team-id default-team-id + :teams-to-delete teams-to-delete + :teams-to-leave teams-to-leave + :member-added-at member-added-at + :organization-member-count-before + organization-member-count-before + :on-error on-error}))))) on-leave-clicked (mf/use-fn (mf/deps leave-fn profile organization default-team-id teams-to-transfer on-error owned-teams-members-loaded?) (fn [] (when owned-teams-members-loaded? - (st/emit! (dnt/show-leave-org-modal {:organization organization - :profile profile - :default-team-id default-team-id - :leave-fn leave-fn - :teams-to-transfer teams-to-transfer - :on-error on-error})))))] + (st/emit! (dnt/show-leave-organization-modal {:organization organization + :profile profile + :default-team-id default-team-id + :leave-fn leave-fn + :teams-to-transfer teams-to-transfer + :on-error on-error})))))] (mf/use-effect (mf/deps owned-teams) (fn [] @@ -710,10 +710,10 @@ [:> dropdown-menu-item* {:on-click on-leave-clicked :class (stl/css :team-options-item)} - (tr "dashboard.leave-org")]])) + (tr "dashboard.leave-organization")]])) -(mf/defc sidebar-org-switch* +(mf/defc sidebar-organization-switch* [{:keys [team profile]}] (let [teams (mf/deref refs/teams) @@ -723,70 +723,70 @@ account-age-days (dnt/account-age-days profile) - current-org (dtm/team->organization team) + current-organization (dtm/team->organization team) - ;; Find the "your-penpot" teams, and transform them in orgs. When + ;; Find the "your-penpot" teams, and transform them in organizations. When ;; the selected team is directly accessible but not listed in - ;; membership teams, include only its org so the org selector can + ;; membership teams, include only its organization so the organization selector can ;; show the current selection without leaking the team into the ;; teams dropdown. - orgs (mf/with-memo [teams current-org] - (cond-> (->> teams - vals - (filter :is-default) - (map dtm/team->organization) - (d/index-by :id)) - (:id current-org) - (assoc (:id current-org) current-org))) + organizations (mf/with-memo [teams current-organization] + (cond-> (->> teams + vals + (filter :is-default) + (map dtm/team->organization) + (d/index-by :id)) + (:id current-organization) + (assoc (:id current-organization) current-organization))) show-dropdown? (or (dnt/is-valid-license? profile) - (> (count orgs) 1)) + (> (count organizations) 1)) - org-teams (mf/with-memo [teams current-org] - (->> teams - vals - (filter #(= (dm/get-in % [:organization :id]) (:id current-org))))) + organization-teams (mf/with-memo [teams current-organization] + (->> teams + vals + (filter #(= (dm/get-in % [:organization :id]) (:id current-organization))))) - default-org? (nil? (:id current-org)) + default-organization? (nil? (:id current-organization)) - show-options? (and (not default-org?) - (not= (:id profile) (:owner-id current-org))) + show-options? (and (not default-organization?) + (not= (:id profile) (:owner-id current-organization))) - show-orgs-menu* + show-organizations-menu* (mf/use-state false) - show-orgs-menu? - (deref show-orgs-menu*) + show-organizations-menu? + (deref show-organizations-menu*) - orgs-rect* + organizations-rect* (mf/use-state nil) - orgs-portal-container + organizations-portal-container (hooks/use-portal-container :popup) - show-org-options-menu* + show-organization-options-menu* (mf/use-state false) - show-org-options-menu? - (deref show-org-options-menu*) + show-organization-options-menu? + (deref show-organization-options-menu*) on-show-options-click (mf/use-fn (fn [event] (dom/stop-propagation event) - (swap! show-org-options-menu* not))) + (swap! show-organization-options-menu* not))) - close-org-options-menu - (mf/use-fn #(reset! show-org-options-menu* false)) + close-organization-options-menu + (mf/use-fn #(reset! show-organization-options-menu* false)) - on-show-orgs-click + on-show-organizations-click (mf/use-fn (fn [event] (dom/stop-propagation event) - (reset! orgs-rect* (dom/get-bounding-rect (dom/get-current-target event))) - (swap! show-orgs-menu* not))) + (reset! organizations-rect* (dom/get-bounding-rect (dom/get-current-target event))) + (swap! show-organizations-menu* not))) - on-show-orgs-keydown + on-show-organizations-keydown (mf/use-fn (fn [event] (when (or (kbd/space? event) @@ -795,10 +795,10 @@ (dom/stop-propagation event) (some-> (dom/get-current-target event) (dom/click))))) - close-orgs-menu - (mf/use-fn #(reset! show-orgs-menu* false)) + close-organizations-menu + (mf/use-fn #(reset! show-organizations-menu* false)) - on-create-org-click + on-create-organization-click (mf/use-fn (mf/deps account-age-days profile subscription-type team-count) (fn [] @@ -822,36 +822,36 @@ (= subscription-type "unlimited") (assoc :show-contact-sales-option true)))))))] (if show-dropdown? - [:div {:class (stl/css :sidebar-org-switch)} - [:div {:class (stl/css :org-switch-content)} - [:button {:class (stl/css-case :current-org true :current-org-no-options (not show-options?)) - :on-click on-show-orgs-click - :on-key-down on-show-orgs-keydown - :aria-expanded show-orgs-menu? + [:div {:class (stl/css :sidebar-organization-switch)} + [:div {:class (stl/css :organization-switch-content)} + [:button {:class (stl/css-case :current-organization true :current-organization-no-options (not show-options?)) + :on-click on-show-organizations-click + :on-key-down on-show-organizations-keydown + :aria-expanded show-organizations-menu? :aria-haspopup "menu"} [:div {:class (stl/css :team-name)} - (if default-org? + (if default-organization? [:* [:span {:class (stl/css :my-teams-icon-xxxl)} [:> raw-svg* {:id penpot-logo-icon-subtle}]] [:span {:class (stl/css :team-text)} (tr "dashboard.my-teams")]] [:* - [:> org-avatar* {:org current-org :size "xxxl"}] + [:> organization-avatar* {:organization current-organization :size "xxxl"}] [:span {:class (stl/css :team-text)} - (:name current-org)]])] + (:name current-organization)]])] arrow-icon] (when show-options? [:> button* {:variant "ghost" :type "button" - :class (stl/css :org-options-btn) + :class (stl/css :organization-options-btn) :on-click on-show-options-click} - (if show-org-options-menu? org-menu-icon-open org-menu-icon)])] + (if show-organization-options-menu? organization-menu-icon-open organization-menu-icon)])] - ;; Orgs Dropdown - (when show-orgs-menu? - (let [rect (deref orgs-rect*)] + ;; Organizations dropdown + (when show-organizations-menu? + (let [rect (deref organizations-rect*)] (mf/portal (mf/html [:div {:style {:position "fixed" @@ -859,40 +859,40 @@ :left (dm/str (:left rect) "px") :width (dm/str (:width rect) "px")}} [:> organizations-selector-dropdown* {:show true - :on-close close-orgs-menu + :on-close close-organizations-menu :id "organizations-list" :class (stl/css :dropdown :teams-dropdown) - :organization current-org + :organization current-organization :profile profile - :organizations (->> (vals orgs) + :organizations (->> (vals organizations) (sort-by (juxt (fn [o] (str/lower (:name o ""))) :id)))}]]) - orgs-portal-container))) - ;; Orgs options - [:> org-options-dropdown* {:show show-org-options-menu? - :on-close close-org-options-menu - :id "team-options" - :class (stl/css :dropdown :options-dropdown) - :organization current-org - :profile profile - :teams org-teams}]] - [:div {:class (stl/css :selected-org)} - [:span {:class (stl/css :org-penpot-icon)} + organizations-portal-container))) + ;; Organization options + [:> organization-options-dropdown* {:show show-organization-options-menu? + :on-close close-organization-options-menu + :id "team-options" + :class (stl/css :dropdown :options-dropdown) + :organization current-organization + :profile profile + :teams organization-teams}]] + [:div {:class (stl/css :selected-organization)} + [:span {:class (stl/css :organization-penpot-icon)} [:> raw-svg* {:id penpot-logo-icon}]] "Penpot" [:> button* {:variant "ghost" :type "button" - :class (stl/css :create-org) - :on-click on-create-org-click} (tr "dashboard.plus-create-new-org")]]))) + :class (stl/css :create-organization) + :on-click on-create-organization-click} (tr "dashboard.plus-create-new-organization")]]))) (mf/defc sidebar-team-switch* [{:keys [team profile]}] (let [nitrate? (contains? cf/flags :nitrate) - org (:organization team) - organization-id (when nitrate? (:id org)) + organization (:organization team) + organization-id (when nitrate? (:id organization)) teams (cond->> (mf/deref refs/teams) nitrate? (filter #(= (dm/get-in (val %) [:organization :id]) organization-id)) @@ -1111,8 +1111,8 @@ [:* [:div {:class (stl/css :sidebar-content-wrapper)} (when nitrate? - [:div {:class (stl/css :orgs-container)} - [:> sidebar-org-switch* {:team team :profile profile}]]) + [:div {:class (stl/css :organizations-container)} + [:> sidebar-organization-switch* {:team team :profile profile}]]) [:div {:ref container :class (stl/css-case :sidebar-content true :sidebar-content-nitrate nitrate?)} [:> sidebar-team-switch* {:team team :profile profile}] diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index f1cafcfe9a..cc2e882e51 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -196,7 +196,7 @@ grid-template-columns: 1fr auto; } -.org-dropdown-item { +.organization-dropdown-item { @extend %menu-item-base; display: grid; @@ -238,7 +238,7 @@ margin: 0; } -.org-section-label { +.organization-section-label { @include t.use-typography("headline-small"); color: var(--color-foreground-secondary); @@ -559,7 +559,7 @@ stroke: var(--icon-foreground); } -.add-org-icon { +.add-organization-icon { @extend %button-icon; width: var(--sp-l); @@ -606,7 +606,7 @@ color: var(--color-accent-tertiary); } -.orgs-container { +.organizations-container { align-items: center; display: flex; height: calc(2 * var(--sp-xxxl)); @@ -615,7 +615,7 @@ padding: 0 var(--sp-l); } -.selected-org { +.selected-organization { @include t.use-typography("body-medium"); color: var(--color-foreground-primary); @@ -626,12 +626,12 @@ gap: var(--sp-s); } -.create-org { +.create-organization { margin-inline-start: auto; text-transform: uppercase; } -.org-penpot-icon { +.organization-penpot-icon { display: flex; justify-content: center; align-items: center; @@ -647,7 +647,7 @@ } } -.org-icon { +.organization-icon { display: flex; justify-content: center; align-items: center; @@ -685,12 +685,12 @@ } } -.sidebar-org-switch { +.sidebar-organization-switch { position: relative; width: 100%; } -.current-org { +.current-organization { @include deprecated.button-style; text-transform: none; @@ -703,15 +703,15 @@ width: 100%; } -.current-org-no-options { +.current-organization-no-options { gap: 0; } -.current-org .arrow-icon { +.current-organization .arrow-icon { margin-inline-end: var(--sp-xs); } -.org-options { +.organization-options { display: flex; justify-content: center; align-items: center; @@ -720,7 +720,7 @@ height: 100%; } -.org-switch-content { +.organization-switch-content { display: grid; grid-template-columns: 1fr auto; align-items: center; @@ -728,7 +728,7 @@ width: 100%; } -.org-options-btn { +.organization-options-btn { --icon-stroke: var(--icon-foreground); display: flex; @@ -742,13 +742,13 @@ } } -.org-menu-icon { +.organization-menu-icon { @extend %button-icon; stroke: var(--icon-stroke); } -.org-menu-icon-open { +.organization-menu-icon-open { @extend %button-icon; stroke: var(--color-accent-primary); diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs index 6537f738f6..3886e15fc7 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.cljs +++ b/frontend/src/app/main/ui/dashboard/subscription.cljs @@ -158,11 +158,11 @@ teams-loaded? (seq teams) - no-orgs-created? (mf/with-memo [teams] - (and (seq teams) - (->> teams - vals - (not-any? :organization)))) + no-organizations-created? (mf/with-memo [teams] + (and (seq teams) + (->> teams + vals + (not-any? :organization)))) handle-click (mf/use-fn @@ -199,17 +199,17 @@ [:* ;; TODO add translations for this texts when we have the definitive ones - (if (and nitrate? teams-loaded? no-orgs-created? (not show-subscription-warning?)) + (if (and nitrate? teams-loaded? no-organizations-created? (not show-subscription-warning?)) ;; Banner for users with active nitrate license but no organizations created [:div {:class (stl/css :nitrate-banner :highlighted)} [:div {:class (stl/css :nitrate-content)} [:span {:class (stl/css :nitrate-title)} (tr "subscription.banner.see-enterprise")]] [:div {:class (stl/css :nitrate-content)} - [:span {:class (stl/css :nitrate-info)} (tr "subscription.banner.create-org-info")] + [:span {:class (stl/css :nitrate-info)} (tr "subscription.banner.create-organization-info")] [:> button* {:variant "primary" :type "button" :class (stl/css :nitrate-bottom-button) - :on-click handle-go-to-cc} (tr "nitrate.activation-success.create-org")]]] + :on-click handle-go-to-cc} (tr "nitrate.activation-success.create-organization")]]] ;; Banner for users without nitrate license (when (not nitrate?) diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index d100c2af27..513aa29d91 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -24,7 +24,7 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.forms :as fm] - [app.main.ui.components.org-avatar :refer [org-avatar*]] + [app.main.ui.components.organization-avatar :refer [organization-avatar*]] [app.main.ui.dashboard.change-owner] [app.main.ui.dashboard.subscription :refer [members-cta* show-subscription-members-banner? @@ -56,8 +56,8 @@ (def ^:private menu-icon (deprecated-icon/icon-xref :menu (stl/css :menu-icon))) -(def ^:private org-menu-icon - (deprecated-icon/icon-xref :menu (stl/css :org-menu-icon))) +(def ^:private organization-menu-icon + (deprecated-icon/icon-xref :menu (stl/css :organization-menu-icon))) (def ^:private warning-icon (deprecated-icon/icon-xref :msg-warning (stl/css :warning-icon))) @@ -888,41 +888,41 @@ (def schema:organization-form [:map {:title "SelectOrgForm"} [:selected-id ::sm/uuid]]) -(mf/defc render-org-combobox-avatar* +(mf/defc render-organization-combobox-avatar* [{:keys [avatar]}] - [:> org-avatar* {:org (:organization avatar) - :size (:size avatar)}]) + [:> organization-avatar* {:organization (:organization avatar) + :size (:size avatar)}]) (mf/defc select-organization-modal {::mf/register modal/components ::mf/register-as :select-organization-modal} - [{:keys [organizations orgs-allowed current-organization-id on-confirm title-key text-key choose-key placeholder-key accept-key cancel-key info-message-key team-id]}] + [{:keys [organizations organizations-allowed current-organization-id on-confirm title-key text-key choose-key placeholder-key accept-key cancel-key info-message-key team-id]}] (let [valid-organizations (mf/with-memo [organizations] (remove #(= (:id %) current-organization-id) organizations)) - options (mf/with-memo [valid-organizations orgs-allowed] + options (mf/with-memo [valid-organizations organizations-allowed] (mapv (fn [organization] - (let [org-id (:id organization) - ;; orgs-allowed is a map of org-id and a boolean indicating if it is allowed - enabled? (or (nil? orgs-allowed) - (true? (get orgs-allowed org-id)))] - (cond-> {:id (str org-id) + (let [organization-id (:id organization) + ;; organizations-allowed is a map of organization-id and a boolean indicating if it is allowed + enabled? (or (nil? organizations-allowed) + (true? (get organizations-allowed organization-id)))] + (cond-> {:id (str organization-id) :label (:name organization) :disabled (not enabled?) :dimmed (not enabled?) - :avatar {:render-fn render-org-combobox-avatar* + :avatar {:render-fn render-organization-combobox-avatar* :organization organization :size "xl"}} (not enabled?) - (assoc :title (tr "dashboard.team-organization.disabled-org-tooltip"))))) + (assoc :title (tr "dashboard.team-organization.disabled-organization-tooltip"))))) valid-organizations)) form (fm/use-form :schema schema:organization-form :initial {}) warning-info* (mf/use-state nil) warning-info (deref warning-info*) - selected-org (mf/with-memo [warning-info valid-organizations] - (when warning-info - (d/seek #(= (:id %) (:organization-id warning-info)) valid-organizations))) + selected-organization (mf/with-memo [warning-info valid-organizations] + (when warning-info + (d/seek #(= (:id %) (:organization-id warning-info)) valid-organizations))) on-change (mf/use-fn @@ -931,13 +931,13 @@ (uforms/on-input-change form :selected-id id) ;; Check for external invitations when selection changes (when (and team-id id) - (let [org-id (d/parse-uuid id)] + (let [organization-id (d/parse-uuid id)] (->> (rp/cmd! :check-team-external-invitations {:team-id team-id - :organization-id org-id}) + :organization-id organization-id}) (rx/subs! (fn [result] - (reset! warning-info* (assoc result :organization-id org-id))) + (reset! warning-info* (assoc result :organization-id organization-id))) (fn [_] (reset! warning-info* nil)))))))) @@ -947,22 +947,22 @@ (fn [] (on-confirm (dm/get-in @form [:clean-data :selected-id]))))] [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-select-org-container :modal-container)} - [:div {:class (stl/css :modal-select-org-header)} - [:h2 {:class (stl/css :modal-select-org-title)} + [:div {:class (stl/css :modal-select-organization-container :modal-container)} + [:div {:class (stl/css :modal-select-organization-header)} + [:h2 {:class (stl/css :modal-select-organization-title)} (tr title-key)] [:button {:class (stl/css :modal-close-btn) :on-click modal/hide!} deprecated-icon/close]] (when text-key - [:div {:class (stl/css :modal-content :modal-select-org-text)} (tr text-key)]) + [:div {:class (stl/css :modal-content :modal-select-organization-text)} (tr text-key)]) - [:div {:class (stl/css :modal-select-org-body)} + [:div {:class (stl/css :modal-select-organization-body)} (when info-message-key - [:div {:class (stl/css :modal-select-org-info)} + [:div {:class (stl/css :modal-select-organization-info)} (tr info-message-key)]) - [:div {:class (stl/css :modal-select-org-content)} + [:div {:class (stl/css :modal-select-organization-content)} (tr choose-key)] [:> combobox* {:id "selected-id" :class (stl/css :team-member) @@ -976,14 +976,14 @@ (when (and warning-info (:has-external-invitations warning-info) (not (:allows-anybody warning-info)) - selected-org) - [:div {:class (stl/css :modal-select-org-warning)} + selected-organization) + [:div {:class (stl/css :modal-select-organization-warning)} [:& context-notification - {:content (tr "dashboard.select-org-modal.external-invitations-will-be-canceled") + {:content (tr "dashboard.select-organization-modal.external-invitations-will-be-canceled") :class (stl/css :external-invitations-warning) :level :warning}] - [:div {:class (stl/css :modal-select-org-content)} - (tr "dashboard.select-org-modal.external-invitations-warning" (:name selected-org))]])] + [:div {:class (stl/css :modal-select-organization-content)} + (tr "dashboard.select-organization-modal.external-invitations-warning" (:name selected-organization))]])] [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons :modal-invitation-action-buttons)} @@ -1526,15 +1526,15 @@ (filter :organization) (map dtm/team->organization))) - ;; Filter to orgs where user is allowed to create/add teams + ;; Filter to organizations where user is allowed to create/add teams organizations (mf/with-memo [all-organizations profile-id] (->> all-organizations - (filter (fn [org] - (let [perm (get-in org [:permissions :create-teams]) - is-owner? (= profile-id (:owner-id org))] + (filter (fn [organization] + (let [perm (get-in organization [:permissions :create-teams]) + is-owner? (= profile-id (:owner-id organization))] (or (= perm "any") is-owner?)))))) - ;; Keep parity with UX requirement: hide only when user belongs to one org. + ;; Keep parity with UX requirement: hide only when user belongs to one organization. can-change-organization? (mf/with-memo [all-organizations] (> (count all-organizations) 1)) @@ -1543,20 +1543,20 @@ (pos? (count all-organizations)) (not (:is-default team)))) - show-org-options-menu* + show-organization-options-menu* (mf/use-state false) - show-org-options-menu? - (deref show-org-options-menu*) + show-organization-options-menu? + (deref show-organization-options-menu*) on-show-options-click (mf/use-fn (fn [event] (dom/stop-propagation event) - (swap! show-org-options-menu* not))) + (swap! show-organization-options-menu* not))) - close-org-options-menu - (mf/use-fn #(reset! show-org-options-menu* false)) + close-organization-options-menu + (mf/use-fn #(reset! show-organization-options-menu* false)) on-image-click (mf/use-fn #(dom/click (mf/ref-val finput))) @@ -1565,11 +1565,11 @@ (fn [file] (st/emit! (dtm/update-team-photo file))) - on-remove-team-from-org + on-remove-team-from-organization (mf/use-fn (mf/deps team) (fn [] - (st/emit! (dnt/show-remove-team-from-org-modal {:team-id (:id team)})))) + (st/emit! (dnt/show-remove-team-from-organization-modal {:team-id (:id team)})))) on-add-team-to-organization (mf/use-fn @@ -1577,11 +1577,11 @@ (fn [] (st/emit! (dnt/show-add-team-to-organization-modal {:team-id (:id team)})))) - on-change-team-org + on-change-team-organization (mf/use-fn (mf/deps team) (fn [] - (st/emit! (dnt/show-change-team-org-modal {:team-id (:id team)}))))] + (st/emit! (dnt/show-change-team-organization-modal {:team-id (:id team)}))))] (mf/with-effect [team] (dom/set-html-title (tr "title.team-settings" @@ -1622,8 +1622,8 @@ (let [organization (:organization team)] (if organization [:div {:class (stl/css :block-content)} - [:div {:class (stl/css :org-block-content)} - [:> org-avatar* {:org (dtm/team->organization team) :size "xxxl"}] + [:div {:class (stl/css :organization-block-content)} + [:> organization-avatar* {:organization (dtm/team->organization team) :size "xxxl"}] [:span {:class (stl/css :block-text)} (:name organization)] @@ -1631,19 +1631,19 @@ [:* [:> button* {:variant "ghost" :type "button" - :class (stl/css-case :org-options-btn (not show-org-options-menu?) :org-options-btn-open show-org-options-menu?) + :class (stl/css-case :organization-options-btn (not show-organization-options-menu?) :organization-options-btn-open show-organization-options-menu?) :on-click on-show-options-click} - org-menu-icon + organization-menu-icon - [:& dropdown {:show show-org-options-menu? :on-close close-org-options-menu :dropdown-id "org-options"} - [:ul {:class (stl/css :org-dropdown) + [:& dropdown {:show show-organization-options-menu? :on-close close-organization-options-menu :dropdown-id "organization-options"} + [:ul {:class (stl/css :organization-dropdown) :role "listbox"} (when can-change-organization? - [:li {:on-click on-change-team-org - :class (stl/css :org-dropdown-item)} + [:li {:on-click on-change-team-organization + :class (stl/css :organization-dropdown-item)} (tr "dashboard.team-organization.change")]) - [:li {:on-click on-remove-team-from-org - :class (stl/css :org-dropdown-item)} + [:li {:on-click on-remove-team-from-organization + :class (stl/css :organization-dropdown-item)} (tr "dashboard.team-organization.remove")]]]]])]] [:* [:div {:class (stl/css :block-content)} diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index a1bf36a814..14366781c4 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -932,47 +932,47 @@ // SELECT ORGANIZATION MODAL -.modal-select-org-container { +.modal-select-organization-container { display: flex; flex-direction: column; inline-size: $sz-512; } -.modal-select-org-header { +.modal-select-organization-header { margin-block-end: var(--sp-xxxl); } -.modal-select-org-body { +.modal-select-organization-body { display: flex; flex-direction: column; gap: var(--sp-s); } -.modal-select-org-warning { +.modal-select-organization-warning { display: flex; flex-direction: column; gap: var(--sp-xxl); } -.modal-select-org-content { +.modal-select-organization-content { @include t.use-typography("body-medium"); color: var(--color-foreground-secondary); } -.modal-select-org-info { +.modal-select-organization-info { @include t.use-typography("body-medium"); color: var(--color-foreground-secondary); } -.modal-select-org-title { +.modal-select-organization-title { @include t.use-typography("headline-large"); color: var(--color-foreground-primary); } -.modal-select-org-text { +.modal-select-organization-text { @include t.use-typography("body-medium"); color: var(--color-foreground-secondary); @@ -984,7 +984,7 @@ // ORGANIZATIONS SETTINGS -.org-block-content { +.organization-block-content { display: grid; grid-template-columns: var(--sp-xxxl) 1fr var(--sp-xxxl); align-items: center; @@ -992,7 +992,7 @@ inline-size: max-content; } -.org-options-btn { +.organization-options-btn { padding: 0; justify-content: center; @@ -1003,7 +1003,7 @@ } } -.org-options-btn-open { +.organization-options-btn-open { padding: 0; justify-content: center; @@ -1013,7 +1013,7 @@ position: relative; } -.org-menu-icon { +.organization-menu-icon { display: flex; justify-content: center; align-items: center; @@ -1025,7 +1025,7 @@ stroke: var(--stroke-color); } -.org-dropdown { +.organization-dropdown { box-shadow: var(--el-shadow-dark); display: flex; flex-direction: column; @@ -1043,7 +1043,7 @@ min-inline-size: $sz-160; } -.org-dropdown-item { +.organization-dropdown-item { @include t.use-typography("body-small"); display: flex; diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 4d60516a7c..21a9708c4d 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -152,10 +152,10 @@ (tr "dashboard.no-permission-create-team.message" organization-name)] :delete-team [(tr "dashboard.delete-team") (tr "dashboard.no-permission-delete-team.message" organization-name)] - :no-orgs-create [(tr "dashboard.select-org-modal.title") - (tr "dashboard.no-org-allows-create-team.message")] - :no-orgs-change [(tr "dashboard.change-org-modal.title") - (tr "dashboard.no-permission-move-team.message" organization-name)])] + :no-organizations-create [(tr "dashboard.select-organization-modal.title") + (tr "dashboard.no-organization-allows-create-team.message")] + :no-organizations-change [(tr "dashboard.change-organization-modal.title") + (tr "dashboard.no-permission-move-team.message" organization-name)])] [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} diff --git a/frontend/src/app/main/ui/ds/controls/combobox.mdx b/frontend/src/app/main/ui/ds/controls/combobox.mdx index 0537d7c000..8b30516547 100644 --- a/frontend/src/app/main/ui/ds/controls/combobox.mdx +++ b/frontend/src/app/main/ui/ds/controls/combobox.mdx @@ -61,23 +61,23 @@ Avatar rendering is defined per option with `:render-fn`, so each avatar type ca ```clj ;; Example renderer for organization avatars -(mf/defc render-org-avatar* +(mf/defc render-organization-avatar* [{:keys [avatar]}] (when (= :organization (:type avatar)) - [:> org-avatar* {:org (:organization avatar) - :size (:size avatar)}])) + [:> organization-avatar* {:organization (:organization avatar) + :size (:size avatar)}])) [:> combobox* {:options [{:label "Design Team" - :id "org-design" - :avatar {:render-fn render-org-avatar* + :id "organization-design" + :avatar {:render-fn render-organization-avatar* :size "s" :organization {:name "Design Team" :organization-avatar-bg-url "https://example.com/avatar-bg.svg" :organization-custom-photo nil}}} {:label "Engineering" - :id "org-engineering" - :avatar {:render-fn render-org-avatar* + :id "organization-engineering" + :avatar {:render-fn render-organization-avatar* :size "s" :organization {:name "Engineering" :organization-avatar-bg-url nil diff --git a/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs index 43fdc77092..8489cdec4a 100644 --- a/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs +++ b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs @@ -32,7 +32,7 @@ date-str (when cancel-at (ct/format-inst cancel-at "d MMMM, yyyy")) - on-create-org + on-create-organization (mf/use-fn (fn [] (modal/hide!) @@ -64,6 +64,6 @@ (tr "nitrate.activation-success.enjoy")] [:> button* {:variant "primary" - :on-click on-create-org + :on-click on-create-organization :class (stl/css :modal-button)} - (tr "nitrate.activation-success.create-org")]]]]])) + (tr "nitrate.activation-success.create-organization")]]]]])) diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index 4c7dace38a..32efbca332 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -13,7 +13,7 @@ [app.main.data.profile :as du] [app.main.repo :as rp] [app.main.store :as st] - [app.main.ui.components.org-avatar :refer [org-avatar*]] + [app.main.ui.components.organization-avatar :refer [organization-avatar*]] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.icons :as deprecated-icon] [app.main.ui.notifications.context-notification :refer [context-notification]] @@ -33,9 +33,9 @@ {::mf/register modal/components ::mf/register-as :delete-account} [] - (let [orgs* (mf/use-state nil) - orgs (deref orgs*) - has-orgs? (seq orgs) + (let [organizations* (mf/use-state nil) + organizations (deref organizations*) + has-organizations? (seq organizations) expanded* (mf/use-state true) expanded? (deref expanded*) @@ -51,11 +51,11 @@ (if (contains? cf/flags :nitrate) (let [sub (->> (rp/cmd! :get-owned-organizations-summary {}) (rx/subs! - (fn [result] (reset! orgs* (or result []))) - (fn [_] (reset! orgs* []))))] + (fn [result] (reset! organizations* (or result []))) + (fn [_] (reset! organizations* []))))] (fn [] (rx/dispose! sub))) - (reset! orgs* []))) + (reset! organizations* []))) [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-container)} @@ -69,33 +69,33 @@ [:div {:class (stl/css :warning-notice)} [:& context-notification {:level :warning - :content (tr (if has-orgs? - "modals.delete-account.info.with-orgs" + :content (tr (if has-organizations? + "modals.delete-account.info.with-organizations" "modals.delete-account.info"))}]] - (when has-orgs? - [:div {:class (stl/css :orgs-section)} - [:button {:class (stl/css :orgs-section-toggle) + (when has-organizations? + [:div {:class (stl/css :organizations-section)} + [:button {:class (stl/css :organizations-section-toggle) :type "button" :aria-expanded expanded? :on-click on-toggle} - [:span {:class (stl/css :orgs-section-title)} - (tr "modals.delete-account.owned-orgs.list-title")] + [:span {:class (stl/css :organizations-section-title)} + (tr "modals.delete-account.owned-organizations.list-title")] [:> icon* {:icon-id i/arrow :size "s" - :class (stl/css-case :orgs-section-arrow true + :class (stl/css-case :organizations-section-arrow true :expanded expanded?)}]] (when expanded? - [:ul {:class (stl/css :org-list)} - (for [{:keys [id name team-count member-count] :as org} orgs] - [:li {:class (stl/css :org-item) :key id} - [:> org-avatar* {:org org :size "xxl"}] - [:div {:class (stl/css :org-info)} - [:span {:class (stl/css :org-name)} name] - [:div {:class (stl/css :org-counts)} - [:span (tr "modals.delete-account.owned-orgs.teams-count" + [:ul {:class (stl/css :organization-list)} + (for [{:keys [id name team-count member-count] :as organization} organizations] + [:li {:class (stl/css :organization-item) :key id} + [:> organization-avatar* {:organization organization :size "xxl"}] + [:div {:class (stl/css :organization-info)} + [:span {:class (stl/css :organization-name)} name] + [:div {:class (stl/css :organization-counts)} + [:span (tr "modals.delete-account.owned-organizations.teams-count" (i18n/c (or team-count 0)))] - [:span (tr "modals.delete-account.owned-orgs.members-count" + [:span (tr "modals.delete-account.owned-organizations.members-count" (i18n/c (or member-count 0)))]]]])])])] [:div {:class (stl/css :modal-footer)} diff --git a/frontend/src/app/main/ui/settings/delete_account.scss b/frontend/src/app/main/ui/settings/delete_account.scss index cabfea544e..ff4a0849e9 100644 --- a/frontend/src/app/main/ui/settings/delete_account.scss +++ b/frontend/src/app/main/ui/settings/delete_account.scss @@ -60,7 +60,7 @@ color: var(--modal-title-foreground-color); } -.orgs-section { +.organizations-section { @include t.use-typography("body-medium"); display: flex; @@ -70,7 +70,7 @@ gap: var(--sp-l); } -.orgs-section-toggle { +.organizations-section-toggle { display: flex; flex-direction: row; align-items: center; @@ -81,20 +81,20 @@ cursor: pointer; } -.orgs-section-title { +.organizations-section-title { color: var(--color-accent-tertiary); } -.orgs-section-arrow { +.organizations-section-arrow { color: var(--color-accent-tertiary); transition: transform 0.15s ease; } -.orgs-section-arrow.expanded { +.organizations-section-arrow.expanded { transform: rotate(90deg); } -.org-list { +.organization-list { display: flex; flex-direction: column; flex: 1 1 auto; @@ -106,23 +106,23 @@ overflow-y: auto; } -.org-item { +.organization-item { display: flex; flex-direction: row; align-items: center; gap: var(--sp-s); } -.org-info { +.organization-info { display: flex; flex-direction: column; } -.org-name { +.organization-name { color: var(--modal-title-foreground-color); } -.org-counts { +.organization-counts { display: flex; flex-direction: row; gap: var(--sp-s); diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index e309bba797..a972fd0f93 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -612,7 +612,7 @@ :cancel-at (when (:cancel-at nitrate-license) (tr "nitrate.subscription.active-until" (ct/format-inst (:cancel-at nitrate-license) "d MMMM, yyyy"))) :benefits-title (tr "subscription.settings.benefits.nitrate-unlimited-benefits") - :benefits [{:label (tr "subscription.settings.enterprise.nitrate.multi-org-management") + :benefits [{:label (tr "subscription.settings.enterprise.nitrate.multi-organization-management") :description (tr "subscription.settings.enterprise.nitrate.support-team")} {:label (tr "subscription.settings.enterprise.nitrate.enterprise-security") :description (tr "subscription.settings.enterprise.nitrate.native-sso")} @@ -770,7 +770,7 @@ :price-value "$25" :price-period (tr "subscription.settings.organization-member-month") :benefits-title (tr "subscription.settings.benefits.nitrate-unlimited-benefits") - :benefits [{:label (tr "subscription.settings.enterprise.nitrate.multi-org-management") + :benefits [{:label (tr "subscription.settings.enterprise.nitrate.multi-organization-management") :description (tr "subscription.settings.enterprise.nitrate.support-team")} {:label (tr "subscription.settings.enterprise.nitrate.enterprise-security") :description (tr "subscription.settings.enterprise.nitrate.native-sso")} @@ -877,7 +877,7 @@ [:div {:class (stl/css :modal-text-medium)} (tr "nitrate.contact-sales.downgrade-title")] [:ul {:class (stl/css :downgrade-list)} - [:li {:class (stl/css :downgrade-item)} (tr "nitrate.contact-sales.downgrade-org-deleted")] + [:li {:class (stl/css :downgrade-item)} (tr "nitrate.contact-sales.downgrade-organization-deleted")] [:li {:class (stl/css :downgrade-item)} (tr "nitrate.contact-sales.downgrade-teams-available")] [:li {:class (stl/css :downgrade-item)} (tr "nitrate.contact-sales.downgrade-storage-limited")]] diff --git a/frontend/test/frontend_tests/data/dashboard_test.cljs b/frontend/test/frontend_tests/data/dashboard_test.cljs index d56b034a6d..bfc218bce8 100644 --- a/frontend/test/frontend_tests/data/dashboard_test.cljs +++ b/frontend/test/frontend_tests/data/dashboard_test.cljs @@ -23,7 +23,7 @@ current-url (str "https://penpot.example.com/#/dashboard/recent?team-id=" team-id) redirect-url "https://idp.example.com/authorize" state {:current-team-id team-id} - event (dcm/handle-change-team-org + event (dcm/handle-change-team-organization {:team {:id team-id :organization organization} :notification nil})] (mock/with-mocks @@ -52,14 +52,14 @@ (t/deftest organization-sso-activation-redirects-current-team (t/async done (let [team-id (uuid/next) - org-id (uuid/next) + organization-id (uuid/next) current-url (str "https://penpot.example.com/#/workspace?team-id=" team-id) redirect-url "https://idp.example.com/authorize" state {:current-team-id team-id :teams {team-id {:id team-id - :organization {:id org-id}}}} + :organization {:id organization-id}}}} event (dcm/handle-organization-change-sso - {:organization-id org-id})] + {:organization-id organization-id})] (mock/with-mocks {cf/flags (conj cf/flags :nitrate) rp/cmd! (mock/stub diff --git a/frontend/test/frontend_tests/data/nitrate_test.cljs b/frontend/test/frontend_tests/data/nitrate_test.cljs index f42f95bd26..2c59912c70 100644 --- a/frontend/test/frontend_tests/data/nitrate_test.cljs +++ b/frontend/test/frontend_tests/data/nitrate_test.cljs @@ -147,10 +147,10 @@ (let [public-uri (u/uri "https://example.com/penpot/")] (t/is (= "https://example.com/penpot/admin-console/" (dnt/build-admin-console-url public-uri "" nil))) - (t/is (= "https://example.com/penpot/admin-console/organization/my-org/organization-id/people/" + (t/is (= "https://example.com/penpot/admin-console/organization/my-organization/organization-id/people/" (dnt/build-admin-console-url public-uri - "organization/my-org/organization-id/people/" + "organization/my-organization/organization-id/people/" nil))) (t/is (= {:action "create-organization" :origin "dashboard:organization-switcher"} diff --git a/frontend/translations/de.po b/frontend/translations/de.po index 18149745bd..5b2646e412 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -370,7 +370,7 @@ msgid "dashboard.copy-suffix" msgstr "(Kopie)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Neue Organisation erstellen" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 70b168bb93..d5ea096fb8 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -98,7 +98,7 @@ msgid "auth.new-password" msgstr "Type a new password" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "You're now part of %s" #: src/app/main/ui/auth/recovery.cljs:36 @@ -297,7 +297,7 @@ msgid "common.unpublish" msgstr "Unpublish" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "You're no longer part of the %s organization." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -367,25 +367,25 @@ msgid "dashboard.change-email" msgstr "Change email" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "MOVE TEAM" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Move to:" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Select an organization" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "Projects and files will remain available to team members. The team will get " "the configuration from the new organization." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Change team's organization" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -397,7 +397,7 @@ msgid "dashboard.copy-suffix" msgstr "(copy)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Create org" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -816,7 +816,7 @@ msgid "dashboard.invite-profile" msgstr "Invite people" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Leave org" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -892,7 +892,7 @@ msgid "dashboard.no-matches-for" msgstr "No matches found for “%s“" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "You don't have permission to add teams to any of your organizations." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -962,7 +962,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Order by status" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "The %s organization has been deleted." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -1002,7 +1002,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Try plugin: " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Create org" #: src/app/main/data/dashboard.cljs:805 @@ -1090,35 +1090,35 @@ msgid "dashboard.section.teams" msgstr "Teams" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "ADD TO ORGANIZATION" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Choose an organization:" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" only allows organization members to join its teams. Any pending " "invitations to people outside the organization will be revoked." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Pending invitations to external users will be canceled." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Here you find all your organizations where you are allowed to create or add " "teams." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Select an organization" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Add team to an organization" #: src/app/main/ui/settings/options.cljs:66 @@ -1242,7 +1242,7 @@ msgid "dashboard.success-move-project" msgstr "Your project has been moved successfully" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "This team now belongs to %s" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1254,7 +1254,7 @@ msgid "dashboard.team-members" msgstr "Team members" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "This team no longer belongs to the organization %s" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1266,7 +1266,7 @@ msgid "dashboard.team-organization.add" msgstr "Add to an organization" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "You don't have permission to add this team to any of your organizations." #: src/app/main/ui/dashboard/team.cljs:1632 @@ -1274,7 +1274,7 @@ msgid "dashboard.team-organization.change" msgstr "Change team organization" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Only people within your organization can be invited." #: src/app/main/ui/dashboard/team.cljs:1639 @@ -1340,7 +1340,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Penpot Free" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "You are no longer a member of the organization %s" #: src/app/main/ui/settings/options.cljs:109 @@ -1727,15 +1727,15 @@ msgid "errors.only-creator-can-unlock" msgstr "Only the version creator can unlock it" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "There was a problem leaving the organization. Please try again." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "The organization owner can't leave the organization." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "That organization doesn't exists" #: src/app/main/ui/settings/password.cljs @@ -3763,17 +3763,17 @@ msgid "modals.add-shared-confirm.message" msgstr "Add “%s” as Shared Library" #: src/app/main/data/nitrate.cljs:199 -msgid "modals.before-leave-org.message" +msgid "modals.before-leave-organization.message" msgstr "" "You are the only member of some of the teams. Those teams will be deleted " "along with its projects and files." #: src/app/main/data/nitrate.cljs:198, src/app/main/ui/dashboard/change_owner.cljs:151 -msgid "modals.before-leave-org.title" +msgid "modals.before-leave-organization.title" msgstr "BEFORE YOU LEAVE THE ORGANIZATION" #: src/app/main/data/nitrate.cljs:202 -msgid "modals.before-leave-org.warning" +msgid "modals.before-leave-organization.warning" msgstr "Any team where you’re the only member will be deleted." #: src/app/main/ui/workspace/nudge.cljs:59 @@ -3831,22 +3831,22 @@ msgstr "By removing your account you’ll lose all your current projects and arc #: src/app/main/ui/settings/delete_account.cljs #, unused -msgid "modals.delete-account.info.with-orgs" +msgid "modals.delete-account.info.with-organizations" msgstr "" "This action is irreversible. Your account, subscription, and organizations " "will be deleted immediately. You will also lose access to your current " "projects and files." #: src/app/main/ui/settings/delete_account.cljs:83 -msgid "modals.delete-account.owned-orgs.list-title" +msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organizations that will be deleted" #: src/app/main/ui/settings/delete_account.cljs:98 -msgid "modals.delete-account.owned-orgs.members-count" +msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s members" #: src/app/main/ui/settings/delete_account.cljs:96 -msgid "modals.delete-account.owned-orgs.teams-count" +msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s teams" #: src/app/main/ui/settings/delete_account.cljs:64 @@ -3930,7 +3930,7 @@ msgid "modals.delete-font.title" msgstr "Deleting font" #: src/app/main/data/team.cljs:129 -msgid "modals.delete-org-team-confirm.message" +msgid "modals.delete-organization-team-confirm.message" msgstr "Are you sure you want to delete this team that is part of %s org?" #: src/app/main/ui/workspace/context_menu.cljs:707, src/app/main/ui/workspace/sidebar/sitemap.cljs:109 @@ -4082,7 +4082,7 @@ msgid "modals.invite-restricted-members.all-blocked-title" msgstr "No invitations were sent" #: src/app/main/data/team.cljs:162 -msgid "modals.invite-restricted-members.all-org-members-in-team" +msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Only \"%s\" organization's members can be invited to this team, and they " "are all already team members. If you need more information, contact the " @@ -4169,35 +4169,35 @@ msgid "modals.leave-confirm.title" msgstr "Leaving team" #: src/app/main/ui/dashboard/change_owner.cljs:158 -msgid "modals.leave-org-and-reassign.hint" +msgid "modals.leave-organization-and-reassign.hint" msgstr "" "You are the owner of some organization's teams. Please promote another " "member to become an owner." #: src/app/main/ui/dashboard/change_owner.cljs:161 -msgid "modals.leave-org-and-reassign.hint-delete" +msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "You are the only member of some of the teams. Those teams will be deleted " "along with its projects and files." #: src/app/main/ui/dashboard/change_owner.cljs:163 -msgid "modals.leave-org-and-reassign.hint-promote" +msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Also, you are the owner of some organization's teams. Please promote " "another member to become an owner." #: src/app/main/data/nitrate.cljs:200, src/app/main/data/nitrate.cljs:209 -msgid "modals.leave-org-confirm.accept" +msgid "modals.leave-organization-confirm.accept" msgstr "Leave organization" #: src/app/main/data/nitrate.cljs:208 -msgid "modals.leave-org-confirm.message" +msgid "modals.leave-organization-confirm.message" msgstr "" "You will permanently lose access to all teams, projects, and files within " "it." #: src/app/main/data/nitrate.cljs:207 -msgid "modals.leave-org-confirm.title" +msgid "modals.leave-organization-confirm.title" msgstr "Leaving %s organization?" #: src/app/main/ui/delete_shared.cljs:54 @@ -4272,21 +4272,21 @@ msgid "modals.remove-shared-confirm.message" msgstr "Remove “%s” as Shared Library" #: src/app/main/data/nitrate.cljs:256 -msgid "modals.remove-team-org.accept" +msgid "modals.remove-team-organization.accept" msgstr "Remove from organization" #: src/app/main/data/nitrate.cljs:254 -msgid "modals.remove-team-org.info" +msgid "modals.remove-team-organization.info" msgstr "" "Projects and files will remain available to team members, but the " "organization's settings will no longer apply." #: src/app/main/data/nitrate.cljs:253 -msgid "modals.remove-team-org.text" +msgid "modals.remove-team-organization.text" msgstr "Are you sure you want to remove the '%s' team from the '%s' organization?" #: src/app/main/data/nitrate.cljs:252 -msgid "modals.remove-team-org.title" +msgid "modals.remove-team-organization.title" msgstr "REMOVE TEAM FROM THE ORGANIZATION" #: src/app/main/ui/workspace/nudge.cljs:52 @@ -4354,7 +4354,7 @@ msgid "nitrate.activation-success.active-until" msgstr "Your plan is active until %s." #: src/app/main/ui/dashboard/subscription.cljs:195, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:68 -msgid "nitrate.activation-success.create-org" +msgid "nitrate.activation-success.create-organization" msgstr "Create organization" #: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:63 @@ -4409,7 +4409,7 @@ msgstr "" "To switch to this plan, please contact our sales team. We'll help you " "update your subscription and ensure everything is set up correctly." -msgid "nitrate.contact-sales.downgrade-org-deleted" +msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Your organization will be deleted." msgid "nitrate.contact-sales.downgrade-storage-limited" @@ -5863,7 +5863,7 @@ msgid "import-shortcuts.apply" msgstr "Apply" #: src/app/main/ui/dashboard/subscription.cljs:191 -msgid "subscription.banner.create-org-info" +msgid "subscription.banner.create-organization-info" msgstr "" "Create an organization and see exactly how Enterprise works. Set rules, " "manage your teams and discover a new level of control over your design " @@ -6098,7 +6098,7 @@ msgstr "Flat monthly bill" msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Unlimited storage" -msgid "subscription.settings.enterprise.nitrate.multi-org-management" +msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Multi-Org Management:" msgid "subscription.settings.enterprise.nitrate.support-team" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 28b9d2335d..caf042a506 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -102,7 +102,7 @@ msgid "auth.new-password" msgstr "Introduce la nueva contraseña" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "Te uniste a la organización %s" #: src/app/main/ui/auth/recovery.cljs:36 @@ -304,7 +304,7 @@ msgid "common.unpublish" msgstr "Despublicar" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "Ya no eres miembro de la organización %s." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -374,25 +374,25 @@ msgid "dashboard.change-email" msgstr "Cambiar correo" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "MOVER EL EQUIPO" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Mover a:" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Elige una organización" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "Los miembros del equipo seguirán teniendo acceso a los proyectos y " "ficheros. El equipo tendrá la configuración de la nueva organización." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Cambiar el equipo de organización" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -404,7 +404,7 @@ msgid "dashboard.copy-suffix" msgstr "(copia)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Crear org" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -824,7 +824,7 @@ msgid "dashboard.invite-profile" msgstr "Invitar a la gente" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Abandonar organización" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -900,7 +900,7 @@ msgid "dashboard.no-matches-for" msgstr "No se encuentra “%s“" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "No tienes permiso para añadir equipos a ninguna de tus organizaciones." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -972,7 +972,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Ordenar por estado" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "La organización %s se ha borrado." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -1012,7 +1012,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Prueba la extensión: " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Crear org" #: src/app/main/data/dashboard.cljs:805 @@ -1100,36 +1100,36 @@ msgid "dashboard.section.teams" msgstr "Equipos" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "AÑADIR A UNA ORGANIZACIÓN" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Elige una organización:" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" solo permite que los miembros de la organización se unan a sus " "equipos. Cualquier invitación pendiente a personas fuera de la organización " "será revocada." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Las invitaciones pendientes a usuarios externos serán canceladas." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Aquí encontrarás todas las organizaciones en las que tienes permiso para " "crear o añadir equipos." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Elige una organización" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Añadir el equipo a una organización" #: src/app/main/ui/settings/options.cljs:66 @@ -1253,7 +1253,7 @@ msgid "dashboard.success-move-project" msgstr "Tu proyecto ha sido movido con éxito" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "Este equipo ahora pertenece a la organización %s" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1265,7 +1265,7 @@ msgid "dashboard.team-members" msgstr "Integrantes del equipo" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "Este equipo ya no pertenece a la organización %s" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1277,7 +1277,7 @@ msgid "dashboard.team-organization.add" msgstr "Añadir a una organización" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "No tienes permiso para añadir este equipo a ninguna de tus organizaciones." #: src/app/main/ui/dashboard/team.cljs:1632 @@ -1285,7 +1285,7 @@ msgid "dashboard.team-organization.change" msgstr "Cambiar el equipo de organización" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Solo se puede invitar a personas que estén dentro de tu organización." #: src/app/main/ui/dashboard/team.cljs:1639 @@ -1351,7 +1351,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Penpot Gratis" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "Ya no perteneces a la organización %s" #: src/app/main/ui/settings/options.cljs:109 @@ -1693,17 +1693,17 @@ msgid "errors.migration-in-progress" msgstr "Migración en proceso" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "" "Ha habido un problema abandonando la organización. Intentalo de nuevo, por " "favor." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "El dueño de la organización no puede abandonarla." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "Esa organización no existe" #: src/app/main/ui/settings/password.cljs @@ -3655,17 +3655,17 @@ msgid "modals.add-shared-confirm.message" msgstr "Añadir “%s” como Biblioteca Compartida" #: src/app/main/data/nitrate.cljs:199 -msgid "modals.before-leave-org.message" +msgid "modals.before-leave-organization.message" msgstr "" "Eres el único miembro de algunos equipos. Esos equipos se van a borrar, " "junto con sus proyectos y ficheros." #: src/app/main/data/nitrate.cljs:198, src/app/main/ui/dashboard/change_owner.cljs:151 -msgid "modals.before-leave-org.title" +msgid "modals.before-leave-organization.title" msgstr "ANTES DE ABANDONAR LA ORGANIZACIÓN" #: src/app/main/data/nitrate.cljs:202 -msgid "modals.before-leave-org.warning" +msgid "modals.before-leave-organization.warning" msgstr "Se van a borrar todos los equipos en los que eres el único miembro." #: src/app/main/ui/workspace/nudge.cljs:59 @@ -3723,22 +3723,22 @@ msgstr "Si borras tu cuenta perderás todos tus proyectos y archivos." #: src/app/main/ui/settings/delete_account.cljs #, unused -msgid "modals.delete-account.info.with-orgs" +msgid "modals.delete-account.info.with-organizations" msgstr "" "Esta acción es irreversible. Tu cuenta, suscripción y organizaciones se " "eliminarán inmediatamente. También perderás el acceso a tus proyectos y " "archivos actuales." #: src/app/main/ui/settings/delete_account.cljs:83 -msgid "modals.delete-account.owned-orgs.list-title" +msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organizaciones que se eliminarán" #: src/app/main/ui/settings/delete_account.cljs:98 -msgid "modals.delete-account.owned-orgs.members-count" +msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s miembros" #: src/app/main/ui/settings/delete_account.cljs:96 -msgid "modals.delete-account.owned-orgs.teams-count" +msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s equipos" #: src/app/main/ui/settings/delete_account.cljs:64 @@ -3812,7 +3812,7 @@ msgid "modals.delete-font.title" msgstr "Eliminando fuente" #: src/app/main/data/team.cljs:129 -msgid "modals.delete-org-team-confirm.message" +msgid "modals.delete-organization-team-confirm.message" msgstr "" "¿Estás seguro de que deseas eliminar este equipo que forma parte de la " "organización %s?" @@ -3957,7 +3957,7 @@ msgid "modals.invite-restricted-members.all-blocked-title" msgstr "No se envió ninguna invitación" #: src/app/main/data/team.cljs:162 -msgid "modals.invite-restricted-members.all-org-members-in-team" +msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Solo se puede invitar a este equipo a miembros de la organización \"%s\", y " "todas esas personas ya son miembros del equipo. Si necesitas más " @@ -4044,35 +4044,35 @@ msgid "modals.leave-confirm.title" msgstr "Abandonando el equipo" #: src/app/main/ui/dashboard/change_owner.cljs:158 -msgid "modals.leave-org-and-reassign.hint" +msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Tienes la propiedad de algunos equipos de esta organización. Por favor " "selecciona otra persona integrante para promover al rol Propiedad." #: src/app/main/ui/dashboard/change_owner.cljs:161 -msgid "modals.leave-org-and-reassign.hint-delete" +msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Eres el único miembro de algunos equipos. Esos equipos se van a borrar, " "junto con sus proyectos y ficheros." #: src/app/main/ui/dashboard/change_owner.cljs:163 -msgid "modals.leave-org-and-reassign.hint-promote" +msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Además, tienes la propiedad de algunos equipos de esta organización. Por " "favor selecciona otra persona integrante para promover al rol Propiedad." #: src/app/main/data/nitrate.cljs:200, src/app/main/data/nitrate.cljs:209 -msgid "modals.leave-org-confirm.accept" +msgid "modals.leave-organization-confirm.accept" msgstr "Abandonar organización" #: src/app/main/data/nitrate.cljs:208 -msgid "modals.leave-org-confirm.message" +msgid "modals.leave-organization-confirm.message" msgstr "" "Perderás permanentemente el acceso a todos los equipos, proyectos y " "archivos en ella." #: src/app/main/data/nitrate.cljs:207 -msgid "modals.leave-org-confirm.title" +msgid "modals.leave-organization-confirm.title" msgstr "¿Abandonando la organización %s?" #: src/app/main/ui/delete_shared.cljs:54 @@ -4147,21 +4147,21 @@ msgid "modals.remove-shared-confirm.message" msgstr "Añadir “%s” como Biblioteca Compartida" #: src/app/main/data/nitrate.cljs:256 -msgid "modals.remove-team-org.accept" +msgid "modals.remove-team-organization.accept" msgstr "Eliminar de la organización" #: src/app/main/data/nitrate.cljs:254 -msgid "modals.remove-team-org.info" +msgid "modals.remove-team-organization.info" msgstr "" "Los proyectos y archivos seguirán estando disponibles para los miembros del " "equipo, pero la configuración de la organización dejará de aplicarse." #: src/app/main/data/nitrate.cljs:253 -msgid "modals.remove-team-org.text" +msgid "modals.remove-team-organization.text" msgstr "¿Estás seguro de que quieres eliminar el equipo %s de la organización %s?" #: src/app/main/data/nitrate.cljs:252 -msgid "modals.remove-team-org.title" +msgid "modals.remove-team-organization.title" msgstr "ELIMINAR EQUIPO DE LA ORGANIZACIÓN" #: src/app/main/ui/workspace/nudge.cljs:52 @@ -4229,7 +4229,7 @@ msgid "nitrate.activation-success.active-until" msgstr "Tu plan está activo hasta el %s." #: src/app/main/ui/dashboard/subscription.cljs:195, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:68 -msgid "nitrate.activation-success.create-org" +msgid "nitrate.activation-success.create-organization" msgstr "Crear organización" #: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:63 @@ -4285,7 +4285,7 @@ msgstr "" "ayudaremos a actualizar tu suscripción y a asegurarnos de que todo esté " "configurado correctamente." -msgid "nitrate.contact-sales.downgrade-org-deleted" +msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Tu organización será eliminada." msgid "nitrate.contact-sales.downgrade-storage-limited" @@ -5718,7 +5718,7 @@ msgid "import-shortcuts.apply" msgstr "Aplicar" #: src/app/main/ui/dashboard/subscription.cljs:191 -msgid "subscription.banner.create-org-info" +msgid "subscription.banner.create-organization-info" msgstr "" "Crea una organización y descubre exactamente cómo funciona Enterprise. " "Establece normas, gestiona tus equipos y disfruta de un nuevo nivel de " @@ -5962,7 +5962,7 @@ msgstr "Factura mensual fija" msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Almacenamiento ilimitado" -msgid "subscription.settings.enterprise.nitrate.multi-org-management" +msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Gestión multiorganización:" msgid "subscription.settings.enterprise.nitrate.support-team" diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index 98044073d1..1e5e36d872 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -366,7 +366,7 @@ msgid "dashboard.copy-suffix" msgstr "(copie)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Créer une organisation" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/fr_CA.po b/frontend/translations/fr_CA.po index 5e32136781..64319e1960 100644 --- a/frontend/translations/fr_CA.po +++ b/frontend/translations/fr_CA.po @@ -98,7 +98,7 @@ msgid "auth.new-password" msgstr "Nouveau mot de passe" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "Tu fais maintenant partie de %s" #: src/app/main/ui/auth/recovery.cljs:36 @@ -300,7 +300,7 @@ msgid "common.unpublish" msgstr "Dépublier" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "Tu ne fais plus partie de l'organisation %s." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -368,25 +368,25 @@ msgid "dashboard.change-email" msgstr "Changer l'adresse courriel" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "DÉPLACER L'ÉQUIPE" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Déplacer vers :" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Sélectionner une organisation" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "Les membres de l'équipe maintiendront l'accès aux projets et fichiers. " "L'équipe recevra les configurations de la nouvelle organisation." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Changer l'organisation de l'équipe" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -398,7 +398,7 @@ msgid "dashboard.copy-suffix" msgstr "(copier)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Crée une nouvelle organisation" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -822,7 +822,7 @@ msgid "dashboard.invite-profile" msgstr "Inviter des gens" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Quitter l'organisation" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -900,7 +900,7 @@ msgid "dashboard.no-matches-for" msgstr "Aucun résultat de recherche pour « %s »" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "Tu n'as pas la permission d'ajouter des équipes à tes organisations." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -970,7 +970,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Ordre par statut" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "L'organisation %s a été supprimée." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -1010,7 +1010,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Essayer le plugiciel : " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Créer une org" #: src/app/main/data/dashboard.cljs:805 @@ -1095,36 +1095,36 @@ msgid "dashboard.section.organizations" msgstr "Organisations" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "AJOUTER À L'ORGANISATION" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Choisir une organisation :" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "« %s » ne permet qu'aux membres d'organisations de joindre ses équipes. Les " "invitations en attente à des gens externes à l'organisation seront " "révoquées." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Les invitations en attente à des usagers externes seront annulées." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Ici tu trouveras toutes les organisations auxquelles tu as le droit de " "créer ou ajouter des équipes." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Choisir une organisation :" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Ajouter une équipe à l'organisation" #: src/app/main/ui/settings/options.cljs:66 @@ -1248,7 +1248,7 @@ msgid "dashboard.success-move-project" msgstr "Le projet a été déplacé avec succès" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "Cette équipe appartient maintenant à l'organisation %s" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1260,7 +1260,7 @@ msgid "dashboard.team-members" msgstr "Membres de l'équipe" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "Cette équipe n'appartient plus à l'organisation %s" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1272,7 +1272,7 @@ msgid "dashboard.team-organization.add" msgstr "Ajouter à l'organisation" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "Tu n'as pas le droit d'ajouter cette équipe à tes organisations." #: src/app/main/ui/dashboard/team.cljs:1632 @@ -1280,7 +1280,7 @@ msgid "dashboard.team-organization.change" msgstr "Changer l'organisation de l'équipe" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Seules les personnes de ton organisation peuvent être invitées." #: src/app/main/ui/dashboard/team.cljs:1639 @@ -1346,7 +1346,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Penpot gratuit" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "Tu n'es plus membre de l'organisation %s" #: src/app/main/ui/settings/options.cljs:109 @@ -1726,15 +1726,15 @@ msgid "errors.only-creator-can-unlock" msgstr "Seul le créateur de la version peut la déverrouiller" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "Un problème est survenu en quittant l'organisation. Merci de réessayer." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Le propriétaire de l'organisation ne peut pas la quitter." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "Cette organisation n'existe pas" #: src/app/main/ui/settings/password.cljs @@ -3760,17 +3760,17 @@ msgid "modals.add-shared-confirm.message" msgstr "Ajouter « %s » comme bibliothèque partagée" #: src/app/main/data/nitrate.cljs:199 -msgid "modals.before-leave-org.message" +msgid "modals.before-leave-organization.message" msgstr "" "Tu es le seul membre de certaines équipes. Ces équipes, ainsi que leurs " "projets et fichiers, seront supprimées." #: src/app/main/data/nitrate.cljs:198, src/app/main/ui/dashboard/change_owner.cljs:151 -msgid "modals.before-leave-org.title" +msgid "modals.before-leave-organization.title" msgstr "AVANT DE QUITTER L'ORGANISATION" #: src/app/main/data/nitrate.cljs:202 -msgid "modals.before-leave-org.warning" +msgid "modals.before-leave-organization.warning" msgstr "Toute équipe dont tu es le seul membre sera supprimée." #: src/app/main/ui/workspace/nudge.cljs:59 @@ -3830,22 +3830,22 @@ msgstr "Si tu supprimes ton compte, tu vas perdre tous tes projets et fichiers." #: src/app/main/ui/settings/delete_account.cljs #, unused -msgid "modals.delete-account.info.with-orgs" +msgid "modals.delete-account.info.with-organizations" msgstr "" "Cette action est irréversible. Ton compte, abonnement et organisations " "seront supprimés immédiatement. L'accès aux projets et fichiers courants " "sera perdu." #: src/app/main/ui/settings/delete_account.cljs:83 -msgid "modals.delete-account.owned-orgs.list-title" +msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisations qui seront supprimées" #: src/app/main/ui/settings/delete_account.cljs:98 -msgid "modals.delete-account.owned-orgs.members-count" +msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s membres" #: src/app/main/ui/settings/delete_account.cljs:96 -msgid "modals.delete-account.owned-orgs.teams-count" +msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s équipes" #: src/app/main/ui/settings/delete_account.cljs:64 @@ -3929,7 +3929,7 @@ msgid "modals.delete-font.title" msgstr "Suppression de la police" #: src/app/main/data/team.cljs:129 -msgid "modals.delete-org-team-confirm.message" +msgid "modals.delete-organization-team-confirm.message" msgstr "Vraiment supprimer cette équipe qui fait partie de l'organisation %s?" #: src/app/main/ui/workspace/context_menu.cljs:707, src/app/main/ui/workspace/sidebar/sitemap.cljs:109 @@ -4077,7 +4077,7 @@ msgid "modals.invite-restricted-members.all-blocked-title" msgstr "Aucune invitation envoyée" #: src/app/main/data/team.cljs:162 -msgid "modals.invite-restricted-members.all-org-members-in-team" +msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Seuls les membres de l'organisation « %s » peuvent être invités à cette " "équipe et ils sont tous déjà membres. Pour plus d'informations, contacter " @@ -4164,35 +4164,35 @@ msgid "modals.leave-confirm.title" msgstr "Quitter l'équipe" #: src/app/main/ui/dashboard/change_owner.cljs:158 -msgid "modals.leave-org-and-reassign.hint" +msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Tu es propriétaire de certaines équipes de cette organisation. Sélectionner " "un autre membre afin de les promouvoir au rôle de propriétaire." #: src/app/main/ui/dashboard/change_owner.cljs:161 -msgid "modals.leave-org-and-reassign.hint-delete" +msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Tu es membre unique de certaines équipes. Ces équipes ainsi que leurs " "projets et fichiers seront supprimés." #: src/app/main/ui/dashboard/change_owner.cljs:163 -msgid "modals.leave-org-and-reassign.hint-promote" +msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "De plus, tu es propriétaire de certaines équipes de cette organisation. " "Sélectionner un autre membre afin de les promouvoir au rôle de propriétaire." #: src/app/main/data/nitrate.cljs:200, src/app/main/data/nitrate.cljs:209 -msgid "modals.leave-org-confirm.accept" +msgid "modals.leave-organization-confirm.accept" msgstr "Quitter l'organisation" #: src/app/main/data/nitrate.cljs:208 -msgid "modals.leave-org-confirm.message" +msgid "modals.leave-organization-confirm.message" msgstr "" "Tu perdras accès en permanence à toutes les équipes, leurs projets et " "fichiers." #: src/app/main/data/nitrate.cljs:207 -msgid "modals.leave-org-confirm.title" +msgid "modals.leave-organization-confirm.title" msgstr "Quitter l'organisation %s?" #: src/app/main/ui/delete_shared.cljs:54 @@ -4267,21 +4267,21 @@ msgid "modals.remove-shared-confirm.message" msgstr "Retirer « %s » comme bibliothèque partagée" #: src/app/main/data/nitrate.cljs:256 -msgid "modals.remove-team-org.accept" +msgid "modals.remove-team-organization.accept" msgstr "Retirer de l'organisation" #: src/app/main/data/nitrate.cljs:254 -msgid "modals.remove-team-org.info" +msgid "modals.remove-team-organization.info" msgstr "" "Les projets et fichiers demeureront disponibles aux membres de l'équipe, " "mais les paramètres de l'organisation ne s'appliqueront plus." #: src/app/main/data/nitrate.cljs:253 -msgid "modals.remove-team-org.text" +msgid "modals.remove-team-organization.text" msgstr "Vraiment retirer l'équipe « %s » de l'organisation « %s »?" #: src/app/main/data/nitrate.cljs:252 -msgid "modals.remove-team-org.title" +msgid "modals.remove-team-organization.title" msgstr "RETIRER L'ÉQUIPE DE L'ORGANISATION" #: src/app/main/ui/workspace/nudge.cljs:52 @@ -4349,7 +4349,7 @@ msgid "nitrate.activation-success.active-until" msgstr "L'abonnement est actif jusqu'au %s." #: src/app/main/ui/dashboard/subscription.cljs:195, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:68 -msgid "nitrate.activation-success.create-org" +msgid "nitrate.activation-success.create-organization" msgstr "Créer une organisation" #: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:63 @@ -4402,7 +4402,7 @@ msgstr "" "soit bien configuré." #, unused -msgid "nitrate.contact-sales.downgrade-org-deleted" +msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Ton organisation sera supprimée." #, unused @@ -5707,7 +5707,7 @@ msgid "shortcuts.zoom-selected" msgstr "Zoomer à la sélection" #: src/app/main/ui/dashboard/subscription.cljs:191 -msgid "subscription.banner.create-org-info" +msgid "subscription.banner.create-organization-info" msgstr "" "Crée une organisation et vois exactement comment fonctionne Enterprise. " "Établis les règles, gère les équipes et profite d'un nouveau niveau de " diff --git a/frontend/translations/he.po b/frontend/translations/he.po index b1240c366e..64ca502358 100644 --- a/frontend/translations/he.po +++ b/frontend/translations/he.po @@ -352,7 +352,7 @@ msgid "dashboard.copy-suffix" msgstr "(עותק)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "יצירת ארגון חדש" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/it.po b/frontend/translations/it.po index 78c40e6930..294c07c44b 100644 --- a/frontend/translations/it.po +++ b/frontend/translations/it.po @@ -98,7 +98,7 @@ msgid "auth.new-password" msgstr "Inserisci una nuova password" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "Ora fai parte di %s" #: src/app/main/ui/auth/recovery.cljs:36 @@ -297,7 +297,7 @@ msgid "common.unpublish" msgstr "Rimuovi pubblicazione" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "Non fai più parte dell'organizzazione %s." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -365,25 +365,25 @@ msgid "dashboard.change-email" msgstr "Cambia indirizzo e-mail" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "SPOSTA TEAM" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Sposta in:" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Seleziona un'organizzazione" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "I progetti e i file rimarranno disponibili per i membri del team. Il team " "riceverà la configurazione dalla nuova organizzazione." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Cambia organizzazione del team" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -395,7 +395,7 @@ msgid "dashboard.copy-suffix" msgstr "(copia)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Crea org" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -820,7 +820,7 @@ msgid "dashboard.invite-profile" msgstr "Invita nel team" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Lascia org" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -898,7 +898,7 @@ msgid "dashboard.no-matches-for" msgstr "Nessuna corrispondenza trovata per \"%s\"" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "Non hai i permessi per aggiungere team a nessuna delle tue organizzazioni." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -968,7 +968,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Ordina per stato" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "L'organizzazione %s è stata eliminata." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -1008,7 +1008,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Prova il plugin: " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Crea org" #: src/app/main/data/dashboard.cljs:805 @@ -1095,36 +1095,36 @@ msgid "dashboard.section.organizations" msgstr "Organizzazioni" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "AGGIUNGI ALL'ORGANIZZAZIONE" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Scegli un'organizzazione:" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" autorizza solo i membri dell'organizzazione ad unirsi al suo team. " "Qualsiasi invito in sospeso a persone al di fuori dell'organizzazione verrà " "revocato." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Gli inviti in sospeso ad utenti esterni verranno annullati." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Qui trovi tutte le organizzazioni dove sei autorizzato a creare o " "aggiungere team." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Seleziona un'organizzazione" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Aggiungi team ad una organizzazione" #: src/app/main/ui/settings/options.cljs:66 @@ -1248,7 +1248,7 @@ msgid "dashboard.success-move-project" msgstr "Il tuo progetto è stato spostato con successo" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "Questo team ora appartiene a %s" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1260,7 +1260,7 @@ msgid "dashboard.team-members" msgstr "Membri del team" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "Questo team non appartiene più all'organizzazione %s" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1272,7 +1272,7 @@ msgid "dashboard.team-organization.add" msgstr "Aggiungi ad una organizzazione" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Non hai i permessi per aggiungere questo team a nessuna delle tue " "organizzazioni." @@ -1282,7 +1282,7 @@ msgid "dashboard.team-organization.change" msgstr "Cambia organizzazione del team" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "" "Solo le persone all'interno della tua organizzazione possono essere " "invitate." @@ -1350,7 +1350,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Penpot Gratis" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "Non sei più un membro dell'organizzazione %s" #: src/app/main/ui/settings/options.cljs:109 @@ -1731,17 +1731,17 @@ msgid "errors.only-creator-can-unlock" msgstr "Solo il creatore della versione può sbloccarla" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "" "Si è verificato un problema durante l'abbandono dell'organizzazione. " "Riprova." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Il proprietario dell'organizzazione non può lasciare l'organizzazione." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "L'organizzazione non esiste" #: src/app/main/ui/settings/password.cljs diff --git a/frontend/translations/ko.po b/frontend/translations/ko.po index f7bb50c9e1..15d6303160 100644 --- a/frontend/translations/ko.po +++ b/frontend/translations/ko.po @@ -344,7 +344,7 @@ msgid "dashboard.copy-suffix" msgstr "(복사)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "새 조직 생성" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/ml.po b/frontend/translations/ml.po index be1766016e..34a6386c12 100644 --- a/frontend/translations/ml.po +++ b/frontend/translations/ml.po @@ -98,7 +98,7 @@ msgid "auth.new-password" msgstr "പുതിയൊരു പാസ്‌വേഡ് ചേർക്കുക" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "നിങ്ങളിപ്പോൾ %s-ന്റെ ഭാഗമാണ്" #: src/app/main/ui/auth/recovery.cljs:36 diff --git a/frontend/translations/nl.po b/frontend/translations/nl.po index 531c4e4a51..c04f12399f 100644 --- a/frontend/translations/nl.po +++ b/frontend/translations/nl.po @@ -363,7 +363,7 @@ msgid "dashboard.copy-suffix" msgstr "(kopie)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Nieuwe org aanmaken" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po index 828afc1571..d2ac3528e8 100644 --- a/frontend/translations/ru.po +++ b/frontend/translations/ru.po @@ -364,7 +364,7 @@ msgid "dashboard.copy-suffix" msgstr "(копия)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Создать новую организацию" #: src/app/main/ui/dashboard/sidebar.cljs:439 diff --git a/frontend/translations/sv.po b/frontend/translations/sv.po index 728ff5a39d..3295b3604d 100644 --- a/frontend/translations/sv.po +++ b/frontend/translations/sv.po @@ -96,7 +96,7 @@ msgid "auth.new-password" msgstr "Skriv ett nytt lösenord" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "Du är nu med i %s" #: src/app/main/ui/auth/recovery.cljs:36 @@ -293,7 +293,7 @@ msgid "common.unpublish" msgstr "Avpublicera" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "Du är inte längre med i organisationen %s." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -363,25 +363,25 @@ msgid "dashboard.change-email" msgstr "Ändra e-post" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "FLYTTA TEAM" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Flytta till:" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Välj en organisation" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "Projekt och filer kommer att förbli tillgängliga för teammedlemmar. Teamet " "kommer att hämta konfigurationen från den nya organisationen." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Ändra teamets organisation" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -393,7 +393,7 @@ msgid "dashboard.copy-suffix" msgstr "(kopia)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Skapa org" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -812,7 +812,7 @@ msgid "dashboard.invite-profile" msgstr "Bjud in personer" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Lämna org" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -888,7 +888,7 @@ msgid "dashboard.no-matches-for" msgstr "Inga träffar hittades för ”%s”" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "Du har inte behörighet att lägga till team i någon av dina organisationer." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -958,7 +958,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Sortera efter status" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "Organisationen %s har tagits bort." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -998,7 +998,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Testa plugin: " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Skapa org" #: src/app/main/data/dashboard.cljs:805 @@ -1083,36 +1083,36 @@ msgid "dashboard.section.organizations" msgstr "Organisationer" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "LÄGG TILL I ORGANISATION" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Välj en organisation:" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" tillåter endast organisationsmedlemmar att gå med i dess team. Alla " "väntande inbjudningar till personer utanför organisationen kommer att " "återkallas." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Väntande inbjudningar till externa användare kommer att avbrytas." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Här hittar du alla dina organisationer där du har rätt att skapa eller " "lägga till team." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Välj en organisation" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Lägg till team i en organisation" #: src/app/main/ui/settings/options.cljs:66 @@ -1236,7 +1236,7 @@ msgid "dashboard.success-move-project" msgstr "Ditt projekt har flyttats" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "Detta team tillhör nu %s" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1248,7 +1248,7 @@ msgid "dashboard.team-members" msgstr "Teammedlemmar" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "Detta team tillhör inte längre organisation %s" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1260,7 +1260,7 @@ msgid "dashboard.team-organization.add" msgstr "Lägg till i en organisation" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Du har inte behörighet att lägga till det här teamet i någon av dina " "organisationer." @@ -1270,7 +1270,7 @@ msgid "dashboard.team-organization.change" msgstr "Ändra teamorganisation" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Endast personer inom din organisation kan bjudas in." #: src/app/main/ui/dashboard/team.cljs:1639 @@ -1336,7 +1336,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Gratis Penpot" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "Du är inte längre medlem i organisation %s" #: src/app/main/ui/settings/options.cljs:109 @@ -1712,15 +1712,15 @@ msgid "errors.only-creator-can-unlock" msgstr "Endast skaparen av versionen kan låsa upp den" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "Det uppstod ett fel när du skulle lämna organisationen. Försök igen." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Organisationsägaren kan inte lämna organisationen." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "Den organisationen finns inte" #: src/app/main/ui/settings/password.cljs @@ -3731,17 +3731,17 @@ msgid "modals.add-shared-confirm.message" msgstr "Lägg till \"%s\" som delat bibliotek" #: src/app/main/data/nitrate.cljs:199 -msgid "modals.before-leave-org.message" +msgid "modals.before-leave-organization.message" msgstr "" "Du är den enda medlemmen i vissa av teamen. Dessa team kommer att raderas " "tillsammans med deras projekt och filer." #: src/app/main/data/nitrate.cljs:198, src/app/main/ui/dashboard/change_owner.cljs:151 -msgid "modals.before-leave-org.title" +msgid "modals.before-leave-organization.title" msgstr "INNAN DU LÄMNAR ORGANISATIONEN" #: src/app/main/data/nitrate.cljs:202 -msgid "modals.before-leave-org.warning" +msgid "modals.before-leave-organization.warning" msgstr "Alla team där du är den enda medlemmen kommer att raderas." #: src/app/main/ui/workspace/nudge.cljs:59 @@ -3801,22 +3801,22 @@ msgstr "Genom att ta bort ditt konto förlorar du alla dina projekt och arkiv." #: src/app/main/ui/settings/delete_account.cljs #, unused -msgid "modals.delete-account.info.with-orgs" +msgid "modals.delete-account.info.with-organizations" msgstr "" "Denna åtgärd kan inte ångras. Ditt konto, din prenumeration och dina " "organisationer kommer att raderas omedelbart. Du kommer också att förlora " "åtkomsten till dina nuvarande projekt och filer." #: src/app/main/ui/settings/delete_account.cljs:83 -msgid "modals.delete-account.owned-orgs.list-title" +msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisationer som kommer att raderas" #: src/app/main/ui/settings/delete_account.cljs:98 -msgid "modals.delete-account.owned-orgs.members-count" +msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s medlemmar" #: src/app/main/ui/settings/delete_account.cljs:96 -msgid "modals.delete-account.owned-orgs.teams-count" +msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s team" #: src/app/main/ui/settings/delete_account.cljs:64 @@ -3900,7 +3900,7 @@ msgid "modals.delete-font.title" msgstr "Tar bort typsnitt" #: src/app/main/data/team.cljs:129 -msgid "modals.delete-org-team-confirm.message" +msgid "modals.delete-organization-team-confirm.message" msgstr "" "Är du säker på att du vill ta bort det här teamet som är en del av " "organisation %s?" @@ -4048,7 +4048,7 @@ msgid "modals.invite-restricted-members.all-blocked-title" msgstr "Inga inbjudningar skickades" #: src/app/main/data/team.cljs:162 -msgid "modals.invite-restricted-members.all-org-members-in-team" +msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Endast medlemmar i organisationen \"%s\" kan bjudas in till det här teamet, " "och de är alla redan teammedlemmar. Om du behöver mer information, kontakta " @@ -4135,35 +4135,35 @@ msgid "modals.leave-confirm.title" msgstr "Lämnar teamet" #: src/app/main/ui/dashboard/change_owner.cljs:158 -msgid "modals.leave-org-and-reassign.hint" +msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Du är ägare till teamen i vissa organisationer. Vänligen befordra en annan " "medlem till att bli ägare." #: src/app/main/ui/dashboard/change_owner.cljs:161 -msgid "modals.leave-org-and-reassign.hint-delete" +msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Du är den enda medlemmen i vissa av teamen. Dessa team kommer att raderas " "tillsammans med deras projekt och filer." #: src/app/main/ui/dashboard/change_owner.cljs:163 -msgid "modals.leave-org-and-reassign.hint-promote" +msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Du är också ägare till vissa organisationers team. Vänligen befordra en " "annan medlem till att bli ägare." #: src/app/main/data/nitrate.cljs:200, src/app/main/data/nitrate.cljs:209 -msgid "modals.leave-org-confirm.accept" +msgid "modals.leave-organization-confirm.accept" msgstr "Lämna organisation" #: src/app/main/data/nitrate.cljs:208 -msgid "modals.leave-org-confirm.message" +msgid "modals.leave-organization-confirm.message" msgstr "" "Du kommer permanent att förlora åtkomsten till alla team, projekt och filer " "i den." #: src/app/main/data/nitrate.cljs:207 -msgid "modals.leave-org-confirm.title" +msgid "modals.leave-organization-confirm.title" msgstr "Lämnar du organisation %s?" #: src/app/main/ui/delete_shared.cljs:54 @@ -4239,21 +4239,21 @@ msgid "modals.remove-shared-confirm.message" msgstr "Ta bort \"%s\" som delat bibliotek" #: src/app/main/data/nitrate.cljs:256 -msgid "modals.remove-team-org.accept" +msgid "modals.remove-team-organization.accept" msgstr "Ta bort från organisationen" #: src/app/main/data/nitrate.cljs:254 -msgid "modals.remove-team-org.info" +msgid "modals.remove-team-organization.info" msgstr "" "Projekt och filer kommer att förbli tillgängliga för teammedlemmar, men " "organisationens inställningar kommer inte längre att gälla." #: src/app/main/data/nitrate.cljs:253 -msgid "modals.remove-team-org.text" +msgid "modals.remove-team-organization.text" msgstr "Är du säker på att du vill ta bort teamet '%s' från organisationen '%s'?" #: src/app/main/data/nitrate.cljs:252 -msgid "modals.remove-team-org.title" +msgid "modals.remove-team-organization.title" msgstr "TA BORT TEAMET FRÅN ORGANISATIONEN" #: src/app/main/ui/workspace/nudge.cljs:52 @@ -4321,7 +4321,7 @@ msgid "nitrate.activation-success.active-until" msgstr "Ditt abonnemang är aktivt till %s." #: src/app/main/ui/dashboard/subscription.cljs:195, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:68 -msgid "nitrate.activation-success.create-org" +msgid "nitrate.activation-success.create-organization" msgstr "Skapa organisation" #: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:63 @@ -4374,7 +4374,7 @@ msgstr "" "korrekt konfigurerat." #, unused -msgid "nitrate.contact-sales.downgrade-org-deleted" +msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Din organisation kommer att raderas." #, unused @@ -5671,7 +5671,7 @@ msgid "shortcuts.zoom-selected" msgstr "Zooma in markerad" #: src/app/main/ui/dashboard/subscription.cljs:191 -msgid "subscription.banner.create-org-info" +msgid "subscription.banner.create-organization-info" msgstr "" "Skapa en organisation och se exakt hur Enterprise fungerar. Sätt regler, " "hantera dina team och upptäck en ny nivå av kontroll över ditt " diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index c61c0ecfa7..8d86f39c25 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -98,7 +98,7 @@ msgid "auth.new-password" msgstr "Yeni bir parola gir" #: src/app/main/ui/auth/verify_token.cljs:53 -msgid "auth.notifications.org-invitation-accepted" +msgid "auth.notifications.organization-invitation-accepted" msgstr "Artık %s organizasyonunun bir parçasısınız" #: src/app/main/ui/auth/recovery.cljs:36 @@ -294,7 +294,7 @@ msgid "common.unpublish" msgstr "Yayından kaldır" #: src/app/main/data/nitrate.cljs:165 -msgid "dasboard.leave-org.toast" +msgid "dashboard.leave-organization.toast" msgstr "Artık %s organizasyonunun bir parçası değilsiniz." #: src/app/main/ui/dashboard/projects.cljs:90 @@ -364,25 +364,25 @@ msgid "dashboard.change-email" msgstr "E-posta adresini değiştir" #, unused -msgid "dashboard.change-org-modal.accept" +msgid "dashboard.change-organization-modal.accept" msgstr "TAKIMI TAŞI" #, unused -msgid "dashboard.change-org-modal.choose" +msgid "dashboard.change-organization-modal.choose" msgstr "Şuraya taşı:" #, unused -msgid "dashboard.change-org-modal.select" +msgid "dashboard.change-organization-modal.select" msgstr "Organizasyon seç" #, unused -msgid "dashboard.change-org-modal.text" +msgid "dashboard.change-organization-modal.text" msgstr "" "Projeler ve dosyalar takım üyeleri tarafından erişilebilir olmaya devam " "edecek. Takım, yeni organizasyondan yapılandırmayı alacak." #: src/app/main/data/nitrate.cljs:399, src/app/main/ui/dashboard/team_form.cljs:157 -msgid "dashboard.change-org-modal.title" +msgid "dashboard.change-organization-modal.title" msgstr "Takımın organizasyonunu değiştir" #: src/app/main/ui/dashboard/deleted.cljs:316 @@ -394,7 +394,7 @@ msgid "dashboard.copy-suffix" msgstr "(kopya)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Yeni organizasyon oluştur" #: src/app/main/ui/dashboard/sidebar.cljs:439 @@ -816,7 +816,7 @@ msgid "dashboard.invite-profile" msgstr "İnsanları davet et" #: src/app/main/ui/dashboard/sidebar.cljs:671 -msgid "dashboard.leave-org" +msgid "dashboard.leave-organization" msgstr "Organizasyondan ayrıl" #: src/app/main/ui/dashboard/sidebar.cljs:570, src/app/main/ui/dashboard/sidebar.cljs:577, src/app/main/ui/dashboard/sidebar.cljs:582, src/app/main/ui/dashboard/team.cljs:433 @@ -894,7 +894,7 @@ msgid "dashboard.no-matches-for" msgstr "\"%s\" için sonuç bulunamadı" #: src/app/main/ui/dashboard/team_form.cljs:156 -msgid "dashboard.no-org-allows-create-team.message" +msgid "dashboard.no-organization-allows-create-team.message" msgstr "Hiçbir organizasyonunuza takım ekleme izniniz yok." #: src/app/main/ui/dashboard/team_form.cljs:152 @@ -964,7 +964,7 @@ msgid "dashboard.order-invitations-by-status" msgstr "Duruma göre sırala" #: src/app/main/data/dashboard.cljs:753 -msgid "dashboard.org-deleted" +msgid "dashboard.organization-deleted" msgstr "%s organizasyonu silindi." #: src/app/main/ui/settings/password.cljs:96, src/app/main/ui/settings/password.cljs:109 @@ -1004,7 +1004,7 @@ msgid "dashboard.plugins.try-plugin" msgstr "Eklentiyi deneyin: " #: src/app/main/ui/dashboard/sidebar.cljs:798 -msgid "dashboard.plus-create-new-org" +msgid "dashboard.plus-create-new-organization" msgstr "+ Organizasyon oluştur" #: src/app/main/data/dashboard.cljs:805 @@ -1091,36 +1091,36 @@ msgid "dashboard.section.organizations" msgstr "Organizasyonlar" #, unused -msgid "dashboard.select-org-modal.accept" +msgid "dashboard.select-organization-modal.accept" msgstr "ORGANİZASYONA EKLE" #, unused -msgid "dashboard.select-org-modal.choose" +msgid "dashboard.select-organization-modal.choose" msgstr "Organizasyon seç:" #: src/app/main/ui/dashboard/team.cljs:975 -msgid "dashboard.select-org-modal.external-invitations-warning" +msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\", yalnızca organizasyon üyelerinin takımlarına katılmasına izin " "veriyor. Organizasyon dışındaki kişilere gönderilmiş bekleyen davetler " "iptal edilecektir." #: src/app/main/ui/dashboard/team.cljs:971 -msgid "dashboard.select-org-modal.external-invitations-will-be-canceled" +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Dış kullanıcılara gönderilen bekleyen davetler iptal edilecektir." #, unused -msgid "dashboard.select-org-modal.permission-info" +msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Burada, takım oluşturmanıza veya eklemenize izin verilen tüm " "organizasyonlarınızı bulabilirsiniz." #, unused -msgid "dashboard.select-org-modal.select" +msgid "dashboard.select-organization-modal.select" msgstr "Organizasyon seç" #: src/app/main/data/nitrate.cljs:342, src/app/main/ui/dashboard/team_form.cljs:155 -msgid "dashboard.select-org-modal.title" +msgid "dashboard.select-organization-modal.title" msgstr "Takımı bir organizasyona ekle" #: src/app/main/ui/settings/options.cljs:66 @@ -1244,7 +1244,7 @@ msgid "dashboard.success-move-project" msgstr "Projen başarıyla taşındı" #, unused -msgid "dashboard.team-belong-org" +msgid "dashboard.team-belong-organization" msgstr "Bu takım artık %s organizasyonuna ait" #: src/app/main/ui/dashboard/team.cljs:1602 @@ -1256,7 +1256,7 @@ msgid "dashboard.team-members" msgstr "Takım üyeleri" #, unused -msgid "dashboard.team-no-longer-belong-org" +msgid "dashboard.team-no-longer-belong-organization" msgstr "Bu takım artık %s organizasyonuna ait değil" #: src/app/main/ui/dashboard/team.cljs:1609 @@ -1268,7 +1268,7 @@ msgid "dashboard.team-organization.add" msgstr "Organizasyona ekle" #: src/app/main/data/nitrate.cljs:341, src/app/main/data/nitrate.cljs:398 -msgid "dashboard.team-organization.add.no-valid-orgs" +msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "Bu takımı hiçbir organizasyonunuza ekleme izniniz yok." #: src/app/main/ui/dashboard/team.cljs:1632 @@ -1276,7 +1276,7 @@ msgid "dashboard.team-organization.change" msgstr "Takım organizasyonunu değiştir" #: src/app/main/ui/dashboard/team.cljs:905 -msgid "dashboard.team-organization.disabled-org-tooltip" +msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Yalnızca organizasyonunuzdaki kişiler davet edilebilir." #: src/app/main/ui/dashboard/team.cljs:1639 @@ -1342,7 +1342,7 @@ msgid "dashboard.upgrade-plan.penpot-free" msgstr "Penpot Ücretsiz" #, unused -msgid "dashboard.user-no-longer-belong-org" +msgid "dashboard.user-no-longer-belong-organization" msgstr "Artık %s organizasyonunun üyesi değilsiniz" #: src/app/main/ui/settings/options.cljs:109 @@ -1719,15 +1719,15 @@ msgid "errors.only-creator-can-unlock" msgstr "Yalnızca sürüm oluşturucu onun kilidini açabilir" #, unused -msgid "errors.org-leave.no-valid-teams" +msgid "errors.organization-leave.no-valid-teams" msgstr "Organizasyondan ayrılma sırasında bir hata oluştu. Lütfen tekrar deneyin." #, unused -msgid "errors.org-leave.org-owner-cannot-leave" +msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Organizasyon sahibi organizasyondan ayrılamaz." #: src/app/main/ui/auth/verify_token.cljs:97 -msgid "errors.org-not-found" +msgid "errors.organization-not-found" msgstr "Bu organizasyon yok" #: src/app/main/ui/settings/password.cljs @@ -3737,17 +3737,17 @@ msgid "modals.add-shared-confirm.message" msgstr "Paylaşılmış Kütüphane olarak “%s” Ekle" #: src/app/main/data/nitrate.cljs:199 -msgid "modals.before-leave-org.message" +msgid "modals.before-leave-organization.message" msgstr "" "Bazı takımların tek üyesi sizsiniz. Bu takımlar, projeleriyle ve " "dosyalarıyla birlikte silinecek." #: src/app/main/data/nitrate.cljs:198, src/app/main/ui/dashboard/change_owner.cljs:151 -msgid "modals.before-leave-org.title" +msgid "modals.before-leave-organization.title" msgstr "ORGANİZASYONDAN AYRILMADAN ÖNCE" #: src/app/main/data/nitrate.cljs:202 -msgid "modals.before-leave-org.warning" +msgid "modals.before-leave-organization.warning" msgstr "Tek üyesi olduğunuz tüm takımlar silinecektir." #: src/app/main/ui/workspace/nudge.cljs:59 @@ -3807,22 +3807,22 @@ msgstr "Hesabını silerek tüm projelerini ve arşivlerini kaybedeceksin." #: src/app/main/ui/settings/delete_account.cljs #, unused -msgid "modals.delete-account.info.with-orgs" +msgid "modals.delete-account.info.with-organizations" msgstr "" "Bu işlem geri alınamaz. Hesabınız, aboneliğiniz ve organizasyonlarınız " "hemen silinecektir. Ayrıca var olan projelerinize ve dosyalarınıza " "erişiminizi de kaybedeceksiniz." #: src/app/main/ui/settings/delete_account.cljs:83 -msgid "modals.delete-account.owned-orgs.list-title" +msgid "modals.delete-account.owned-organizations.list-title" msgstr "Silinecek organizasyonlar" #: src/app/main/ui/settings/delete_account.cljs:98 -msgid "modals.delete-account.owned-orgs.members-count" +msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s üye" #: src/app/main/ui/settings/delete_account.cljs:96 -msgid "modals.delete-account.owned-orgs.teams-count" +msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s takım" #: src/app/main/ui/settings/delete_account.cljs:64 @@ -3906,7 +3906,7 @@ msgid "modals.delete-font.title" msgstr "Yazı tipini sil" #: src/app/main/data/team.cljs:129 -msgid "modals.delete-org-team-confirm.message" +msgid "modals.delete-organization-team-confirm.message" msgstr "" "%s organizasyonunun parçası olan bu takımı silmek istediğinizden emin " "misiniz?" @@ -4054,7 +4054,7 @@ msgid "modals.invite-restricted-members.all-blocked-title" msgstr "Davet gönderilmedi" #: src/app/main/data/team.cljs:162 -msgid "modals.invite-restricted-members.all-org-members-in-team" +msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Bu takıma yalnızca \"%s\" organizasyonunun üyeleri davet edilebilir ve bu " "kişiler zaten takımın üyeleridir. Daha fazla bilgiye ihtiyacınız varsa, " @@ -4141,35 +4141,35 @@ msgid "modals.leave-confirm.title" msgstr "Takımdan ayrıl" #: src/app/main/ui/dashboard/change_owner.cljs:158 -msgid "modals.leave-org-and-reassign.hint" +msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Bazı organizasyon takımlarının sahibi sizsiniz. Lütfen başka bir üyeyi " "sahibi olarak atayın." #: src/app/main/ui/dashboard/change_owner.cljs:161 -msgid "modals.leave-org-and-reassign.hint-delete" +msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Bazı takımların tek üyesi sizsiniz. Bu takımlar, projeleriyle ve " "dosyalarıyla birlikte silinecek." #: src/app/main/ui/dashboard/change_owner.cljs:163 -msgid "modals.leave-org-and-reassign.hint-promote" +msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Ayrıca, bazı organizasyon takımlarının sahibi sizsiniz. Lütfen başka bir " "üyeyi sahibi olarak atayın." #: src/app/main/data/nitrate.cljs:200, src/app/main/data/nitrate.cljs:209 -msgid "modals.leave-org-confirm.accept" +msgid "modals.leave-organization-confirm.accept" msgstr "Organizasyondan ayrıl" #: src/app/main/data/nitrate.cljs:208 -msgid "modals.leave-org-confirm.message" +msgid "modals.leave-organization-confirm.message" msgstr "" "İçindeki tüm takımlara, projelere ve dosyalara erişiminizi kalıcı olarak " "kaybedeceksiniz." #: src/app/main/data/nitrate.cljs:207 -msgid "modals.leave-org-confirm.title" +msgid "modals.leave-organization-confirm.title" msgstr "%s organizasyonundan ayrılıyor musunuz?" #: src/app/main/ui/delete_shared.cljs:54 @@ -4244,21 +4244,21 @@ msgid "modals.remove-shared-confirm.message" msgstr "“%s” Paylaşılan Kütüphanesini Kaldır" #: src/app/main/data/nitrate.cljs:256 -msgid "modals.remove-team-org.accept" +msgid "modals.remove-team-organization.accept" msgstr "Organizasyondan kaldır" #: src/app/main/data/nitrate.cljs:254 -msgid "modals.remove-team-org.info" +msgid "modals.remove-team-organization.info" msgstr "" "Projeler ve dosyalar takım üyeleri tarafından kullanılabilir olmaya devam " "edecek, ancak organizasyonun ayarları artık geçerli olmayacaktır." #: src/app/main/data/nitrate.cljs:253 -msgid "modals.remove-team-org.text" +msgid "modals.remove-team-organization.text" msgstr "'%s' takımını '%s' organizasyonundan kaldırmak istediğinizden emin misiniz?" #: src/app/main/data/nitrate.cljs:252 -msgid "modals.remove-team-org.title" +msgid "modals.remove-team-organization.title" msgstr "TAKIMI ORGANİZASYONDAN KALDIR" #: src/app/main/ui/workspace/nudge.cljs:52 @@ -4326,7 +4326,7 @@ msgid "nitrate.activation-success.active-until" msgstr "Planınız %s tarihine kadar etkindir." #: src/app/main/ui/dashboard/subscription.cljs:195, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:68 -msgid "nitrate.activation-success.create-org" +msgid "nitrate.activation-success.create-organization" msgstr "Organizasyon oluştur" #: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:63 @@ -4379,7 +4379,7 @@ msgstr "" "ayarlandığından emin olacağız." #, unused -msgid "nitrate.contact-sales.downgrade-org-deleted" +msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Organizasyonunuz silinecek." #, unused @@ -5673,7 +5673,7 @@ msgid "shortcuts.zoom-selected" msgstr "Seçilene yakınlaştır" #: src/app/main/ui/dashboard/subscription.cljs:191 -msgid "subscription.banner.create-org-info" +msgid "subscription.banner.create-organization-info" msgstr "" "Bir organizasyon oluşturun ve Kurumsal planın tam olarak nasıl çalıştığını " "görün. Kurallar belirleyin, takımlarınızı yönetin ve tasarım iş akışınız " diff --git a/frontend/translations/ukr_UA.po b/frontend/translations/ukr_UA.po index f75760968f..82d58d69c0 100644 --- a/frontend/translations/ukr_UA.po +++ b/frontend/translations/ukr_UA.po @@ -368,7 +368,7 @@ msgid "dashboard.copy-suffix" msgstr "(копія)" #: src/app/main/ui/dashboard/sidebar.cljs:371 -msgid "dashboard.create-new-org" +msgid "dashboard.create-new-organization" msgstr "Створити організацію" #: src/app/main/ui/dashboard/sidebar.cljs:439