mirror of
https://github.com/penpot/penpot.git
synced 2025-05-30 02:06:10 +02:00
🎉 Allow import a template from a link
This commit is contained in:
parent
0d70ceb264
commit
2f5f31814a
6 changed files with 103 additions and 20 deletions
|
@ -111,6 +111,7 @@
|
||||||
(def grid-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
(def grid-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
||||||
(def plugins-list-uri (obj/get global "penpotPluginsListUri" "https://penpot.app/penpothub/plugins"))
|
(def plugins-list-uri (obj/get global "penpotPluginsListUri" "https://penpot.app/penpothub/plugins"))
|
||||||
(def plugins-whitelist (into #{} (obj/get global "penpotPluginsWhitelist" [])))
|
(def plugins-whitelist (into #{} (obj/get global "penpotPluginsWhitelist" [])))
|
||||||
|
(def templates-uri (obj/get global "penpotTemplatesUri" "https://penpot.github.io/penpot-files/"))
|
||||||
|
|
||||||
(defn- normalize-uri
|
(defn- normalize-uri
|
||||||
[uri-str]
|
[uri-str]
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
(mf/defc dashboard-legacy-redirect*
|
(mf/defc dashboard-legacy-redirect*
|
||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/private true}
|
::mf/private true}
|
||||||
[{:keys [section team-id project-id search-term plugin-url]}]
|
[{:keys [section team-id project-id search-term plugin-url template-url]}]
|
||||||
(let [section (case section
|
(let [section (case section
|
||||||
:dashboard-legacy-search
|
:dashboard-legacy-search
|
||||||
:dashboard-search
|
:dashboard-search
|
||||||
|
@ -97,7 +97,8 @@
|
||||||
(let [params {:team-id team-id
|
(let [params {:team-id team-id
|
||||||
:project-id project-id
|
:project-id project-id
|
||||||
:search-term search-term
|
:search-term search-term
|
||||||
:plugin plugin-url}]
|
:plugin plugin-url
|
||||||
|
:template-url template-url}]
|
||||||
(st/emit! (rt/nav section (d/without-nils params)))))
|
(st/emit! (rt/nav section (d/without-nils params)))))
|
||||||
|
|
||||||
[:> loader*
|
[:> loader*
|
||||||
|
@ -211,11 +212,12 @@
|
||||||
:dashboard-invitations
|
:dashboard-invitations
|
||||||
:dashboard-webhooks
|
:dashboard-webhooks
|
||||||
:dashboard-settings)
|
:dashboard-settings)
|
||||||
(let [params (get params :query)
|
(let [params (get params :query)
|
||||||
team-id (some-> params :team-id uuid)
|
team-id (some-> params :team-id uuid)
|
||||||
project-id (some-> params :project-id uuid)
|
project-id (some-> params :project-id uuid)
|
||||||
search-term (some-> params :search-term)
|
search-term (some-> params :search-term)
|
||||||
plugin-url (some-> params :plugin)]
|
plugin-url (some-> params :plugin)
|
||||||
|
template-url (some-> params :template)]
|
||||||
[:?
|
[:?
|
||||||
#_[:& app.main.ui.releases/release-notes-modal {:version "2.4"}]
|
#_[:& app.main.ui.releases/release-notes-modal {:version "2.4"}]
|
||||||
#_[:& app.main.ui.onboarding/onboarding-templates-modal]
|
#_[:& app.main.ui.onboarding/onboarding-templates-modal]
|
||||||
|
@ -241,7 +243,8 @@
|
||||||
:team-id team-id
|
:team-id team-id
|
||||||
:search-term search-term
|
:search-term search-term
|
||||||
:plugin-url plugin-url
|
:plugin-url plugin-url
|
||||||
:project-id project-id}]]])
|
:project-id project-id
|
||||||
|
:template-url template-url}]]])
|
||||||
|
|
||||||
:workspace
|
:workspace
|
||||||
(let [params (get params :query)
|
(let [params (get params :query)
|
||||||
|
@ -322,13 +325,15 @@
|
||||||
(let [team-id (some-> params :path :team-id uuid)
|
(let [team-id (some-> params :path :team-id uuid)
|
||||||
project-id (some-> params :path :project-id uuid)
|
project-id (some-> params :path :project-id uuid)
|
||||||
search-term (some-> params :query :search-term)
|
search-term (some-> params :query :search-term)
|
||||||
plugin-url (some-> params :query :plugin)]
|
plugin-url (some-> params :query :plugin)
|
||||||
|
template-url (some-> params :template)]
|
||||||
[:> dashboard-legacy-redirect*
|
[:> dashboard-legacy-redirect*
|
||||||
{:team-id team-id
|
{:team-id team-id
|
||||||
:section section
|
:section section
|
||||||
:project-id project-id
|
:project-id project-id
|
||||||
:search-term search-term
|
:search-term search-term
|
||||||
:plugin-url plugin-url}])
|
:plugin-url plugin-url
|
||||||
|
:template-url template-url}])
|
||||||
|
|
||||||
:viewer-legacy
|
:viewer-legacy
|
||||||
(let [{:keys [query-params path-params]} route
|
(let [{:keys [query-params path-params]} route
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.notifications :as notif]
|
[app.main.data.notifications :as notif]
|
||||||
[app.main.data.plugins :as dp]
|
[app.main.data.plugins :as dp]
|
||||||
|
[app.main.data.project :as dpj]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.router :as rt]
|
[app.main.router :as rt]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -33,9 +34,14 @@
|
||||||
[app.main.ui.workspace.plugins]
|
[app.main.ui.workspace.plugins]
|
||||||
[app.plugins.register :as preg]
|
[app.plugins.register :as preg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
[app.util.http :as http]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
|
[app.util.storage :as storage]
|
||||||
|
[app.util.webapi :as wapi]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]
|
||||||
|
@ -205,9 +211,47 @@
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(st/emit! (notif/error "The plugin URL is incorrect")))))))))
|
(st/emit! (notif/error "The plugin URL is incorrect")))))))))
|
||||||
|
|
||||||
|
(defn use-templates-import
|
||||||
|
[can-edit? template-url default-project-id]
|
||||||
|
(mf/with-layout-effect
|
||||||
|
[can-edit? template-url default-project-id]
|
||||||
|
(when (and (some? template-url) (some? default-project-id))
|
||||||
|
(if can-edit?
|
||||||
|
(let [valid-url? (and (str/ends-with? template-url ".penpot")
|
||||||
|
(str/starts-with? template-url cf/templates-uri))
|
||||||
|
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
|
||||||
|
on-import #(st/emit! (dpj/fetch-files default-project-id)
|
||||||
|
(dd/fetch-recent-files)
|
||||||
|
(dd/fetch-projects)
|
||||||
|
(dd/clear-selected-files)
|
||||||
|
(ptk/event ::ev/event {::ev/name "install-template-from-link-finished"
|
||||||
|
:name template-name
|
||||||
|
:url template-url}))]
|
||||||
|
(if valid-url?
|
||||||
|
(do
|
||||||
|
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
|
||||||
|
(->> (http/send! {:method :get
|
||||||
|
:uri template-url
|
||||||
|
:response-type :blob
|
||||||
|
:omit-default-headers true})
|
||||||
|
(rx/subs!
|
||||||
|
(fn [result]
|
||||||
|
(if (or (< (:status result) 200) (>= (:status result) 300))
|
||||||
|
(st/emit! (notif/error (tr "dashboard.import.error")))
|
||||||
|
(st/emit! (modal/show
|
||||||
|
{:type :import
|
||||||
|
:project-id default-project-id
|
||||||
|
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
|
||||||
|
:on-finish-import on-import})))))))
|
||||||
|
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
|
||||||
|
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
|
||||||
|
|
||||||
|
(binding [storage/*sync* true]
|
||||||
|
(swap! storage/session dissoc :template-url)))))
|
||||||
|
|
||||||
(mf/defc dashboard*
|
(mf/defc dashboard*
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
[{:keys [profile project-id team-id search-term plugin-url section]}]
|
[{:keys [profile project-id team-id search-term plugin-url template-url section]}]
|
||||||
(let [team (mf/deref refs/team)
|
(let [team (mf/deref refs/team)
|
||||||
projects (mf/deref refs/projects)
|
projects (mf/deref refs/projects)
|
||||||
|
|
||||||
|
@ -216,6 +260,9 @@
|
||||||
(->> (vals projects)
|
(->> (vals projects)
|
||||||
(filterv #(= team-id (:team-id %)))))
|
(filterv #(= team-id (:team-id %)))))
|
||||||
|
|
||||||
|
can-edit? (dm/get-in team [:permissions :can-edit])
|
||||||
|
template-url (or template-url (:template-url storage/session))
|
||||||
|
|
||||||
default-project
|
default-project
|
||||||
(mf/with-memo [projects]
|
(mf/with-memo [projects]
|
||||||
(->> projects
|
(->> projects
|
||||||
|
@ -239,6 +286,7 @@
|
||||||
(events/unlistenByKey key))))
|
(events/unlistenByKey key))))
|
||||||
|
|
||||||
(use-plugin-register plugin-url team-id (:id default-project))
|
(use-plugin-register plugin-url team-id (:id default-project))
|
||||||
|
(use-templates-import can-edit? template-url (:id default-project))
|
||||||
|
|
||||||
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
||||||
[:> modal-container*]
|
[:> modal-container*]
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.router :as rt]
|
[app.main.router :as rt]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.util.storage :as storage]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.v2.core :as ptk]))
|
[potok.v2.core :as ptk]))
|
||||||
|
@ -75,14 +76,23 @@
|
||||||
["/workspace" :workspace]
|
["/workspace" :workspace]
|
||||||
["/workspace/:project-id/:file-id" :workspace-legacy]])
|
["/workspace/:project-id/:file-id" :workspace-legacy]])
|
||||||
|
|
||||||
|
|
||||||
|
(defn- store-session-params
|
||||||
|
[{:keys [template]}]
|
||||||
|
(binding [storage/*sync* true]
|
||||||
|
(when (some? template)
|
||||||
|
(swap! storage/session assoc
|
||||||
|
:template-url template))))
|
||||||
|
|
||||||
(defn on-navigate
|
(defn on-navigate
|
||||||
[router path]
|
[router path]
|
||||||
(let [location (.-location js/document)
|
(let [location (.-location js/document)
|
||||||
[base-path qs] (str/split path "?")
|
[base-path qs] (str/split path "?")
|
||||||
location-path (dm/str (.-origin location) (.-pathname location))
|
location-path (dm/str (.-origin location) (.-pathname location))
|
||||||
valid-location? (= location-path (dm/str cf/public-uri))
|
valid-location? (= location-path (dm/str cf/public-uri))
|
||||||
match (rt/match router path)
|
match (rt/match router path)
|
||||||
empty-path? (or (= base-path "") (= base-path "/"))]
|
empty-path? (or (= base-path "") (= base-path "/"))
|
||||||
|
query-params (u/query-string->map qs)]
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(not valid-location?)
|
(not valid-location?)
|
||||||
|
@ -99,14 +109,15 @@
|
||||||
(rx/subs! (fn [{:keys [id] :as profile}]
|
(rx/subs! (fn [{:keys [id] :as profile}]
|
||||||
(cond
|
(cond
|
||||||
(= id uuid/zero)
|
(= id uuid/zero)
|
||||||
(st/emit! (rt/nav :auth-login))
|
(do
|
||||||
|
(store-session-params query-params)
|
||||||
|
(st/emit! (rt/nav :auth-login)))
|
||||||
|
|
||||||
empty-path?
|
empty-path?
|
||||||
(let [team-id (or (dtm/get-last-team-id)
|
(let [team-id (or (dtm/get-last-team-id)
|
||||||
(:default-team-id profile))]
|
(:default-team-id profile))]
|
||||||
(st/emit! (rt/nav :dashboard-recent
|
(st/emit! (rt/nav :dashboard-recent
|
||||||
(-> (u/query-string->map qs)
|
(assoc query-params :team-id team-id))))
|
||||||
(assoc :team-id team-id)))))
|
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (rt/assign-exception {:type :not-found})))))))))
|
(st/emit! (rt/assign-exception {:type :not-found})))))))))
|
||||||
|
|
|
@ -6966,6 +6966,15 @@ msgstr "None"
|
||||||
msgid "dashboard.settings.notifications.submit"
|
msgid "dashboard.settings.notifications.submit"
|
||||||
msgstr "Update settings"
|
msgstr "Update settings"
|
||||||
|
|
||||||
|
msgid "dashboard.import.error"
|
||||||
|
msgstr "Import failed. Please try again"
|
||||||
|
|
||||||
|
msgid "dashboard.import.bad-url"
|
||||||
|
msgstr "Import failed. The template URL is incorrect"
|
||||||
|
|
||||||
|
msgid "dashboard.import.no-perms"
|
||||||
|
msgstr "You don’t have permission to import to this team"
|
||||||
|
|
||||||
msgid "labels.notifications"
|
msgid "labels.notifications"
|
||||||
msgstr "Notifications"
|
msgstr "Notifications"
|
||||||
|
|
||||||
|
|
|
@ -6687,7 +6687,7 @@ msgstr "Nombre"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||||
msgid "workspace.token.enter-token-name"
|
msgid "workspace.token.enter-token-name"
|
||||||
msgstr "Introduce un nombre para el token %s"
|
msgstr "Introduce un nombre para el token %s"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||||
msgid "workspace.token.token-value"
|
msgid "workspace.token.token-value"
|
||||||
|
@ -6923,6 +6923,15 @@ msgstr "Ninguna"
|
||||||
msgid "dashboard.settings.notifications.submit"
|
msgid "dashboard.settings.notifications.submit"
|
||||||
msgstr "Actualizar configuración"
|
msgstr "Actualizar configuración"
|
||||||
|
|
||||||
|
msgid "dashboard.import.error"
|
||||||
|
msgstr "La importación ha fallado. Intentalo de nuevo, por favor"
|
||||||
|
|
||||||
|
msgid "dashboard.import.bad-url"
|
||||||
|
msgstr "La importación ha fallado. La URL de la plantilla es incorrecta"
|
||||||
|
|
||||||
|
msgid "dashboard.import.no-perms"
|
||||||
|
msgstr "No tienes permisos para importar en este equipo"
|
||||||
|
|
||||||
msgid "labels.notifications"
|
msgid "labels.notifications"
|
||||||
msgstr "Notificaciones"
|
msgstr "Notificaciones"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue