diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index ea0fc54f41..db97bfd58b 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -30,7 +30,7 @@ [app.main.ui.hooks.resize :as r] [app.main.ui.icons :as i] [app.main.ui.workspace.plugins :as uwp] - [app.plugins :as plugins] + [app.plugins.register :as preg] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -609,7 +609,7 @@ ::mf/wrap [mf/memo]} [{:keys [open-plugins on-close]}] (when (features/active-feature? @st/state "plugins/runtime") - (let [plugins @plugins/pluginsdb] + (let [plugins (preg/plugins-list)] [:& dropdown-menu {:show true :list-class (stl/css-case :sub-menu true :plugins true) :on-close on-close} diff --git a/frontend/src/app/main/ui/workspace/plugins.cljs b/frontend/src/app/main/ui/workspace/plugins.cljs index bceeaf9c17..e337455351 100644 --- a/frontend/src/app/main/ui/workspace/plugins.cljs +++ b/frontend/src/app/main/ui/workspace/plugins.cljs @@ -16,7 +16,7 @@ [app.main.ui.components.search-bar :refer [search-bar]] [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.icons :as i] - [app.plugins :as plugins] + [app.plugins.register :as preg] [app.util.avatars :as avatars] [app.util.dom :as dom] [app.util.http :as http] @@ -80,7 +80,7 @@ ::mf/register-as :plugin-management} [] - (let [plugins-state* (mf/use-state @plugins/pluginsdb) + (let [plugins-state* (mf/use-state #(preg/plugins-list)) plugins-state @plugins-state* plugin-url* (mf/use-state "") @@ -117,14 +117,14 @@ (rx/subs! (fn [body] (reset! fetching-manifest? false) - (let [plugin (plugins/parser-manifest plugin-url body)] + (let [plugin (preg/parse-manifest plugin-url body)] (st/emit! (ptk/event ::ev/event {::ev/name "install-plugin" :name (:name plugin) :url plugin-url})) (modal/show! :plugin-permissions {:plugin plugin :on-accept #(do - (plugins/install-plugin! plugin) + (preg/install-plugin! plugin) (modal/show! :plugin-management {}))}) (reset! input-status* :success) (reset! plugin-url* ""))) @@ -146,12 +146,13 @@ (mf/use-callback (mf/deps plugins-state) (fn [plugin-index] - (let [plugin (nth @plugins/pluginsdb plugin-index)] + (let [plugins-list (preg/plugins-list) + plugin (nth plugins-list plugin-index)] (st/emit! (ptk/event ::ev/event {::ev/name "remove-plugin" :name (:name plugin) :host (:host plugin)})) - (plugins/remove-plugin! plugin) - (reset! plugins-state* @plugins/pluginsdb))))] + (preg/remove-plugin! plugin) + (reset! plugins-state* (preg/plugins-list)))))] [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :plugin-management)} diff --git a/frontend/src/app/plugins.cljs b/frontend/src/app/plugins.cljs index c66260e522..d3a4872a73 100644 --- a/frontend/src/app/plugins.cljs +++ b/frontend/src/app/plugins.cljs @@ -7,7 +7,6 @@ (ns app.plugins "RPC for plugins runtime." (:require - [app.common.uuid :as uuid] [app.main.features :as features] [app.main.store :as st] [app.plugins.api :as api] @@ -18,10 +17,6 @@ [beicon.v2.core :as rx] [potok.v2.core :as ptk])) -(def pluginsdb register/pluginsdb) -(def install-plugin! register/install-plugin!) -(def remove-plugin! register/remove-plugin!) - (defn init-plugins-runtime! [] (when-let [init-runtime (obj/get global "initPluginsRuntime")] @@ -41,28 +36,3 @@ (rx/tap init-plugins-runtime!) (rx/ignore))))) -(defn parser-manifest - [plugin-url ^js manifest] - (let [name (obj/get manifest "name") - desc (obj/get manifest "description") - code (obj/get manifest "code") - icon (obj/get manifest "icon") - - permissions (into #{} (obj/get manifest "permissions" [])) - permissions - (cond-> permissions - (contains? permissions "content:write") - (conj "content:read") - - (contains? permissions "library:write") - (conj "content:write")) - - origin (obj/get (js/URL. plugin-url) "origin") - plugin-id (str (uuid/next))] - {:plugin-id plugin-id - :name name - :description desc - :host origin - :code code - :icon icon - :permissions (->> permissions (mapv str))})) diff --git a/frontend/src/app/plugins/register.cljs b/frontend/src/app/plugins/register.cljs index 7ba2bdcb92..c7e6190c8c 100644 --- a/frontend/src/app/plugins/register.cljs +++ b/frontend/src/app/plugins/register.cljs @@ -7,54 +7,126 @@ (ns app.plugins.register "RPC for plugins runtime." (:require - [app.common.data :as d])) + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.uuid :as uuid] + [app.util.object :as obj])) -;; TODO: Remove clj->js and parse into a better data structure for accessing the permissions +;; Stores the installed plugins information +(defonce ^:private registry (atom {})) -(def pluginsdb (atom nil)) +(defn plugins-list + "Retrieves the plugin data as an ordered list of plugin elements" + [] + (->> (:ids @registry) + (mapv #(dm/get-in @registry [:data %])))) + +(defn parse-manifest + "Read the manifest.json defined by the plugins definition and transforms it into an + object that will be stored in the register." + [plugin-url ^js manifest] + (let [name (obj/get manifest "name") + desc (obj/get manifest "description") + code (obj/get manifest "code") + icon (obj/get manifest "icon") + + permissions (into #{} (obj/get manifest "permissions" [])) + permissions + (cond-> permissions + (contains? permissions "content:write") + (conj "content:read") + + (contains? permissions "library:write") + (conj "content:write")) + + origin (obj/get (js/URL. plugin-url) "origin") + + prev-plugin + (->> (:data @registry) + (vals) + (d/seek (fn [plugin] + (and (= name (:name plugin)) + (= origin (:host plugin)))))) + + plugin-id (d/nilv (:plugin-id prev-plugin) (str (uuid/next)))] + {:plugin-id plugin-id + :name name + :description desc + :host origin + :code code + :icon icon + :permissions (into #{} (map str) permissions)})) + +(defn format-plugin-data + "Format into a JS object the plugin data. This will be used to be stored in the local storage." + [{:keys [plugin-id name description host code icon permissions]}] + #js {:plugin-id plugin-id + :name name + :description description + :host host + :code code + :icon icon + :permissions (apply array permissions)}) + +(defn parse-plugin-data + "Parsers the JS plugin data into a CLJS data structure. This will be used primarily when the local storage + data is retrieved" + [^js data] + {:plugin-id (obj/get data "plugin-id") + :name (obj/get data "name") + :description (obj/get data "description") + :host (obj/get data "host") + :code (obj/get data "code") + :icon (obj/get data "icon") + :permissions (into #{} (obj/get data "permissions"))}) (defn load-from-store [] (let [ls (.-localStorage js/window) plugins-val (.getItem ls "plugins")] (when plugins-val - (let [plugins-js (.parse js/JSON plugins-val)] - (js->clj plugins-js {:keywordize-keys true}))))) + (let [stored (->> (.parse js/JSON plugins-val) + (map parse-plugin-data))] + (reset! registry + {:ids (->> stored (map :plugin-id)) + :data (d/index-by :plugin-id stored)}))))) (defn save-to-store - [plugins] - (let [ls (.-localStorage js/window) - plugins-js (clj->js plugins) - plugins-val (.stringify js/JSON plugins-js)] - (.setItem ls "plugins" plugins-val))) + [] + (->> (:ids @registry) + (map #(dm/get-in @registry [:data %])) + (map format-plugin-data) + (apply array) + (.stringify js/JSON) + (.setItem (.-localStorage js/window) "plugins"))) (defn init [] - (reset! pluginsdb (load-from-store))) + (load-from-store)) (defn install-plugin! [plugin] - (let [plugins (as-> @pluginsdb $ - (remove (fn [{:keys [name host]}] - (and (= name (:name plugin)) - (= host (:host plugin)))) $) - (conj $ plugin) - (vec $))] - (reset! pluginsdb plugins) - (save-to-store plugins))) + (letfn [(update-ids [ids] + (conj + (->> ids (remove #(= % (:plugin-id plugin)))) + (:plugin-id plugin)))] + (swap! registry #(-> % + (update :ids update-ids) + (update :data assoc (:plugin-id plugin) plugin))) + (save-to-store))) (defn remove-plugin! [{:keys [plugin-id]}] - (let [plugins - (into [] - (keep (fn [plugin] - (when (not= plugin-id (:plugin-id plugin)) plugin))) - @pluginsdb)] - (reset! pluginsdb plugins) - (save-to-store plugins))) + (letfn [(update-ids [ids] + (->> ids + (remove #(= % plugin-id))))] + (swap! registry #(-> % + (update :ids update-ids) + (update :data dissoc plugin-id))) + (save-to-store))) (defn check-permission [plugin-id permission] (or (= plugin-id "TEST") - (let [{:keys [permissions]} (->> @pluginsdb (d/seek #(= (:plugin-id %) plugin-id)))] - (->> permissions (d/seek #(= % permission)))))) + (let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])] + (contains? permissions permission))))