0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-04 13:50:12 -05:00

🎉 Convert libraries to file libraries

This commit is contained in:
Andrés Moya 2020-07-28 14:57:55 +02:00
parent 8c8b5887d6
commit 49c57be84a
23 changed files with 605 additions and 295 deletions

View file

@ -0,0 +1,29 @@
-- ALTER TABLE color_library
-- DROP COLUMN team_id,
-- DROP COLUMN name,
-- ADD COLUMN file_id uuid NOT NULL REFERENCES file(id) ON DELETE CASCADE;
--
-- CREATE INDEX color_library__file_id__idx
-- ON color_library(file_id);
TRUNCATE TABLE color;
TRUNCATE TABLE color_library CASCADE;
TRUNCATE TABLE image;
TRUNCATE TABLE image_library CASCADE;
TRUNCATE TABLE icon;
TRUNCATE TABLE icon_library CASCADE;
ALTER TABLE color
DROP COLUMN library_id,
ADD COLUMN file_id uuid NOT NULL REFERENCES file(id) ON DELETE CASCADE;
CREATE INDEX color__file_id__idx
ON color(file_id);
ALTER TABLE image
DROP COLUMN library_id,
ADD COLUMN file_id uuid NOT NULL REFERENCES file(id) ON DELETE CASCADE;
CREATE INDEX image__file_id__idx
ON image(file_id);

View file

