1 | /* |
2 | This file is part of the KDE project |
3 | |
4 | SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com> |
5 | SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> |
6 | |
7 | SPDX-License-Identifier: LGPL-2.0-or-later |
8 | */ |
9 | |
10 | #ifndef KPLUGINMETADATA_H |
11 | #define KPLUGINMETADATA_H |
12 | |
13 | #include "kcoreaddons_export.h" |
14 | |
15 | #include <QExplicitlySharedDataPointer> |
16 | #include <QJsonObject> |
17 | #include <QMetaType> |
18 | #include <QString> |
19 | #include <QStringList> |
20 | |
21 | #include <functional> |
22 | |
23 | class QPluginLoader; |
24 | class QStaticPlugin; |
25 | class KPluginMetaDataPrivate; |
26 | class KAboutPerson; |
27 | /** |
28 | * @class KPluginMetaData kpluginmetadata.h KPluginMetaData |
29 | * |
30 | * This class allows easily accessing some standardized values from the JSON metadata that |
31 | * can be embedded into Qt plugins. Additional plugin-specific metadata can be retrieved by |
32 | * directly reading from the QJsonObject returned by KPluginMetaData::rawData(). |
33 | * |
34 | * For embedded metadata, you should not specify an id manually. Instead the id will |
35 | * be derived from the file basename. |
36 | * |
37 | * The following keys will be read from an object "KPlugin" inside the metadata JSON: |
38 | * |
39 | * Key | Accessor function | JSON Type |
40 | * -------------------| -------------------- | --------------------- |
41 | * Name | name() | string |
42 | * Description | description() | string |
43 | * Icon | iconName() | string |
44 | * Authors | authors() | object array (KAboutPerson) |
45 | * Category | category() | string |
46 | * License | license() | string |
47 | * Copyright | copyrightText() | string |
48 | * Id | pluginId() | string |
49 | * Version | version() | string |
50 | * Website | website() | string |
51 | * BugReportUrl | bugReportUrl() | string |
52 | * EnabledByDefault | isEnabledByDefault() | bool |
53 | * MimeTypes | mimeTypes() | string array |
54 | * FormFactors | formFactors() | string array |
55 | * Translators | translators() | object array (KAboutPerson) |
56 | * OtherContributors | otherContributors() | object array (KAboutPerson) |
57 | * |
58 | * The Authors, Translators and OtherContributors keys are expected to be |
59 | * list of objects that match the structure expected by KAboutPerson::fromJSON(). |
60 | * |
61 | * An example metadata json file could look like this: |
62 | * @verbatim |
63 | { |
64 | "KPlugin": { |
65 | "Name": "Date and Time", |
66 | "Description": "Date and time by timezone", |
67 | "Icon": "preferences-system-time", |
68 | "Authors": [ { "Name": "Aaron Seigo", "Email": "aseigo@kde.org" } ], |
69 | "Category": "Date and Time", |
70 | "EnabledByDefault": "true", |
71 | "License": "LGPL", |
72 | "Version": "1.0", |
73 | "Website": "https://plasma.kde.org/" |
74 | } |
75 | } |
76 | @endverbatim |
77 | * |
78 | * @sa KAboutPerson::fromJSON() |
79 | * @since 5.1 |
80 | */ |
81 | class KCOREADDONS_EXPORT KPluginMetaData |
82 | { |
83 | Q_GADGET |
84 | Q_PROPERTY(bool isValid READ isValid CONSTANT) |
85 | Q_PROPERTY(bool isHidden READ isHidden CONSTANT) |
86 | Q_PROPERTY(QString fileName READ fileName CONSTANT) |
87 | Q_PROPERTY(QJsonObject rawData READ rawData CONSTANT) |
88 | Q_PROPERTY(QString name READ name CONSTANT) |
89 | Q_PROPERTY(QString description READ description CONSTANT) |
90 | Q_PROPERTY(QList<KAboutPerson> authors READ authors CONSTANT) |
91 | Q_PROPERTY(QList<KAboutPerson> translators READ translators CONSTANT) |
92 | Q_PROPERTY(QList<KAboutPerson> otherContributors READ otherContributors CONSTANT) |
93 | Q_PROPERTY(QString category READ category CONSTANT) |
94 | Q_PROPERTY(QString iconName READ iconName CONSTANT) |
95 | Q_PROPERTY(QString license READ license CONSTANT) |
96 | Q_PROPERTY(QString licenseText READ licenseText CONSTANT) |
97 | Q_PROPERTY(QString copyrightText READ copyrightText CONSTANT) |
98 | Q_PROPERTY(QString pluginId READ pluginId CONSTANT) |
99 | Q_PROPERTY(QString version READ version CONSTANT) |
100 | Q_PROPERTY(QString website READ website CONSTANT) |
101 | Q_PROPERTY(QString bugReportUrl READ bugReportUrl CONSTANT) |
102 | Q_PROPERTY(QStringList mimeTypes READ mimeTypes CONSTANT) |
103 | Q_PROPERTY(QStringList formFactors READ formFactors CONSTANT) |
104 | Q_PROPERTY(bool isEnabledByDefault READ isEnabledByDefault CONSTANT) |
105 | |
106 | public: |
107 | /** |
108 | * Options for creating a KPluginMetaData object. |
109 | * @since 5.91 |
110 | */ |
111 | enum KPluginMetaDataOption { |
112 | AllowEmptyMetaData = 1, ///< Plugins with empty metaData are considered valid |
113 | /** |
114 | * If KCoreAddons should keep metadata in cache. This makes querying the namespace again faster. Consider using this if you need revalidation of plugins |
115 | * @since 6.0 |
116 | */ |
117 | CacheMetaData = 2, |
118 | }; |
119 | Q_DECLARE_FLAGS(KPluginMetaDataOptions, KPluginMetaDataOption) |
120 | Q_FLAG(KPluginMetaDataOption) |
121 | |
122 | /** Creates an invalid KPluginMetaData instance */ |
123 | KPluginMetaData(); |
124 | |
125 | /** |
126 | * Reads the plugin metadata from a QPluginLoader instance. You must call QPluginLoader::setFileName() |
127 | * or use the appropriate constructor on @p loader before calling this. |
128 | * @param option Added in 6.0, see enum docs |
129 | */ |
130 | KPluginMetaData(const QPluginLoader &loader, KPluginMetaDataOptions options = {}); |
131 | |
132 | /** |
133 | * Reads the plugin metadata from a plugin which can be loaded from @p file. |
134 | * |
135 | * Platform-specific library suffixes may be omitted since @p file will be resolved |
136 | * using the same logic as QPluginLoader. |
137 | * |
138 | * @see QPluginLoader::setFileName() |
139 | */ |
140 | KPluginMetaData(const QString &pluginFile, KPluginMetaDataOptions options = {}); |
141 | |
142 | /** |
143 | * Creates a KPluginMetaData from a QJsonObject holding the metadata and a file name |
144 | * This can be used if the data is not retrieved from a Qt C++ plugin library but from some |
145 | * other source. |
146 | * |
147 | * @param metaData the JSON metadata to use for this object |
148 | * @param pluginFile the file that the plugin can be loaded from |
149 | * |
150 | * @since 6.0 |
151 | */ |
152 | KPluginMetaData(const QJsonObject &metaData, const QString &fileName); |
153 | |
154 | /** |
155 | * Copy contructor |
156 | */ |
157 | KPluginMetaData(const KPluginMetaData &); |
158 | /** |
159 | * Copy assignment |
160 | */ |
161 | KPluginMetaData &operator=(const KPluginMetaData &); |
162 | /** |
163 | * Destructor |
164 | */ |
165 | ~KPluginMetaData(); |
166 | |
167 | /** |
168 | * Load a KPluginMetaData instance from a .json file. Unlike the constructor with a single file argument, |
169 | * this ensure that only JSON format plugins are loaded and any other type is rejected. |
170 | * |
171 | * @param jsonFile the .json file to load |
172 | * @since 5.91 |
173 | */ |
174 | static KPluginMetaData fromJsonFile(const QString &jsonFile); |
175 | |
176 | /** |
177 | * @param directory The directory to search for plugins. If a relative path is given for @p directory, |
178 | * all entries of QCoreApplication::libraryPaths() will be checked with @p directory appended as a |
179 | * subdirectory. If an absolute path is given only that directory will be searched. |
180 | * @note Check if the returned KPluginMetaData is valid before continuing to use it. |
181 | * |
182 | * @param pluginId The Id of the plugin. The id should be the same as the filename, see KPluginMetaData::pluginId() |
183 | * @param option Added in 6.0, see enum docs |
184 | * @since 5.84 |
185 | */ |
186 | static KPluginMetaData findPluginById(const QString &directory, const QString &pluginId, KPluginMetaDataOptions options = {}); |
187 | |
188 | /** |
189 | * Find all plugins inside @p directory. Only plugins which have JSON metadata will be considered. |
190 | * |
191 | * @param directory The directory to search for plugins. If a relative path is given for @p directory, |
192 | * all entries of QCoreApplication::libraryPaths() will be checked with @p directory appended as a |
193 | * subdirectory. If an absolute path is given only that directory will be searched. |
194 | * |
195 | * @param filter a callback function that returns @c true if the found plugin should be loaded |
196 | * and @c false if it should be skipped. If this argument is omitted all plugins will be loaded |
197 | * @param option Weather or not allow plugins with empty metadata to be considered valid |
198 | * |
199 | * @return all plugins found in @p directory that fulfil the constraints of @p filter |
200 | * @since 5.86 |
201 | */ |
202 | static QList<KPluginMetaData> |
203 | findPlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter = {}, KPluginMetaDataOptions options = {}); |
204 | |
205 | /** |
206 | * @return whether this object holds valid information about a plugin. |
207 | * If this is @c true pluginId() will return a non-empty string. |
208 | */ |
209 | bool isValid() const; |
210 | |
211 | /** |
212 | * @return whether this object should be hidden |
213 | * |
214 | * @since 5.8 |
215 | */ |
216 | bool isHidden() const; |
217 | |
218 | /** |
219 | * @return the path to the plugin. |
220 | * When the KPluginMetaData(QJsonObject, QString) constructor is used, the string is not modified. |
221 | * Otherwise, the path is resolved using QPluginLoader. |
222 | * For static plugins the fileName is the namespace and pluginId concatenated |
223 | * |
224 | * @note It is not guaranteed that this is a valid path to a shared library (i.e. loadable |
225 | * by QPluginLoader) since the metadata could also refer to a non-C++ plugin. |
226 | */ |
227 | QString fileName() const; |
228 | |
229 | /** |
230 | * @return the full metadata stored inside the plugin file. |
231 | */ |
232 | QJsonObject rawData() const; |
233 | |
234 | /** |
235 | * @return the user visible name of the plugin. |
236 | */ |
237 | QString name() const; |
238 | |
239 | /** |
240 | * @return a short description of the plugin. |
241 | */ |
242 | QString description() const; |
243 | |
244 | /** |
245 | * @return the author(s) of this plugin. |
246 | */ |
247 | QList<KAboutPerson> authors() const; |
248 | |
249 | /** |
250 | * @return the translator(s) of this plugin. |
251 | * |
252 | * @since 5.18 |
253 | */ |
254 | QList<KAboutPerson> translators() const; |
255 | |
256 | /** |
257 | * @return a list of people that contributed to this plugin (other than the authors and translators). |
258 | * |
259 | * @since 5.18 |
260 | */ |
261 | QList<KAboutPerson> otherContributors() const; |
262 | |
263 | /** |
264 | * @return the categories of this plugin (e.g. "playlist/skin"). |
265 | */ |
266 | QString category() const; |
267 | |
268 | /** |
269 | * @return the icon name for this plugin |
270 | * @see QIcon::fromTheme() |
271 | */ |
272 | QString iconName() const; |
273 | |
274 | /** |
275 | * @return the short license identifier (e.g. LGPL). |
276 | * @see KAboutLicense::byKeyword() for retrieving the full license information |
277 | */ |
278 | QString license() const; |
279 | |
280 | /** |
281 | * @return the text of the license, equivalent to KAboutLicense::byKeyword(license()).text() |
282 | * @since 5.73 |
283 | */ |
284 | QString licenseText() const; |
285 | |
286 | /** |
287 | * @return a short copyright statement |
288 | * |
289 | * @since 5.18 |
290 | */ |
291 | QString copyrightText() const; |
292 | |
293 | /** |
294 | * @return the unique identifier within the namespace of the plugin |
295 | * |
296 | * For C++ plugins, this ID is derived from the filename. |
297 | * It should not be set in the metadata explicitly. |
298 | * |
299 | * When using @ref KPluginMetaData::fromJsonFile or @ref KPluginMetaData(QJsonObject, QString), |
300 | * the "Id" of the "KPlugin" object will be used. If unset, it will be derived |
301 | * from the filename. |
302 | */ |
303 | QString pluginId() const; |
304 | |
305 | /** |
306 | * @return the version of the plugin. |
307 | */ |
308 | QString version() const; |
309 | |
310 | /** |
311 | * @return the website of the plugin. |
312 | */ |
313 | QString website() const; |
314 | |
315 | /** |
316 | * @return the website where people can report a bug found in this plugin |
317 | * @since 5.99 |
318 | */ |
319 | QString bugReportUrl() const; |
320 | |
321 | /** |
322 | * @return a list of MIME types this plugin can handle (e.g. "application/pdf", "image/png", etc.) |
323 | * @since 5.16 |
324 | */ |
325 | QStringList mimeTypes() const; |
326 | |
327 | /** |
328 | * @return true if this plugin can handle the given mimetype |
329 | * This is more accurate than mimeTypes().contains(mimeType) because it also |
330 | * takes MIME type inheritance into account. |
331 | * @since 5.66 |
332 | */ |
333 | bool supportsMimeType(const QString &mimeType) const; |
334 | |
335 | /** |
336 | * @return A string list of formfactors this plugin is useful for, e.g. desktop, handset or mediacenter. |
337 | * The keys for this are not formally defined, though the above-mentioned values should be used when applicable. |
338 | * |
339 | * @since 5.12 |
340 | */ |
341 | QStringList formFactors() const; |
342 | |
343 | /** |
344 | * @return whether the plugin should be enabled by default. |
345 | * This is only a recommendation, applications can ignore this value if they want to. |
346 | */ |
347 | bool isEnabledByDefault() const; |
348 | |
349 | /** |
350 | * Returns @c true if the plugin is enabled in @p config, otherwise returns isEnabledByDefault(). |
351 | * This can be used in conjunction with KPluginWidget. |
352 | * |
353 | * The @p config param should be a KConfigGroup object, because KCoreAddons can not depend |
354 | * on KConfig directly, this parameter is a template. |
355 | * @param config KConfigGroup where the enabled state is stored |
356 | * @since 5.89 |
357 | */ |
358 | template<typename T> |
359 | bool isEnabled(const T &config) const |
360 | { |
361 | Q_ASSERT(config.isValid()); |
362 | return config.readEntry(pluginId() + QLatin1String("Enabled" ), isEnabledByDefault()); |
363 | } |
364 | |
365 | /** |
366 | * @return the string value for @p key from the metadata or @p defaultValue if the key does not exist |
367 | * |
368 | * if QString is not the correct type for @p key you should use the other overloads or @ref KPluginMetaData::rawData |
369 | */ |
370 | QString value(const QString &key, const QString &defaultValue = QString()) const; |
371 | QString value(const QString &key, const char *ch) const = delete; |
372 | |
373 | /** |
374 | * @overload |
375 | * @since 5.88 |
376 | */ |
377 | bool value(const QString &key, bool defaultValue) const; |
378 | |
379 | /** |
380 | * @overload |
381 | * @since 5.88 |
382 | */ |
383 | int value(const QString &key, int defaultValue) const; |
384 | |
385 | /** @return the value for @p key from the metadata or @p defaultValue if the key does not exist. |
386 | * If the type of @p key is string, a list containing just that string will be returned. |
387 | * If the type is array, the list will contain one entry for each array member. |
388 | * @overload |
389 | * @since 5.88 |
390 | */ |
391 | QStringList value(const QString &key, const QStringList &defaultValue) const; |
392 | |
393 | /** |
394 | * @return @c true if this object is equal to @p other, otherwise @c false |
395 | */ |
396 | bool operator==(const KPluginMetaData &other) const; |
397 | |
398 | /** |
399 | * @return @c true if this object is not equal to @p other, otherwise @c false. |
400 | */ |
401 | inline bool operator!=(const KPluginMetaData &other) const |
402 | { |
403 | return !(*this == other); |
404 | } |
405 | |
406 | /** |
407 | * @note for loading plugin the plugin independently of it being static/dynamic |
408 | * use @ref KPluginFactory::loadFactory or @ref KPluginFactory::instantiatePlugin. |
409 | * @return true if the instance represents a static plugin |
410 | * @since 5.89 |
411 | */ |
412 | bool isStaticPlugin() const; |
413 | |
414 | private: |
415 | KCOREADDONS_NO_EXPORT QStaticPlugin staticPlugin() const; |
416 | KCOREADDONS_NO_EXPORT QString requestedFileName() const; |
417 | |
418 | QExplicitlySharedDataPointer<KPluginMetaDataPrivate> d; |
419 | friend class KPluginFactory; |
420 | friend class KPluginMetaDataPrivate; |
421 | }; |
422 | |
423 | inline size_t qHash(const KPluginMetaData &md, size_t seed) |
424 | { |
425 | return qHash(key: md.pluginId(), seed); |
426 | } |
427 | |
428 | /// @since 6.0 |
429 | KCOREADDONS_EXPORT QDebug operator<<(QDebug debug, const KPluginMetaData &metaData); |
430 | |
431 | Q_DECLARE_TYPEINFO(KPluginMetaData, Q_RELOCATABLE_TYPE); |
432 | Q_DECLARE_OPERATORS_FOR_FLAGS(KPluginMetaData::KPluginMetaDataOptions) |
433 | |
434 | #endif // KPLUGINMETADATA_H |
435 | |