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 | struct QStaticPlugin; |
25 | class KPluginMetaDataPrivate; |
26 | class KAboutPerson; |
27 | /*! |
28 | \class KPluginMetaData |
29 | \inmodule KCoreAddons |
30 | |
31 | \brief This class allows easily accessing some standardized values from the JSON metadata that |
32 | can be embedded into Qt plugins. |
33 | |
34 | Additional plugin-specific metadata can be retrieved by |
35 | directly reading from the QJsonObject returned by KPluginMetaData::rawData. |
36 | |
37 | For embedded metadata, you should not specify an id manually. Instead the id will |
38 | be derived from the file basename. |
39 | |
40 | The following keys will be read from an object "KPlugin" inside the metadata JSON: |
41 | |
42 | \table |
43 | \header |
44 | \li Key |
45 | \li Accessor function |
46 | \li JSON Type |
47 | \row |
48 | \li Name |
49 | \li name() |
50 | \li string |
51 | \row |
52 | \li Description |
53 | \li description() |
54 | \li string |
55 | \row |
56 | \li Icon |
57 | \li iconName() |
58 | \li string |
59 | \row |
60 | \li Authors |
61 | \li authors() |
62 | \li object array (KAboutPerson) |
63 | \row |
64 | \li Category |
65 | \li category() |
66 | \li string |
67 | \row |
68 | \li License |
69 | \li license() |
70 | \li string |
71 | \row |
72 | \li Copyright |
73 | \li copyrightText() |
74 | \li string |
75 | \row |
76 | \li Id |
77 | \li pluginId() |
78 | \li string |
79 | \row |
80 | \li Version |
81 | \li version() |
82 | \li string |
83 | \row |
84 | \li Website |
85 | \li website() |
86 | \li string |
87 | \row |
88 | \li BugReportUrl |
89 | \li bugReportUrl() |
90 | \li string |
91 | \row |
92 | \li EnabledByDefault |
93 | \li isEnabledByDefault() |
94 | \li bool |
95 | \row |
96 | \li MimeTypes |
97 | \li mimeTypes() |
98 | \li string array |
99 | \row |
100 | \li FormFactors |
101 | \li formFactors() |
102 | \li string array |
103 | \row |
104 | \li Translators |
105 | \li translators() |
106 | \li object array (KAboutPerson) |
107 | \row |
108 | \li OtherContributors |
109 | \li otherContributors() |
110 | \li object array (KAboutPerson) |
111 | \endtable |
112 | |
113 | The Authors, Translators and OtherContributors keys are expected to be |
114 | list of objects that match the structure expected by KAboutPerson::fromJSON. |
115 | |
116 | An example metadata json file could look like this: |
117 | \badcode |
118 | { |
119 | "KPlugin": { |
120 | "Name": "Date and Time", |
121 | "Description": "Date and time by timezone", |
122 | "Icon": "preferences-system-time", |
123 | "Authors": [ { "Name": "Aaron Seigo", "Email": "aseigo@kde.org" } ], |
124 | "Category": "Date and Time", |
125 | "EnabledByDefault": "true", |
126 | "License": "LGPL", |
127 | "Version": "1.0", |
128 | "Website": "https://plasma.kde.org/" |
129 | } |
130 | } |
131 | \endcode |
132 | |
133 | \sa KAboutPerson::fromJSON() |
134 | \since 5.1 |
135 | */ |
136 | class KCOREADDONS_EXPORT KPluginMetaData |
137 | { |
138 | Q_GADGET |
139 | |
140 | /*! |
141 | * \property KPluginMetaData::isValid |
142 | */ |
143 | Q_PROPERTY(bool isValid READ isValid CONSTANT) |
144 | |
145 | /*! |
146 | * \property KPluginMetaData::isHidden |
147 | */ |
148 | Q_PROPERTY(bool isHidden READ isHidden CONSTANT) |
149 | |
150 | /*! |
151 | * \property KPluginMetaData::fileName |
152 | */ |
153 | Q_PROPERTY(QString fileName READ fileName CONSTANT) |
154 | |
155 | /*! |
156 | * \property KPluginMetaData::rawData |
157 | */ |
158 | Q_PROPERTY(QJsonObject rawData READ rawData CONSTANT) |
159 | |
160 | /*! |
161 | * \property KPluginMetaData::name |
162 | */ |
163 | Q_PROPERTY(QString name READ name CONSTANT) |
164 | |
165 | /*! |
166 | * \property KPluginMetaData::description |
167 | */ |
168 | Q_PROPERTY(QString description READ description CONSTANT) |
169 | |
170 | /*! |
171 | * \property KPluginMetaData::authors |
172 | */ |
173 | Q_PROPERTY(QList<KAboutPerson> authors READ authors CONSTANT) |
174 | |
175 | /*! |
176 | * \property KPluginMetaData::translators |
177 | */ |
178 | Q_PROPERTY(QList<KAboutPerson> translators READ translators CONSTANT) |
179 | |
180 | /*! |
181 | * \property KPluginMetaData::otherContributors |
182 | */ |
183 | Q_PROPERTY(QList<KAboutPerson> otherContributors READ otherContributors CONSTANT) |
184 | |
185 | /*! |
186 | * \property KPluginMetaData::category |
187 | */ |
188 | Q_PROPERTY(QString category READ category CONSTANT) |
189 | |
190 | /*! |
191 | * \property KPluginMetaData::iconName |
192 | */ |
193 | Q_PROPERTY(QString iconName READ iconName CONSTANT) |
194 | |
195 | /*! |
196 | * \property KPluginMetaData::license |
197 | */ |
198 | Q_PROPERTY(QString license READ license CONSTANT) |
199 | |
200 | /*! |
201 | * \property KPluginMetaData::licenseText |
202 | */ |
203 | Q_PROPERTY(QString licenseText READ licenseText CONSTANT) |
204 | |
205 | /*! |
206 | * \property KPluginMetaData::copyrightText |
207 | */ |
208 | Q_PROPERTY(QString copyrightText READ copyrightText CONSTANT) |
209 | |
210 | /*! |
211 | * \property KPluginMetaData::pluginId |
212 | */ |
213 | Q_PROPERTY(QString pluginId READ pluginId CONSTANT) |
214 | |
215 | /*! |
216 | * \property KPluginMetaData::version |
217 | */ |
218 | Q_PROPERTY(QString version READ version CONSTANT) |
219 | |
220 | /*! |
221 | * \property KPluginMetaData::website |
222 | */ |
223 | Q_PROPERTY(QString website READ website CONSTANT) |
224 | |
225 | /*! |
226 | * \property KPluginMetaData::bugReportUrl |
227 | */ |
228 | Q_PROPERTY(QString bugReportUrl READ bugReportUrl CONSTANT) |
229 | |
230 | /*! |
231 | * \property KPluginMetaData::mimeTypes |
232 | */ |
233 | Q_PROPERTY(QStringList mimeTypes READ mimeTypes CONSTANT) |
234 | |
235 | /*! |
236 | * \property KPluginMetaData::formFactors |
237 | */ |
238 | Q_PROPERTY(QStringList formFactors READ formFactors CONSTANT) |
239 | |
240 | /*! |
241 | * \property KPluginMetaData::isEnabledByDefault |
242 | */ |
243 | Q_PROPERTY(bool isEnabledByDefault READ isEnabledByDefault CONSTANT) |
244 | |
245 | public: |
246 | /*! |
247 | * Options for creating a KPluginMetaData object. |
248 | * \since 5.91 |
249 | * |
250 | * \value AllowEmptyMetaData Plugins with empty metaData are considered valid |
251 | * \value [since 6.0] CacheMetaData If KCoreAddons should keep metadata in cache. This makes querying the namespace again faster. Consider using |
252 | * this if you need revalidation of plugins |
253 | * |
254 | */ |
255 | enum KPluginMetaDataOption { |
256 | AllowEmptyMetaData = 1, |
257 | CacheMetaData = 2, |
258 | }; |
259 | Q_DECLARE_FLAGS(KPluginMetaDataOptions, KPluginMetaDataOption) |
260 | Q_FLAG(KPluginMetaDataOption) |
261 | |
262 | /*! Creates an invalid KPluginMetaData instance */ |
263 | KPluginMetaData(); |
264 | |
265 | /*! |
266 | * Reads the plugin metadata from a QPluginLoader instance. You must call QPluginLoader::setFileName() |
267 | * or use the appropriate constructor on \a loader before calling this. |
268 | * |
269 | * \a options Whether or not plugins without JSON metadata are considered valid. |
270 | */ |
271 | KPluginMetaData(const QPluginLoader &loader, KPluginMetaDataOptions options = {}); |
272 | |
273 | /*! |
274 | * Reads the plugin metadata from a plugin which can be loaded from \a file. |
275 | * |
276 | * Platform-specific library suffixes may be omitted since \a file will be resolved |
277 | * using the same logic as QPluginLoader. |
278 | * |
279 | * \a options Whether or not plugins without JSON metadata are considered valid. |
280 | * |
281 | * \sa QPluginLoader::setFileName() |
282 | */ |
283 | KPluginMetaData(const QString &pluginFile, KPluginMetaDataOptions options = {}); |
284 | |
285 | /*! |
286 | * Creates a KPluginMetaData from a QJsonObject holding the metadata and a file name |
287 | * This can be used if the data is not retrieved from a Qt C++ plugin library but from some |
288 | * other source. |
289 | * |
290 | * \a metaData the JSON metadata to use for this object |
291 | * |
292 | * \a pluginFile the file that the plugin can be loaded from |
293 | * |
294 | * \since 6.0 |
295 | */ |
296 | KPluginMetaData(const QJsonObject &metaData, const QString &fileName); |
297 | |
298 | KPluginMetaData(const KPluginMetaData &); |
299 | |
300 | KPluginMetaData &operator=(const KPluginMetaData &); |
301 | |
302 | ~KPluginMetaData(); |
303 | |
304 | /*! |
305 | * Load a KPluginMetaData instance from a .json file. Unlike the constructor with a single file argument, |
306 | * this ensure that only JSON format plugins are loaded and any other type is rejected. |
307 | * |
308 | * \a jsonFile the .json file to load |
309 | * |
310 | * \since 5.91 |
311 | */ |
312 | static KPluginMetaData fromJsonFile(const QString &jsonFile); |
313 | |
314 | /*! |
315 | * \a directory The directory to search for plugins. If a relative path is given for \a directory, |
316 | * all entries of QCoreApplication::libraryPaths() will be checked with \a directory appended as a |
317 | * subdirectory. If an absolute path is given only that directory will be searched. |
318 | * |
319 | * \note Check if the returned KPluginMetaData is valid before continuing to use it. |
320 | * |
321 | * \a pluginId The Id of the plugin. The id should be the same as the filename, see KPluginMetaData::pluginId() |
322 | * |
323 | * \a options Whether or not plugins without JSON metadata are considered valid. |
324 | * |
325 | * \since 5.84 |
326 | */ |
327 | static KPluginMetaData findPluginById(const QString &directory, const QString &pluginId, KPluginMetaDataOptions options = {}); |
328 | |
329 | /*! |
330 | * Find all plugins inside \a directory that satisfy a filter. |
331 | * |
332 | * \a directory The directory to search for plugins. If a relative path is given for \a directory, |
333 | * all entries of QCoreApplication::libraryPaths() will be checked with \a directory appended as a |
334 | * subdirectory. If an absolute path is given only that directory will be searched. |
335 | * |
336 | * \a filter a callback function that returns \c true if the found plugin should be loaded |
337 | * and \c false if it should be skipped. If this argument is omitted all plugins will be loaded. |
338 | * |
339 | * \a options Whether or not plugins without JSON metadata are considered valid. |
340 | * |
341 | * Returns all plugins found in \a directory that fulfill the constraints of \a filter |
342 | * \since 5.86 |
343 | */ |
344 | static QList<KPluginMetaData> |
345 | findPlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter = {}, KPluginMetaDataOptions options = {}); |
346 | |
347 | /*! |
348 | * Returns whether this object holds valid information about a plugin. |
349 | * |
350 | * If this is \c true pluginId() will return a non-empty string. |
351 | */ |
352 | bool isValid() const; |
353 | |
354 | /*! |
355 | * Returns whether this object should be hidden |
356 | * |
357 | * \since 5.8 |
358 | */ |
359 | bool isHidden() const; |
360 | |
361 | /*! |
362 | * Returns the path to the plugin. |
363 | * |
364 | * When the KPluginMetaData(QJsonObject, QString) constructor is used, the string is not modified. |
365 | * Otherwise, the path is resolved using QPluginLoader. |
366 | * For static plugins the fileName is the namespace and pluginId concatenated. |
367 | * |
368 | * \note It is not guaranteed that this is a valid path to a shared library (i.e. loadable |
369 | * by QPluginLoader) since the metadata could also refer to a non-C++ plugin. |
370 | */ |
371 | QString fileName() const; |
372 | |
373 | /*! |
374 | * Returns the full metadata stored inside the plugin file. |
375 | */ |
376 | QJsonObject rawData() const; |
377 | |
378 | /*! |
379 | * Returns the user visible name of the plugin. |
380 | */ |
381 | QString name() const; |
382 | |
383 | /*! |
384 | * Returns a short description of the plugin. |
385 | */ |
386 | QString description() const; |
387 | |
388 | /*! |
389 | * Returns the author(s) of this plugin. |
390 | */ |
391 | QList<KAboutPerson> authors() const; |
392 | |
393 | /*! |
394 | * Returns the translator(s) of this plugin. |
395 | * |
396 | * \since 5.18 |
397 | */ |
398 | QList<KAboutPerson> translators() const; |
399 | |
400 | /*! |
401 | * Returns a list of people that contributed to this plugin (other than the authors and translators). |
402 | * |
403 | * \since 5.18 |
404 | */ |
405 | QList<KAboutPerson> otherContributors() const; |
406 | |
407 | /*! |
408 | * Returns the categories of this plugin (e.g. "playlist/skin"). |
409 | */ |
410 | QString category() const; |
411 | |
412 | /*! |
413 | * Returns the icon name for this plugin |
414 | * \sa QIcon::fromTheme() |
415 | */ |
416 | QString iconName() const; |
417 | |
418 | /*! |
419 | * Returns the short license identifier (e.g. LGPL). |
420 | * |
421 | * See KAboutLicense::byKeyword() for retrieving the full license information |
422 | */ |
423 | QString license() const; |
424 | |
425 | /*! |
426 | * Returns the text of the license, equivalent to KAboutLicense::byKeyword(license()).text() |
427 | * |
428 | * \since 5.73 |
429 | */ |
430 | QString licenseText() const; |
431 | |
432 | /*! |
433 | * Returns a short copyright statement |
434 | * |
435 | * \since 5.18 |
436 | */ |
437 | QString copyrightText() const; |
438 | |
439 | /*! |
440 | * Returns the unique identifier within the namespace of the plugin |
441 | * |
442 | * For C++ plugins, this ID is derived from the filename. |
443 | * It should not be set in the metadata explicitly. |
444 | * |
445 | * When using KPluginMetaData::fromJsonFile or KPluginMetaData(QJsonObject, QString), |
446 | * the "Id" of the "KPlugin" object will be used. If unset, it will be derived |
447 | * from the filename. |
448 | */ |
449 | QString pluginId() const; |
450 | |
451 | /*! |
452 | * Returns the version of the plugin. |
453 | */ |
454 | QString version() const; |
455 | |
456 | /*! |
457 | * Returns the website of the plugin. |
458 | */ |
459 | QString website() const; |
460 | |
461 | /*! |
462 | * Returns the website where people can report a bug found in this plugin |
463 | * \since 5.99 |
464 | */ |
465 | QString bugReportUrl() const; |
466 | |
467 | /*! |
468 | * Returns a list of MIME types this plugin can handle (e.g. "application/pdf", "image/png", etc.) |
469 | * \since 5.16 |
470 | */ |
471 | QStringList mimeTypes() const; |
472 | |
473 | /*! |
474 | * Returns true if this plugin can handle the given mimetype |
475 | * This is more accurate than mimeTypes().contains(mimeType) because it also |
476 | * takes MIME type inheritance into account. |
477 | * \since 5.66 |
478 | */ |
479 | bool supportsMimeType(const QString &mimeType) const; |
480 | |
481 | /*! |
482 | * Returns A string list of formfactors this plugin is useful for, e.g. desktop, handset or mediacenter. |
483 | * The keys for this are not formally defined, though the above-mentioned values should be used when applicable. |
484 | * |
485 | * \since 5.12 |
486 | */ |
487 | QStringList formFactors() const; |
488 | |
489 | /*! |
490 | * Returns whether the plugin should be enabled by default. |
491 | * This is only a recommendation, applications can ignore this value if they want to. |
492 | */ |
493 | bool isEnabledByDefault() const; |
494 | |
495 | /*! |
496 | * Returns \c true if the plugin is enabled in \a config, otherwise returns isEnabledByDefault(). |
497 | * This can be used in conjunction with KPluginWidget. |
498 | * |
499 | * The \a config param should be a KConfigGroup object, because KCoreAddons can not depend |
500 | * on KConfig directly, this parameter is a template. |
501 | * |
502 | * \a config KConfigGroup where the enabled state is stored |
503 | * |
504 | * \since 5.89 |
505 | */ |
506 | template<typename T> |
507 | bool isEnabled(const T &config) const |
508 | { |
509 | Q_ASSERT(config.isValid()); |
510 | return config.readEntry(pluginId() + QLatin1String("Enabled" ), isEnabledByDefault()); |
511 | } |
512 | |
513 | /*! |
514 | * Returns the string value for \a key from the metadata or \a defaultValue if the key does not exist |
515 | * |
516 | * if QString is not the correct type for \a key you should use the other overloads or KPluginMetaData::rawData |
517 | */ |
518 | QString value(QStringView key, const QString &defaultValue = QString()) const; |
519 | QString value(const QString &key, const QString &defaultValue = QString()) const; // TODO KF7: remove |
520 | QString value(const QString &key, const char *ch) const = delete; |
521 | |
522 | /*! |
523 | * \overload |
524 | * \since 5.88 |
525 | */ |
526 | bool value(QStringView key, bool defaultValue) const; |
527 | bool value(const QString &key, bool defaultValue) const; // TODO KF7: remove |
528 | |
529 | /*! |
530 | * \overload |
531 | * \since 5.88 |
532 | */ |
533 | int value(QStringView key, int defaultValue) const; |
534 | int value(const QString &key, int defaultValue) const; // TODO KF7: remove |
535 | |
536 | /*! Returns the value for \a key from the metadata or \a defaultValue if the key does not exist. |
537 | * If the type of \a key is string, a list containing just that string will be returned. |
538 | * If the type is array, the list will contain one entry for each array member. |
539 | * \overload |
540 | * \since 5.88 |
541 | */ |
542 | QStringList value(QStringView key, const QStringList &defaultValue) const; |
543 | QStringList value(const QString &key, const QStringList &defaultValue) const; // TODO KF7: remove |
544 | |
545 | /*! |
546 | * |
547 | */ |
548 | bool operator==(const KPluginMetaData &other) const; |
549 | |
550 | inline bool operator!=(const KPluginMetaData &other) const |
551 | { |
552 | return !(*this == other); |
553 | } |
554 | |
555 | /*! |
556 | * Returns true if the instance represents a static plugin |
557 | * |
558 | * \note for loading plugin the plugin independently of it being static/dynamic |
559 | * use KPluginFactory::loadFactory or KPluginFactory::instantiatePlugin. |
560 | * \since 5.89 |
561 | */ |
562 | bool isStaticPlugin() const; |
563 | |
564 | private: |
565 | KCOREADDONS_NO_EXPORT QStaticPlugin staticPlugin() const; |
566 | KCOREADDONS_NO_EXPORT QString requestedFileName() const; |
567 | |
568 | QExplicitlySharedDataPointer<KPluginMetaDataPrivate> d; |
569 | friend class KPluginFactory; |
570 | friend class KPluginMetaDataPrivate; |
571 | }; |
572 | |
573 | inline size_t qHash(const KPluginMetaData &md, size_t seed) |
574 | { |
575 | return qHash(key: md.pluginId(), seed); |
576 | } |
577 | |
578 | /*! |
579 | * \since 6.0 |
580 | * \relates KPluginMetaData |
581 | */ |
582 | KCOREADDONS_EXPORT QDebug operator<<(QDebug debug, const KPluginMetaData &metaData); |
583 | |
584 | Q_DECLARE_TYPEINFO(KPluginMetaData, Q_RELOCATABLE_TYPE); |
585 | Q_DECLARE_OPERATORS_FOR_FLAGS(KPluginMetaData::KPluginMetaDataOptions) |
586 | |
587 | #endif // KPLUGINMETADATA_H |
588 | |