Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2024 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "extensionsconfiguration.h"
23 :
24 : #include "global/settings.h"
25 : #include "global/serialization/json.h"
26 : #include "global/io/file.h"
27 : #include "global/io/dir.h"
28 : #include "multiwindows/resourcelockguard.h"
29 : #include "legacy/extpluginsloader.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::extensions;
35 :
36 : static const muse::Settings::Key USER_PLUGINS_PATH("plugins", "application/paths/myPlugins");
37 :
38 : static const std::string EXTENSIONS_RESOURCE_NAME("EXTENSIONS");
39 :
40 0 : void ExtensionsConfiguration::init()
41 : {
42 0 : settings()->setDefaultValue(USER_PLUGINS_PATH, Val(globalConfiguration()->userDataPath() + "/Plugins"));
43 0 : settings()->valueChanged(USER_PLUGINS_PATH).onReceive(nullptr, [this](const Val& val) {
44 0 : m_pluginsUserPathChanged.send(val.toString());
45 0 : });
46 :
47 0 : if (!pluginsUserPath().empty()) {
48 0 : io::Dir::mkpath(pluginsUserPath());
49 : }
50 :
51 0 : io::Dir::mkpath(userPath());
52 0 : }
53 :
54 0 : io::path_t ExtensionsConfiguration::defaultPath() const
55 : {
56 0 : return globalConfiguration()->appDataPath() + "/extensions";
57 : }
58 :
59 0 : io::path_t ExtensionsConfiguration::userPath() const
60 : {
61 0 : return globalConfiguration()->userAppDataPath() + "/extensions";
62 : }
63 :
64 0 : io::path_t ExtensionsConfiguration::pluginsDefaultPath() const
65 : {
66 0 : return globalConfiguration()->appDataPath() + "/plugins";
67 : }
68 :
69 0 : io::path_t ExtensionsConfiguration::pluginsUserPath() const
70 : {
71 0 : return settings()->value(USER_PLUGINS_PATH).toPath();
72 : }
73 :
74 0 : void ExtensionsConfiguration::setUserPluginsPath(const io::path_t& path)
75 : {
76 0 : settings()->setSharedValue(USER_PLUGINS_PATH, Val(path));
77 0 : }
78 :
79 0 : async::Channel<io::path_t> ExtensionsConfiguration::pluginsUserPathChanged() const
80 : {
81 0 : return m_pluginsUserPathChanged;
82 : }
83 :
84 0 : Ret ExtensionsConfiguration::setExtensionConfigs(const std::map<Uri, ExtensionConfig>& configs)
85 : {
86 0 : JsonArray arr;
87 0 : for (const auto& p : configs) {
88 0 : JsonObject obj;
89 0 : obj["uri"] = p.first.toString();
90 :
91 0 : const ExtensionConfig& c = p.second;
92 0 : obj["enabled"] = c.enabled;
93 :
94 0 : arr.append(obj);
95 0 : }
96 :
97 0 : Ret ret;
98 0 : ByteArray data = JsonDocument(arr).toJson();
99 : {
100 0 : mi::WriteResourceLockGuard lock_guard(multiwindowsProvider.get(), EXTENSIONS_RESOURCE_NAME);
101 0 : ret = io::File::writeFile(userPath() + "/config.json", data);
102 0 : }
103 :
104 0 : if (!ret) {
105 0 : LOGE() << "failed write config data, err: " << ret.toString();
106 : }
107 :
108 0 : return ret;
109 0 : }
110 :
111 0 : std::map<muse::Uri, ExtensionConfig> ExtensionsConfiguration::extensionConfigs() const
112 : {
113 0 : const io::path_t configPath = userPath() + "/config.json";
114 0 : const io::path_t oldPluginsConfigPath = globalConfiguration()->userAppDataPath() + "/plugins/plugins.json";
115 :
116 : //! NOTE Load current config
117 0 : if (io::File::exists(configPath)) {
118 0 : ByteArray data;
119 : {
120 0 : mi::ReadResourceLockGuard lock_guard(multiwindowsProvider.get(), EXTENSIONS_RESOURCE_NAME);
121 0 : Ret ret = io::File::readFile(configPath, data);
122 0 : if (!ret) {
123 0 : LOGE() << "failed read config data, err: " << ret.toString() << ", file: " << configPath;
124 0 : return {};
125 : }
126 0 : }
127 :
128 0 : std::string err;
129 0 : JsonDocument doc = JsonDocument::fromJson(data, &err);
130 0 : if (!err.empty()) {
131 0 : LOGE() << "failed parse json, err: " << err;
132 0 : return {};
133 : }
134 :
135 0 : if (!doc.isArray()) {
136 0 : LOGE() << "bad format of config file";
137 0 : return {};
138 : }
139 :
140 0 : std::map<Uri, ExtensionConfig> result;
141 0 : JsonArray arr = doc.rootArray();
142 0 : for (size_t i = 0; i < arr.size(); ++i) {
143 0 : JsonObject obj = arr.at(i).toObject();
144 :
145 0 : Uri uri = Uri(obj.value("uri").toStdString());
146 :
147 0 : ExtensionConfig c;
148 0 : c.enabled = obj.value("enabled").toBool();
149 :
150 0 : result.insert({ uri, c });
151 0 : }
152 :
153 0 : return result;
154 0 : }
155 : //! NOTE Load old plugins config
156 : else {
157 0 : ByteArray data;
158 :
159 : {
160 0 : mi::ReadResourceLockGuard lock_guard(multiwindowsProvider.get(), EXTENSIONS_RESOURCE_NAME);
161 0 : Ret ret = io::File::readFile(oldPluginsConfigPath, data);
162 0 : if (!ret) {
163 0 : LOGE() << "failed read config data, err: " << ret.toString() << ", file: " << oldPluginsConfigPath;
164 0 : return {};
165 : }
166 0 : }
167 :
168 0 : std::string err;
169 0 : JsonDocument doc = JsonDocument::fromJson(data, &err);
170 0 : if (!err.empty()) {
171 0 : LOGE() << "failed parse json, err: " << err;
172 0 : return {};
173 : }
174 :
175 0 : if (!doc.isArray()) {
176 0 : LOGE() << "bad format of config file";
177 0 : return {};
178 : }
179 :
180 0 : std::map<std::string /*codeKey*/, Uri> uris;
181 : {
182 : legacy::ExtPluginsLoader pluginsLoader;
183 0 : uris = pluginsLoader.loadCodekeyUriMap(pluginsDefaultPath(), pluginsUserPath());
184 : }
185 :
186 0 : std::map<Uri, ExtensionConfig> result;
187 0 : JsonArray arr = doc.rootArray();
188 0 : for (size_t i = 0; i < arr.size(); ++i) {
189 0 : JsonObject obj = arr.at(i).toObject();
190 :
191 0 : std::string codeKey = obj.value("codeKey").toStdString();
192 0 : Uri uri = muse::value(uris, codeKey);
193 0 : if (!uri.isValid()) {
194 0 : LOGW() << "not found plugin with codeKey: " << codeKey;
195 0 : continue;
196 : }
197 :
198 0 : ExtensionConfig c;
199 0 : c.enabled = obj.value("enabled").toBool();
200 :
201 0 : result.insert({ uri, c });
202 0 : }
203 :
204 0 : return result;
205 0 : }
206 0 : }
|