@ -48,7 +48,7 @@
(s/def ::width integer?)
(s/def ::height integer?)
(s/def ::format #{:jpeg :webp :png})
(s/def ::format #{:jpeg :webp :png :svg})
(s/def ::quality #(< 0 % 101))
(s/def ::thumbnail-params
@ -62,21 +62,24 @@
(case format
:png ".png"
:jpeg ".jpg"
:webp ".webp"))
:webp ".webp"
:svg ".svg"))
(defn format->mtype
[format]
(case format
:png "image/png"
:jpeg "image/jpeg"
:webp "image/webp"))
:webp "image/webp"
:svg "image/svg+xml"))
(defn mtype->format
[mtype]
(case mtype
"image/jpeg" :jpeg
"image/webp" :webp
"image/png" :png
"image/png" :png
"image/jpeg" :jpeg
"image/webp" :webp
"image/svg+xml" :svg
nil))
(defn- generic-process
@ -127,18 +130,21 @@
(defmethod process :info
[{:keys [input] :as params}]
(us/assert ::input input)
(let [{:keys [path mtype]} input
instance (Info. (str path))
mtype' (.getProperty instance "Mime type")]
(when (and (string? mtype)
(not= mtype mtype'))
(ex/raise :type :validation
:code :image-type-mismatch
:hint "Seems like you are uploading a file whose content does not match the extension."))
{:width (.getImageWidth instance)
:height (.getImageHeight instance)
:mtype mtype'}))
(let [{:keys [path mtype]} input]
(if (= mtype "image/svg+xml")
{:width 100
:height 100
:mtype mtype}
(let [instance (Info. (str path))
mtype' (.getProperty instance "Mime type")]
(when (and (string? mtype)
(not= mtype mtype'))
(ex/raise :type :validation
:code :image-type-mismatch
:hint "Seems like you are uploading a file whose content does not match the extension."))
{:width (.getImageWidth instance)
:height (.getImageHeight instance)
:mtype mtype'}))))
(defmethod process :default
[{:keys [cmd] :as params}]
@ -164,13 +170,15 @@
(defn resolve-urls
[row src dst]
(s/assert map? row)
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri media/media-storage value)]
(assoc-in row dst (str url))))))
(if (and src dst)
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri media/media-storage value)]
(assoc-in row dst (str url)))))
row))
(defn- resolve-uri
[storage row src dst]

View file

@ -29,6 +29,8 @@
[uxbox.util.blob :as blob]
[uxbox.common.uuid :as uuid]
[uxbox.util.data :as data]
[uxbox.services.mutations.projects :as projects]
[uxbox.services.mutations.files :as files]
[uxbox.services.mutations.colors :as colors]
[uxbox.services.mutations.icons :as icons]
[uxbox.services.mutations.images :as images]
@ -49,120 +51,243 @@
(s/def ::path ::us/string)
(s/def ::regex #(instance? java.util.regex.Pattern %))
(s/def ::colors
;; (s/def ::colors
;; (s/* (s/cat :name ::us/string :color ::us/color)))
;;
;; (s/def ::import-item-media
;; (s/keys :req-un [::name ::path ::regex]))
;;
;; (s/def ::import-item-color
;; (s/keys :req-un [::name ::id ::colors]))
(s/def ::import-images
(s/keys :req-un [::path ::regex]))
(s/def ::import-color
(s/* (s/cat :name ::us/string :color ::us/color)))
(s/def ::import-item-media
(s/keys :req-un [::name ::path ::regex]))
(s/def ::import-colors (s/coll-of ::import-color))
(s/def ::import-item-color
(s/keys :req-un [::name ::id ::colors]))
(s/def ::import-library
(s/keys :req-un [::name]
:opt-un [::import-images ::import-colors]))
(defn exit!
([] (exit! 0))
([code]
(System/exit code)))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; Icons Libraries Importer
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; (defn- icon-library-exists?
;; [conn id]
;; (s/assert ::us/uuid id)
;; (let [row (db/get-by-id conn :icon-library id)]
;; (if row true false)))
;;
;; (defn- create-icons-library
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +icons-uuid-ns+ name)]
;; (log/info "Creating icons library:" name)
;; (icons/create-library conn {:team-id uuid/zero
;; :id id
;; :name name})))
;;
;; (defn- create-icons-library-if-not-exists
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +icons-uuid-ns+ name)]
;; (when-not (icon-library-exists? conn id)
;; (create-icons-library conn item))
;; id))
;;
;; (defn- create-icon
;; [conn library-id icon-id localpath]
;; (s/assert fs/path? localpath)
;; (s/assert ::us/uuid library-id)
;; (s/assert ::us/uuid icon-id)
;; (let [filename (fs/name localpath)
;; extension (second (fs/split-ext filename))
;; data (svg/parse localpath)
;; mdata (select-keys data [:width :height :view-box])]
;;
;; (log/info "Creating or updating icon" filename icon-id)
;; (icons/create-icon conn {:id icon-id
;; :library-id library-id
;; :name (:name data filename)
;; :content (:content data)
;; :metadata mdata})))
;;
;; (defn- icon-exists?
;; [conn id]
;; (s/assert ::us/uuid id)
;; (let [row (db/get-by-id conn :icon id)]
;; (if row true false)))
;;
;; (defn- import-icon-if-not-exists
;; [conn library-id fpath]
;; (s/assert ::us/uuid library-id)
;; (s/assert fs/path? fpath)
;; (let [icon-id (uuid/namespaced +icons-uuid-ns+ (str library-id (fs/name fpath)))]
;; (when-not (icon-exists? conn icon-id)
;; (create-icon conn library-id icon-id fpath))
;; icon-id))
;;
;; (defn- import-icons
;; [conn library-id {:keys [path regex] :as item}]
;; (run! (fn [fpath]
;; (when (re-matches regex (str fpath))
;; (import-icon-if-not-exists conn library-id fpath)))
;; (->> (fs/list-dir path)
;; (filter fs/regular-file?))))
;;
;; (defn- process-icons-library
;; [conn basedir {:keys [path regex] :as item}]
;; (s/assert ::import-item-media item)
;; (let [library-id (create-icons-library-if-not-exists conn item)]
;; (->> (assoc item :path (fs/join basedir path))
;; (import-icons conn library-id))))
;;
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; --- Images Libraries Importer
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; (defn- image-library-exists?
;; [conn id]
;; (s/assert ::us/uuid id)
;; (let [row (db/get-by-id conn :image-library id)]
;; (if row true false)))
;;
;; (defn- create-images-library
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +images-uuid-ns+ name)]
;; (log/info "Creating image library:" name)
;; (images/create-library conn {:id id
;; :team-id uuid/zero
;; :name name})))
;;
;; (defn- create-images-library-if-not-exists
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +images-uuid-ns+ name)]
;; (when-not (image-library-exists? conn id)
;; (create-images-library conn item)
;; id)))
;;
;; (defn- create-image
;; [conn library-id image-id localpath]
;; (s/assert fs/path? localpath)
;; (s/assert ::us/uuid library-id)
;; (s/assert ::us/uuid image-id)
;; (let [filename (fs/name localpath)
;; extension (second (fs/split-ext filename))
;; file (io/as-file localpath)
;; mtype (case extension
;; ".jpg" "image/jpeg"
;; ".png" "image/png"
;; ".webp" "image/webp")]
;; (log/info "Creating image" filename image-id)
;; (images/create-image conn {:content {:tempfile localpath
;; :filename filename
;; :content-type mtype
;; :size (.length file)}
;; :id image-id
;; :library-id library-id
;; :user uuid/zero
;; :name filename})))
;;
;; (defn- image-exists?
;; [conn id]
;; (s/assert ::us/uuid id)
;; (let [row (db/get-by-id conn :image id)]
;; (if row true false)))
;;
;; (defn- import-image-if-not-exists
;; [conn library-id fpath]
;; (s/assert ::us/uuid library-id)
;; (s/assert fs/path? fpath)
;; (let [image-id (uuid/namespaced +images-uuid-ns+ (str library-id (fs/name fpath)))]
;; (when-not (image-exists? conn image-id)
;; (create-image conn library-id image-id fpath))
;; image-id))
;;
;; (defn- import-images
;; [conn library-id {:keys [path regex] :as item}]
;; (run! (fn [fpath]
;; (when (re-matches regex (str fpath))
;; (import-image-if-not-exists conn library-id fpath)))
;; (->> (fs/list-dir path)
;; (filter fs/regular-file?))))
;;
;; (defn- process-images-library
;; [conn basedir {:keys [path regex] :as item}]
;; (s/assert ::import-item-media item)
;; (let [library-id (create-images-library-if-not-exists conn item)]
;; (->> (assoc item :path (fs/join basedir path))
;; (import-images conn library-id))))
;;
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; Colors Libraries Importer
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; (defn- color-library-exists?
;; [conn id]
;; (s/assert ::us/uuid id)
;; (let [row (db/get-by-id conn :file id)]
;; (if row true false)))
;;
;; (defn- create-colors-library
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +colors-uuid-ns+ name)]
;; (log/info "Creating color library:" name)
;; (colors/create-library conn {:id id
;; :team-id uuid/zero
;; :name name})))
;;
;;
;; (defn- create-colors-library-if-not-exists
;; [conn {:keys [name] :as item}]
;; (let [id (uuid/namespaced +colors-uuid-ns+ name)]
;; (when-not (color-library-exists? conn id)
;; (create-colors-library conn item))
;; id))
;;
;; (defn- create-color
;; [conn library-id name content]
;; (s/assert ::us/uuid library-id)
;; (s/assert ::us/color content)
;; (let [color-id (uuid/namespaced +colors-uuid-ns+ (str library-id content))]
;; (log/info "Creating color" color-id "-" name content)
;; (colors/create-color conn {:id color-id
;; :library-id library-id
;; :name name
;; :content content})
;; color-id))
;;
;; (defn- import-colors
;; [conn library-id {:keys [colors] :as item}]
;; (db/delete! conn :color {:library-id library-id})
;; (run! (fn [[name content]]
;; (create-color conn library-id name content))
;; (partition-all 2 colors)))
;;
;; (defn- process-colors-library
;; [conn {:keys [name id colors] :as item}]
;; (us/verify ::import-item-color item)
;; (let [library-id (create-colors-library-if-not-exists conn item)]
;; (import-colors conn library-id item)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Icons Libraries Importer
;; Images Importer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- icon-library-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :icon-library id)]
(if row true false)))
(defn- create-icons-library
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +icons-uuid-ns+ name)]
(log/info "Creating icons library:" name)
(icons/create-library conn {:team-id uuid/zero
:id id
:name name})))
(defn- create-icons-library-if-not-exists
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +icons-uuid-ns+ name)]
(when-not (icon-library-exists? conn id)
(create-icons-library conn item))
id))
(defn- create-icon
[conn library-id icon-id localpath]
(s/assert fs/path? localpath)
(s/assert ::us/uuid library-id)
(s/assert ::us/uuid icon-id)
(let [filename (fs/name localpath)
extension (second (fs/split-ext filename))
data (svg/parse localpath)
mdata (select-keys data [:width :height :view-box])]
(log/info "Creating or updating icon" filename icon-id)
(icons/create-icon conn {:id icon-id
:library-id library-id
:name (:name data filename)
:content (:content data)
:metadata mdata})))
(defn- icon-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :icon id)]
(if row true false)))
(defn- import-icon-if-not-exists
[conn library-id fpath]
(s/assert ::us/uuid library-id)
(s/assert fs/path? fpath)
(let [icon-id (uuid/namespaced +icons-uuid-ns+ (str library-id (fs/name fpath)))]
(when-not (icon-exists? conn icon-id)
(create-icon conn library-id icon-id fpath))
icon-id))
(defn- import-icons
[conn library-id {:keys [path regex] :as item}]
(run! (fn [fpath]
(when (re-matches regex (str fpath))
(import-icon-if-not-exists conn library-id fpath)))
(->> (fs/list-dir path)
(filter fs/regular-file?))))
(defn- process-icons-library
[conn basedir {:keys [path regex] :as item}]
(s/assert ::import-item-media item)
(let [library-id (create-icons-library-if-not-exists conn item)]
(->> (assoc item :path (fs/join basedir path))
(import-icons conn library-id))))
;; --- Images Libraries Importer
(defn- image-library-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :image-library id)]
(if row true false)))
(defn- create-images-library
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +images-uuid-ns+ name)]
(log/info "Creating image library:" name)
(images/create-library conn {:id id
:team-id uuid/zero
:name name})))
(defn- create-images-library-if-not-exists
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +images-uuid-ns+ name)]
(when-not (image-library-exists? conn id)
(create-images-library conn item)
id)))
(defn- create-image
[conn library-id image-id localpath]
[conn file-id image-id localpath]
(s/assert fs/path? localpath)
(s/assert ::us/uuid library-id)
(s/assert ::us/uuid file-id)
(s/assert ::us/uuid image-id)
(let [filename (fs/name localpath)
extension (second (fs/split-ext filename))
@ -170,14 +295,15 @@
mtype (case extension
".jpg" "image/jpeg"
".png" "image/png"
".webp" "image/webp")]
".webp" "image/webp"
".svg" "image/svg+xml")]
(log/info "Creating image" filename image-id)
(images/create-image conn {:content {:tempfile localpath
:filename filename
:content-type mtype
:size (.length file)}
:id image-id
:library-id library-id
:file-id file-id
:user uuid/zero
:name filename})))
@ -188,85 +314,100 @@
(if row true false)))
(defn- import-image-if-not-exists
[conn library-id fpath]
(s/assert ::us/uuid library-id)
[conn file-id fpath]
(s/assert ::us/uuid file-id)
(s/assert fs/path? fpath)
(let [image-id (uuid/namespaced +images-uuid-ns+ (str library-id (fs/name fpath)))]
(let [image-id (uuid/namespaced +images-uuid-ns+ (str file-id (fs/name fpath)))]
(when-not (image-exists? conn image-id)
(create-image conn library-id image-id fpath))
(create-image conn file-id image-id fpath))
image-id))
(defn- import-images
[conn library-id {:keys [path regex] :as item}]
[conn file-id {:keys [path regex] :as images}]
(run! (fn [fpath]
(when (re-matches regex (str fpath))
(import-image-if-not-exists conn library-id fpath)))
(import-image-if-not-exists conn file-id fpath)))
(->> (fs/list-dir path)
(filter fs/regular-file?))))
(defn- process-images-library
[conn basedir {:keys [path regex] :as item}]
(s/assert ::import-item-media item)
(let [library-id (create-images-library-if-not-exists conn item)]
(->> (assoc item :path (fs/join basedir path))
(import-images conn library-id))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Colors Libraries Importer
;; Colors Importer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- color-library-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :color-library id)]
(if row true false)))
(defn- create-colors-library
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +colors-uuid-ns+ name)]
(log/info "Creating color library:" name)
(colors/create-library conn {:id id
:team-id uuid/zero
:name name})))
(defn- create-colors-library-if-not-exists
[conn {:keys [name] :as item}]
(let [id (uuid/namespaced +colors-uuid-ns+ name)]
(when-not (color-library-exists? conn id)
(create-colors-library conn item))
id))
(defn- create-color
[conn library-id name content]
(s/assert ::us/uuid library-id)
[conn file-id name content]
(s/assert ::us/uuid file-id)
(s/assert ::us/color content)
(let [color-id (uuid/namespaced +colors-uuid-ns+ (str library-id content))]
(let [color-id (uuid/namespaced +colors-uuid-ns+ (str file-id content))]
(log/info "Creating color" color-id "-" name content)
(colors/create-color conn {:id color-id
:library-id library-id
:file-id file-id
:name name
:content content})
color-id))
(defn- import-colors
[conn library-id {:keys [colors] :as item}]
(db/delete! conn :color {:library-id library-id})
[conn file-id colors]
(db/delete! conn :color {:file-id file-id})
(run! (fn [[name content]]
(create-color conn library-id name content))
(create-color conn file-id name content))
(partition-all 2 colors)))
(defn- process-colors-library
[conn {:keys [name id colors] :as item}]
(us/verify ::import-item-color item)
(let [library-id (create-colors-library-if-not-exists conn item)]
(import-colors conn library-id item)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Libraries Importer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- library-file-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :file id)]
(if row true false)))
(defn- create-library-file-if-not-exists
[conn project-id {:keys [name] :as library-file}]
(let [id (uuid/namespaced +colors-uuid-ns+ name)]
(when-not (library-file-exists? conn id)
(log/info "Creating library-file:" name)
(files/create-file conn {:id id
:profile-id uuid/zero
:project-id project-id
:name name})
(files/create-page conn {:file-id id}))
id))
(defn- process-library
[conn basedir project-id {:keys [name images colors] :as library}]
(us/verify ::import-library library)
(let [library-file-id (create-library-file-if-not-exists conn project-id library)]
(when images
(->> (assoc images :path (fs/join basedir (:path images)))
(import-images conn library-file-id)))
(when colors
(import-colors conn library-file-id colors))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Entry Point
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- project-exists?
[conn id]
(s/assert ::us/uuid id)
(let [row (db/get-by-id conn :project id)]
(if row true false)))
(defn- create-project-if-not-exists
[conn {:keys [name] :as project}]
(let [id (uuid/namespaced +colors-uuid-ns+ name)]
(when-not (project-exists? conn id)
(log/info "Creating project" name)
(projects/create-project conn {:id id
:team-id uuid/zero
:name name
:default? false}))
id))
(defn- validate-path
[path]
(when-not path
@ -295,20 +436,21 @@
[]
(mount/stop))
(defn- importer
[conn basedir data]
(let [images (:images data)
icons (:icons data)
colors (:colors data)]
(run! #(process-images-library conn basedir %) images)
(run! #(process-icons-library conn basedir %) icons)
(run! #(process-colors-library conn %) colors)))
;; (defn- importer
;; [conn basedir data]
;; (let [images (:images data)
;; icons (:icons data)
;; colors (:colors data)]
;; (run! #(process-images-library conn basedir %) images)
;; (run! #(process-icons-library conn basedir %) icons)
;; (run! #(process-colors-library conn %) colors)))
(defn run
[path]
(let [[basedir data] (read-file path)]
(let [[basedir libraries] (read-file path)]
(db/with-atomic [conn db/pool]
(importer conn basedir data))))
(let [project-id (create-project-if-not-exists conn {:name "Media loader"})]
(run! #(process-library conn basedir project-id %) libraries)))))
(defn -main
[& [path]]

View file

@ -59,7 +59,11 @@
{:desc "Add session_id field to page_change table"
:name "0011-add-session-id-field-to-page-change-table"
:fn (mg/resource "migrations/0011-add-session-id-field-to-page-change-table.sql")}]})
:fn (mg/resource "migrations/0011-add-session-id-field-to-page-change-table.sql")}
{:desc "Make libraries linked to a file"
:name "0012-make-libraries-linked-to-a-file"
:fn (mg/resource "migrations/0012-make-libraries-linked-to-a-file.sql")}]})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Entry point

View file

@ -119,26 +119,27 @@
(declare create-color)
(s/def ::create-color
(s/keys :req-un [::profile-id ::name ::content ::library-id]
(s/keys :req-un [::profile-id ::name ::content ::file-id]
:opt-un [::id]))
(sm/defmutation ::create-color
[{:keys [profile-id library-id] :as params}]
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [lib (select-library-for-update conn library-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(create-color conn params))))
(create-color conn params)))
;; (let [lib (select-library-for-update conn library-id)]
;; (teams/check-edition-permissions! conn profile-id (:team-id lib))
;; (create-color conn params))))
(def ^:private sql:create-color
"insert into color (id, name, library_id, content)
"insert into color (id, name, file_id, content)
values ($1, $2, $3, $4) returning *")
(defn create-color
[conn {:keys [id name library-id content]}]
[conn {:keys [id name file-id content]}]
(let [id (or id (uuid/next))]
(db/insert! conn :color {:id id
:name name
:library-id library-id
:file-id file-id
:content content})))

View file

@ -62,7 +62,7 @@
:is-admin true
:can-edit true}))
(defn- create-file
(defn create-file
[conn {:keys [id profile-id name project-id] :as params}]
(let [id (or id (uuid/next))
file (db/insert! conn :file {:id id :project-id project-id :name name})]
@ -70,7 +70,7 @@
(create-file-profile conn))
file))
(defn- create-page
(defn create-page
[conn {:keys [file-id] :as params}]
(let [id (uuid/next)]
(db/insert! conn :page

View file

@ -33,7 +33,7 @@
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::library-id ::us/uuid)
(s/def ::file-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::url ::us/url)
@ -112,7 +112,7 @@
(declare persist-image-thumbnail-on-fs)
(def valid-image-types?
#{"image/jpeg", "image/png", "image/webp"})
#{"image/jpeg", "image/png", "image/webp", "image/svg+xml"})
(s/def :uxbox$upload/filename ::us/string)
(s/def :uxbox$upload/size ::us/integer)
@ -128,17 +128,17 @@
(s/def ::content ::upload)
(s/def ::add-image-from-url
(s/keys :req-un [::profile-id ::library-id ::url]
(s/keys :req-un [::profile-id ::file-id ::url]
:opt-un [::id]))
(s/def ::upload-image
(s/keys :req-un [::profile-id ::library-id ::name ::content]
(s/keys :req-un [::profile-id ::file-id ::name ::content]
:opt-un [::id]))
(sm/defmutation ::add-image-from-url
[{:keys [profile-id library-id url] :as params}]
[{:keys [profile-id file-id url] :as params}]
(db/with-atomic [conn db/pool]
(let [lib (select-library-for-update conn library-id)]
(let [lib (select-library-for-update conn file-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(let [content (images/download-image url)
params' (merge params {:content content
@ -146,14 +146,14 @@
(create-image conn params')))))
(sm/defmutation ::upload-image
[{:keys [profile-id library-id] :as params}]
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [lib (select-library-for-update conn library-id)]
(let [lib (select-library-for-update conn file-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(create-image conn params))))
(defn create-image
[conn {:keys [id content library-id name]}]
[conn {:keys [id content file-id name]}]
(when-not (valid-image-types? (:content-type content))
(ex/raise :type :validation
:code :image-type-not-allowed
@ -165,11 +165,15 @@
opts (assoc thumbnail-options
:input {:mtype (:mtype info)
:path path})
thumb (persist-image-thumbnail-on-fs opts)]
thumb (if-not (= (:mtype info) "image/svg+xml")
(persist-image-thumbnail-on-fs opts)
(assoc info
:path path
:quality 0))]
(-> (db/insert! conn :image
{:id (or id (uuid/next))
:library-id library-id
:file-id file-id
:name name
:path (str path)
:width (:width info)

View file

@ -28,9 +28,10 @@
(s/def ::id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::file-id ::us/uuid)
(s/def ::library-id (s/nilable ::us/uuid))
;; --- Query: Colors Librarys
;; --- Query: Colors Libraries
(def ^:private sql:libraries
"select lib.*,
@ -78,31 +79,31 @@
(ex/raise :type :not-found))
row))
;; --- Query: Colors (by library)
;; --- Query: Colors (by file)
(declare retrieve-colors)
(s/def ::colors
(s/keys :req-un [::profile-id ::library-id]))
(s/keys :req-un [::profile-id ::file-id]))
(sq/defquery ::colors
[{:keys [profile-id library-id] :as params}]
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [lib (retrieve-library conn library-id)]
(teams/check-read-permissions! conn profile-id (:team-id lib))
(retrieve-colors conn library-id))))
(retrieve-colors conn file-id)))
;; (let [lib (retrieve-library conn library-id)]
;; (teams/check-read-permissions! conn profile-id (:team-id lib))
;; (retrieve-colors conn library-id))))
(def ^:private sql:colors
"select color.*
from color as color
inner join color_library as lib on (lib.id = color.library_id)
"select *
from color
where color.deleted_at is null
and color.library_id = ?
and color.file_id = ?
order by created_at desc")
(defn- retrieve-colors
[conn library-id]
(db/exec! conn [sql:colors library-id]))
[conn file-id]
(db/exec! conn [sql:colors file-id]))
;; --- Query: Color (by ID)

View file

@ -53,6 +53,11 @@
and (ppr.is_admin = true or
ppr.is_owner = true or
ppr.can_edit = true)
union
select p.*
from project as p
where p.team_id = uuid_nil()
and p.deleted_at is null
)
select distinct
file.*,
@ -80,23 +85,52 @@
(mapv decode-row rows)))
;; --- Query: Draft Files
;; --- Query: Project Files
(def ^:private sql:files
"select distinct
"with projects as (
select p.*
from project as p
inner join team_profile_rel as tpr on (tpr.team_id = p.team_id)
where tpr.profile_id = ?
and p.deleted_at is null
and (tpr.is_admin = true or
tpr.is_owner = true or
tpr.can_edit = true)
union
select p.*
from project as p
inner join project_profile_rel as ppr on (ppr.project_id = p.id)
where ppr.profile_id = ?
and p.deleted_at is null
and (ppr.is_admin = true or
ppr.is_owner = true or
ppr.can_edit = true)
union
select p.*
from project as p
where p.team_id = uuid_nil()
and p.deleted_at is null
)
select distinct
f.*,
array_agg(pg.id) over pages_w as pages,
first_value(pg.data) over pages_w as data
from file as f
inner join file_profile_rel as fp_r on (fp_r.file_id = f.id)
left join page as pg on (f.id = pg.file_id)
where fp_r.profile_id = ?
and f.project_id = ?
where f.project_id = ?
and (exists (select *
from file_profile_rel as fp_r
where fp_r.profile_id = ?
and fp_r.file_id = f.id
and (fp_r.is_admin = true or
fp_r.is_owner = true or
fp_r.can_edit = true))
or exists (select *
from projects as p
where p.id = f.project_id))
and f.deleted_at is null
and pg.deleted_at is null
and (fp_r.is_admin = true or
fp_r.is_owner = true or
fp_r.can_edit = true)
window pages_w as (partition by f.id order by pg.ordering
range between unbounded preceding
and unbounded following)
@ -108,7 +142,9 @@
(sq/defquery ::files
[{:keys [profile-id project-id] :as params}]
(->> (db/exec! db/pool [sql:files profile-id project-id])
(->> (db/exec! db/pool [sql:files
profile-id profile-id
project-id profile-id])
(mapv decode-row)))
;; --- Query: File Permissions
@ -136,7 +172,12 @@
from project_profile_rel as ppr
inner join file as f on (f.project_id = ppr.project_id)
where f.id = ?
and ppr.profile_id = ?;")
and ppr.profile_id = ?
union all
select true, true, true
from file as f
inner join project as p on (f.project_id = p.id)
and p.team_id = uuid_nil();")
(defn check-edition-permissions!
[conn profile-id file-id]

View file

@ -21,7 +21,7 @@
(s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::library-id ::us/uuid)
(s/def ::file-id ::us/uuid)
;; --- Query: Image Librarys
@ -77,32 +77,34 @@
(declare retrieve-images)
(s/def ::images
(s/keys :req-un [::profile-id ::library-id]))
(s/keys :req-un [::profile-id ::file-id]))
;; TODO: check if we can resolve url with transducer for reduce
;; garbage generation for each request
(sq/defquery ::images
[{:keys [profile-id library-id] :as params}]
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [lib (retrieve-library conn library-id)]
(teams/check-read-permissions! conn profile-id (:team-id lib))
(->> (retrieve-images conn library-id)
(mapv #(images/resolve-urls % :path :uri))
(mapv #(images/resolve-urls % :thumb-path :thumb-uri))))))
(->> (retrieve-images conn file-id)
(mapv #(images/resolve-urls % :path :uri))
(mapv #(images/resolve-urls % :thumb-path :thumb-uri)))))
;; (let [lib (retrieve-library conn file-id)]
;; (teams/check-read-permissions! conn profile-id (:team-id lib))
;; (->> (retrieve-images conn file-id)
;; (mapv #(images/resolve-urls % :path :uri))
;; (mapv #(images/resolve-urls % :thumb-path :thumb-uri))))))
(def ^:private sql:images
"select img.*
"select *
from image as img
inner join image_library as lib on (lib.id = img.library_id)
where img.deleted_at is null
and img.library_id = ?
and img.file_id = ?
order by created_at desc")
(defn- retrieve-images
[conn library-id]
(db/exec! conn [sql:images library-id]))
[conn file-id]
(db/exec! conn [sql:images file-id]))
@ -125,9 +127,9 @@
(def ^:private sql:single-image
"select img.*,
lib.team_id as team_id
file.team_id as team_id
from image as img
inner join image_library as lib on (lib.id = img.library_id)
inner join file on (file.id = img.file_id)
where img.deleted_at is null
and img.id = ?
order by created_at desc")

View file

@ -48,10 +48,19 @@
and (ppr.is_admin = true or
ppr.is_owner = true or
ppr.can_edit = true)
union
select p.*,
(select count(*) from file as f
where f.project_id = p.id
and deleted_at is null)
from project as p
where p.team_id = uuid_nil()
and p.deleted_at is null
)
select *
from projects
where team_id = ?
or team_id = uuid_nil()
order by modified_at desc")
(def ^:private sql:project-by-id

View file

@ -134,6 +134,31 @@
}
}
.group-list {
overflow-y: scroll;
}
.group-list-item {
display: flex;
align-items: center;
margin-top: $x-small;
font-size: $fs11;
color: $color-white;
& .color-block {
width: 20px;
height: 20px;
border-radius: 10px;
margin-right: $x-small;
}
& span {
margin-left: $x-small;
color: $color-gray-30;
text-transform: uppercase;
}
}
.context-menu {
position: absolute;
top: 10px;

View file

@ -75,10 +75,9 @@
(s/def ::layout-flags (s/coll-of ::layout-flag))
(def default-layout
#{;; :sitemap
;; :sitemap-pages
;; :layers
:assets
#{:sitemap
:sitemap-pages
:layers
:element-options
:rules
:display-grid
@ -1441,6 +1440,7 @@
(def add-image-from-url dwp/add-image-from-url)
(def upload-image dwp/upload-image)
(def delete-file-image dwp/delete-file-image)
(def fetch-colors dwp/fetch-colors)
(def rename-page dwp/rename-page)
(def delete-page dwp/delete-page)
(def create-empty-page dwp/create-empty-page)

View file

@ -289,7 +289,7 @@
(ptk/reify ::fetch-images
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query :file-images {:file-id file-id})
(->> (rp/query :images {:file-id file-id})
(rx/map images-fetched)))))
(defn images-fetched
@ -300,6 +300,26 @@
(let [images (d/index-by :id images)]
(assoc state :workspace-images images)))))
;; --- Fetch Workspace Colors
(declare colors-fetched)
(defn fetch-colors
[file-id]
(ptk/reify ::fetch-colors
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query :colors {:file-id file-id})
(rx/map colors-fetched)))))
(defn colors-fetched
[colors]
(ptk/reify ::colors-fetched
ptk/UpdateEvent
(update [_ state]
(let [colors (d/index-by :id colors)]
(assoc state :workspace-colors colors)))))
;; --- Upload Image

View file

@ -60,6 +60,9 @@
(def workspace-images
(l/derived :workspace-images st/state))
(def workspace-colors
(l/derived :workspace-colors st/state))
(def workspace-users
(l/derived :workspace-users st/state))

View file

@ -89,10 +89,24 @@
[:div.asset-group
[:div.group-title
(tr "workspace.assets.colors")
[:div.group-button {:on-click add-color} i/plus]]]))
[:span (str "\u00A0(") (count colors) ")"] ;; Unicode 00A0 is non-breaking space
[:div.group-button {:on-click add-color} i/plus]]
[:div.group-list
(for [color (sort-by :name colors)]
[:div.group-list-item {:key (:name color)
:on-context-menu #(println "context")}
[:div.color-block {:style {:background-color (:content color)}}]
(:name color)
(when-not (= (:name color) (:content color))
[:span (:content color)])])]]))
(mf/defc library-toolbox
[{:keys [library-id images initial-open? search-term box-filter] :as props}]
[{:keys [library-id
images
colors
initial-open?
search-term
box-filter] :as props}]
(let [open? (mf/use-state initial-open?)
toggle-open #(swap! open? not)]
[:div.tool-window
@ -107,13 +121,14 @@
(when (or (= box-filter :all) (= box-filter :graphics))
[:& graphics-box {:library-id library-id :images images}])
(when (or (= box-filter :all) (= box-filter :colors))
[:& colors-box {:colors {}}])])]))
[:& colors-box {:colors colors}])])]))
(mf/defc assets-toolbox
[]
(let [team-id (-> refs/workspace-project mf/deref :team-id)
file-id (-> refs/workspace-file mf/deref :id)
file-images (mf/deref refs/workspace-images)
file-colors (mf/deref refs/workspace-colors)
state (mf/use-state {:search-term ""
:box-filter :all})
@ -121,6 +136,9 @@
filtered-images (filter #(matches-search (:name %) (:search-term @state))
(vals file-images))
filtered-colors (filter #(matches-search (:name %) (:search-term @state))
(vals file-colors))
on-search-term-change (fn [event]
(let [value (-> (dom/get-target event)
(dom/get-value))]
@ -135,7 +153,8 @@
(mf/use-effect
(mf/deps file-id)
#(when file-id
(st/emit! (dw/fetch-images file-id))))
(st/emit! (dw/fetch-images file-id))
(st/emit! (dw/fetch-colors file-id))))
[:div.assets-bar
@ -158,6 +177,7 @@
[:& library-toolbox {:library-id file-id
:images filtered-images
:colors filtered-colors
:initial-open? true
:search-term (:search-term @state)
:box-filter (:box-filter @state)}]]))

View file

@ -1,54 +1,58 @@
{:icons
[{:name "Material Design (Action)"
:path "./icons/material-action"
:regex #"^.*_48px\.svg$"}
[
;; Icons
{:name "Material Design (Action)"
:images {:path "./icons/material-action"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Alert)"
:path "./icons/material-alert"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-alert"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Av)"
:path "./icons/material-av"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-av"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Content)"
:path "./icons/material-content"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-content"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Device)"
:path "./icons/material-device"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-device"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Editor)"
:path "./icons/material-editor"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-editor"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (File)"
:path "./icons/material-file"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-file"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Hardware)"
:path "./icons/material-hardware"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-hardware"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Image)"
:path "./icons/material-image"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-image"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Maps)"
:path "./icons/material-maps"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-maps"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Navigation)"
:path "./icons/material-navigation"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-navigation"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Notification)"
:path "./icons/material-notification"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-notification"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Places)"
:path "./icons/material-places"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-places"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Social)"
:path "./icons/material-social"
:regex #"^.*_48px\.svg$"}
:images {:path "./icons/material-social"
:regex #"^.*_48px\.svg$"}}
{:name "Material Design (Toggle)"
:path "./icons/material-toggle"
:regex #"^.*_48px\.svg$"}
]
:images {:path "./icons/material-toggle"
:regex #"^.*_48px\.svg$"}}
:colors
[{:name "Flat design"
:id #uuid "00000000-0000-0000-0000-00000000001"
;; Images
{:name "Unsplash"
:images {:path "./images/unsplash"
:regex #"^.*\.jpg$"}}
;; Colors
{:name "Flat design"
:colors ["turquoise-50" "#e8f8f5"
"turquoise-100" "#d1f2eb"
"turquoise-200" "#a3e4d7"
@ -250,9 +254,7 @@
"asbestos-800" "#515a5a"
"asbestos-900" "#424949"]}
{:name "Material design"
:id #uuid "00000000-0000-0000-0000-000000000020"
:colors ["red-50" "#ffebee"
"red-100" "#ffcdd2"
"red-200" "#ef9a9a"
@ -509,5 +511,4 @@
"blue-grey-900" "#263238"
"white" "#ffffff"
"black" "#000000"]}
]}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB