Warning, /frameworks/kpackage/README.md is written in an unsupported language. File is not indexed.

0001 # KPackage Framework
0002 
0003 Installation and loading of additional content (ex: scripts, images...) as packages.
0004 
0005 ## Introduction
0006 
0007 The KPackage framework lets the user install and load packages of non binary content such as scripted extensions or graphic assets, as if they were traditional plugins.
0008 
0009 # Using KPackage
0010 
0011 The frameworks consists of 3 classes: PackageStructure, PackageLoader and Package.
0012 The central class is Package, that represents an installed package and is used to access its file, install, update or uninstall it.
0013 
0014 In order to use the framework the developer has to define a package structure. (the package type, that will be depending on the particular application, and the type of content that will be provided, may be a sound theme, graphics assets, qml files etc).
0015 
0016 The package loader is used to load instances of Package of a given type.
0017 
0018 ## Package class
0019 
0020 Package is the central class of the framework, and it represents a package of a certain type, it can be either "abstract", not pointing to a particular package on disk (in which case the only things allowed will be installing and uninstalling) or it can point to a particular package installed on disk: its path() not being empty and valid.
0021 
0022 A Package defines a particular filesystem structure for installed addons, made up of allowed subdirectories in which content will be organized and several optional named files, if you want to mandate a particular named file in every package (for instance if your addons need a file named "main.qml" in order to be considered valid).
0023 
0024 A fixed structure is encouraged by the api of Package in order to encourage plugin creators to distribute packages made with a standard and expected structure, encouraging a common path installation prefix, and strongly discouraging cross references between different packages such as ../ relative paths and symlinks across packages.
0025 
0026 An example filesystem structure (that in the end, depends from the PackageStructure) may be:
0027 
0028 ```
0029 
0030 (root)
0031 |-- code
0032 |   `-- main.js
0033 |-- config
0034 |   `-- config.xml
0035 |-- images
0036 |   `-- background.png
0037 `-- metadata.json
0038 
0039 ```
0040 
0041 The special, main and always required for every package structure file is the "metadata" file, which describes the package with values such as name, description, pluginname etc. It is in the JSON format accepted by KPluginMetadata. The metadata is accessible with Package::metadata().
0042 
0043 All the other files are under the contents/ subdirectory: a folder under addDirectoryDefinition will be registered under contents/.
0044 
0045 If the developer wants that those extension package require to have a particular file or folder, he will use setRequired() on a particular key: in that case if a package misses that file or folder, isValid() will return false and all the filePath resolution won't work.
0046 
0047 To access a file within the package, the filePath(key, filename) method is used. In order to use that the caller doesn't have to care where the package is installed and absolute paths of any kind, it will pass a relative one, and the prober absolute path will be resolved and returned.
0048 
0049 The key parameter is one of those that has been registered by the structure beforehand by addFileDefinition or addDirectoryDefinition. Files asked with an unknown keys won't be resolved.
0050 
0051 Accessing a file under a directory will look like package.filePath("ui", "main.qml") while accessing a particular named file will look like package.filePath("mainscript").
0052 
0053 If as file path is passed a relative path with ".." elements in it or an absolute path, the resolution will fail as well, in order to discourage cross references (same thing will happen if the resolved file is a symlink), unless the package structure explicitly allowed it with setAllowExternalPaths().
0054 Here is a minimal example of a PackageStructure::initPackage.
0055 
0056 ```
0057 ...
0058 void MyStructure::initPackage(KPackage::Package *package)
0059 {
0060     package->setDefaultPackageRoot(QStringLiteral("myapp" "/packages/"));
0061 
0062     package->addDirectoryDefinition("images", QStringLiteral("images"), i18n("Images"));
0063     QStringList mimetypes;
0064     mimetypes << QStringLiteral("image/svg+xml") << QStringLiteral("image/png") << QStringLiteral("image/jpeg");
0065     package->setMimeTypes("images", mimetypes);
0066     package->addDirectoryDefinition("code", QStringLiteral("code"), i18n("Javascript files"));
0067     package->addFileDefinition("mainscript", QStringLiteral("scripts/main.js"), i18n("Main Script File"));
0068     //this way, the package will not be considered valid if mainscript is not present
0069     package->setRequired("mainscript", true);
0070 }
0071 ...
0072 K_PLUGIN_CLASS_WITH_JSON(MyStructure, "myapp-packagestructure-mystructure.json")
0073 ```
0074 
0075 The line K_PLUGIN_CLASS_WITH_JSON is important in order to export the PackageStructure subclass MyStructure as a standalone plugin library using the KPluginLoader architecture, in order to be loadable and recognizable by a PackageLoader instance from any process (without the need to explicitly link to a library containing the MyStructure implementation).
0076 
0077 In order to build the plugin, it is also needed a .json file describing the metadata for the plugin:
0078 
0079 ```json
0080 {
0081     "KPlugin": {
0082         "Authors": [
0083             {
0084                 "Email": "john@example.com",
0085                 "Name": "John Doe"
0086             }
0087         ],
0088         "Id": "MyApp/MyStructure",
0089         "Name": "My package type",
0090         "Version": "1"
0091     },
0092     "X-KDE-ParentApp": "org.kde.myapp"
0093 }
0094 
0095 
0096 ```
0097 
0098 And an own CMakeLists.txt.
0099 
0100 ```
0101 #build the PackageStructure implementation as a standalone library
0102 add_library(myapp_packagestructure_mystructure MODULE mystructure.cpp)
0103 
0104 target_link_libraries(myapp_packagestructure_mystructure
0105    KF5::I18n
0106    KF5::Package
0107 )
0108 
0109 #install the plugin where PackageLoader looks for them
0110 install(TARGETS myapp_packagestructure_mystructure DESTINATION ${KDE_INSTALL_PLUGINDIR}/kpackage/packagestructure)
0111 
0112 ```
0113 
0114 The C++ implementation with its CMake and JSON files are recommended to be in their own subdirectory, for separation respect to the code of the parent application.
0115 
0116 
0117 ## Package structures
0118 
0119 Package structures are instance of PackageStructure and are shipeed as plugins.
0120 
0121 PackageStructure::initPackage will be executed once when any package is created, and this initializes the Package instance on what are the allowed subfolders and named files for this type. The most important thing to do in initPackage is setting a packageRoot path, that will be a common prefix under which all packages will be installed, relative to the xdg data paths. For instance the packages of type "plasmoid" will be under "plasma/plasmoids" which means searching under ~/.local/share/plasma/plasmoids and /usr/share/plasma/plasmoids.
0122 
0123 This is the only required function to be reimplemented for a given PackageStructure, the other ones being optional only when particular needs ensue.
0124 
0125 PackageStructure::pathChanged gets executed when the base path of the Package changes (that means, what package on the filesystem the class is pointing to, as this can change at runetime with Package::setPath). Reimplement this only if you need to have extra check/operations when such a thing happens.
0126 
0127 ## Package loader
0128 
0129 The PackageLoader is used to list, find and load Package instances.
0130 
0131 The most important functions of this class are listPackages/findPackages to list and search packages of a certain type (package structure), for example listing all plasmoids or all plasmoids that have "clock" in their description PackageLoader::loadPackage loads a "blank" package of a certain type. The returned package will be a Package instance that has been initialized to that given structure, pointing to a particular package in the filesystem only if the optional parameter packagePath has been passed. If not, it will be an invalid package, still usable to install and uninstall packages of this type or it can always load a package on the filesystem with the setPath Package method, relative to the packageroot.
0132 
0133 Upon setPath, subdirectories of the packageroot will be searched both in the global system installation and in the user home, preferring the home, such as ~/.local/share/plasma/plasmoids and /usr/share/plasma/plasmoids. If one has to iterate through packages, creating a single Package instance then loading different ones with subsequent calls of setPath is preferable over creating multiple instances for performance reasons.
0134 Example of code loading a package:
0135 
0136 ```
0137 KPackage::Package p = KPackage::PackageLoader::self()->loadPackage("Plasma/Applet", "org.kde.plasma.analogclock");
0138 if (p.isValid()) {
0139     qDebug() << p.filePath("mainscript");
0140 }
0141 ```