======== Plugin Manager ======== {{ :en:entwickler:plugin_manager_overview.png?direct |}} The Plugin Manager module provides a simple user interface and standardized procedures for * installing, * uninstalling, * updating, * managing and * integrating plugins in Admidio. ======= Make your plugin compatible with the Plugin Manager (Admidio 5) ======= In order to make your plugin compatible with the Plugin Manager certain requirements must first be met. These include: * [[en:entwickler:plugin_manager#naming_conventions|Naming conventions]] * A consistent [[en:entwickler:plugin_manager#folder_structure|folder structure]] * The use of the new plugin namespace //Plugins// * A [[en:entwickler:plugin_manager#plugin_class|plugin class]] * A JSON [[en:entwickler:plugin_manager#configuration_file|configuration file]] ====== Naming conventions ====== In order to use the new plugin namespace //Plugins// some naming conventions must be observed: - Neither any folder of the plugin nor any file used by the plugin may contain delimiters like //"-"// or //"_"// especially if it should be used via a class import. For consistency reasons only the optional folder //db_scripts// (see: [[en:entwickler:plugin_manager#folder_structure|folder structure]]) should be an exception from this rule. - For better readability file and folder names containing multiple words should follow the CamelCase notation. ====== Folder structure ====== The basic folder structure of any plugin is shown below using the overview plugin "Birthday" as example: {{:en:entwickler:folder_structure_birthday.png?direct&400|}} ===== classes folder ===== ==== Presenter classes ==== === Plugin-specific classes === Inside the Presenter folder all necessary PagePresenter classes of the plugin took place. These should be simmilar to the module-specific PagePresenter classes. === Plugin preferences class === If the plugin provides preferences there should be a class //"[PluginName]**PreferencesPresenter**"// present. This class contains a single static preference method following the naming conventions of the Preferences class of Admidio. Below an example snippet of the //createBirthdayForm// preference method from the "Birthday" overview plugin: public static function createBirthdayForm(Smarty $smarty): string { global $gL10n, $gCurrentSession; $pluginBirthday = Birthday::getInstance(); $formValues = $pluginBirthday::getPluginConfig(); $formBirthday = new FormPresenter( 'adm_preferences_form_birthday', $pluginBirthday::getPluginPath() . '/templates/preferences.plugin.birthday.tpl', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/preferences.php', array('mode' => 'save', 'panel' => 'birthday')), null, array('class' => 'form-preferences') ); $selectBoxEntries = array( '0' => $gL10n->get('SYS_DISABLED'), '1' => $gL10n->get('SYS_ENABLED'), '2' => $gL10n->get('ORG_ONLY_FOR_REGISTERED_USER') ); $formBirthday->addSelectBox( 'birthday_plugin_enabled', Language::translateIfTranslationStrId($formValues['birthday_plugin_enabled']['name']), $selectBoxEntries, array( 'defaultValue' => $formValues['birthday_plugin_enabled']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_plugin_enabled']['description'] ) ); $formBirthday->addCheckbox( 'birthday_show_names_extern', Language::translateIfTranslationStrId($formValues['birthday_show_names_extern']['name']), $formValues['birthday_show_names_extern']['value'], array('helpTextId' => $formValues['birthday_show_names_extern']['description']) ); [...] $formBirthday->addSubmitButton( 'adm_button_save_birthday', $gL10n->get('SYS_SAVE'), array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3') ); $formBirthday->addToSmarty($smarty); $gCurrentSession->addFormObject($formBirthday); return $smarty->fetch($pluginBirthday::getPluginPath() . '/templates/preferences.plugin.birthday.tpl'); } ==== Entity, Service and ValueObjects classes ==== If the plugin has specific Entity, Service or ValueObjects classes these should be placed in the corresponding subfolders as it is done with the Admidio modules. ==== Main plugin class ==== The main [[en:entwickler:plugin_manager#plugin_class|plugin class]] (e.g.: //Birthday//) has to take place directly in the classes folder. ===== db_scripts folder ===== This folder is optional and is used to place plugin-specific database scripts used when installing (//db-install.sql//) or uninstalling(//db-uninstall.sql//) the plugin. In addition to possible database scripts update scripts (e.g.: //update_1_0.xml//) for the plugin could be placed in this folder. These should be structured like the update scripts used for Admidio to updateto a new version. Simmilar to the update scripts used for Admidio it is possible to call plugin-specific update steps. To implement these methods a final class named //UpdateStepsCode// has to be present in the folder //"[PluginName]\classes\Service\"//. The class implementation should look like this: final class UpdateStepsCode { /** * @var Database */ private static Database $db; /** * Set the database * @param Database $database The database instance */ public static function setDatabase(Database $database) { self::$db = $database; } /** * @throws Exception */ public static function /*[method name used in update_x_x.xml]*/() { [...] } } ===== languages folder ===== This folder contains the plugin specific translation files as before. ===== templates folder ===== To move away from the deprecated classes HtmlPage and HtmlTable plugins should exclusively use the Smarty template engine whose files are stored in this folder. ===== Root plugin folder ===== ==== Main plugin file ==== Inside the root plugin folder a main plugin file is necessary. This file can contain plugin specific logic like the module entry files of Admidio or simply initialize the [[en:entwickler:plugin_manager#plugin_class|plugin class]] like following example: doRender(isset($page) ? $page : null); } catch (Throwable $e) { echo $e->getMessage(); } ==== Plugin configuration file ==== Each plugin has to provide a JSON configuration file containing basic plugin informations and configurations (see: [[en:entwickler:plugin_manager#configuration_file|Configuration file]]). ====== Configuration file ====== Each plugin has o provide a JSON configuration file. The following table gives an overview of all keys currently available: ^ Key: ^ Mandatory: ^ Description: ^ | "name" | X | Defines the plugin name (can be a translation ID or plain text). | | "description" | | A short description of the plugin shown on the Plugin Manager overview (can be a translation ID or plain text). | | "version" | X | The current version of the plugin. | | "author" | | The author of the plugin. | | "url" | | A URL to a Wiki page or a projekt page. | | "icon" | | The icon displayed in the Admidio sidebar and on the Plugin Manager overview. | | "mainFile" | | The filename of the main plugin entry point. If not set "index.php" or "PluginName.php" is assumed. | | "dependencies" | (X) |An array of all dependencies the plugin needs to work properly. If this is not set the Plugin Manager isn't able to detect whether the plugin can be installed properly. | | "defaultConfig" | (X) | An object containing all configuration parameters used by the plugin. Every parameter needs to contain a "name", "descprition", "type" and "value" key. | The following example shows the structure based on the overview plugin Birthday: { "name": "PLG_BIRTHDAY_PLUGIN_NAME", "description": "PLG_BIRTHDAY_PLUGIN_DESCRIPTION", "version": "1.0.0", "author": "Admidio Team", "url": "https://www.admidio.org", "icon": "bi-cake2", "mainFile": "index.php", "dependencies": [ "Admidio\\Infrastructure\\Plugins\\Overview", "Admidio\\Infrastructure\\Plugins\\PluginAbstract", "Admidio\\Infrastructure\\Utils\\SecurityUtils", "Admidio\\Roles\\Service\\RolesService" ], "defaultConfig": { "birthday_plugin_enabled": { "name": "ORG_ACCESS_TO_PLUGIN", "description": "ORG_ACCESS_TO_PLUGIN_DESC", "type": "integer", "value" : 1 }, "birthday_show_names_extern": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_NAMES_EXTERN", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_NAMES_EXTERN_DESC", "type": "boolean", "value" : false }, "birthday_show_names": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_NAMES", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_NAMES_DESC", "type": "integer", "value" : 0 }, "birthday_show_age": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_AGE", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_AGE_DESC", "type": "boolean", "value" : false }, "birthday_show_age_salutation": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_AGE_SALUTATION", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_AGE_SALUTATION_DESC", "type": "integer", "value" : 18 }, "birthday_show_notice_none": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_NOTICE_NONE", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_NOTICE_NONE_DESC", "type": "boolean", "value" : true }, "birthday_show_past": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_PAST", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_PAST_DESC", "type": "integer", "value" : 1 }, "birthday_show_future": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_FUTURE", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_FUTURE_DESC", "type": "integer", "value" : 2 }, "birthday_show_display_limit": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_DISPLAY_LIMIT", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_DISPLAY_LIMIT_DESC", "type": "integer", "value" : 200 }, "birthday_show_email_extern": { "name": "PLG_BIRTHDAY_PREFERENCES_SHOW_EMAIL_EXTERN", "description": "PLG_BIRTHDAY_PREFERENCES_SHOW_EMAIL_EXTERN_DESC", "type": "integer", "value" : 0 }, "birthday_roles_view_plugin": { "name": "PLG_BIRTHDAY_PREFERENCES_ROLES_VIEW_PLUGIN", "description": "PLG_BIRTHDAY_PREFERENCES_ROLES_VIEW_PLUGIN_DESC", "type": "array", "value" : ["All"] }, "birthday_roles_sql": { "name": "PLG_BIRTHDAY_PREFERENCES_ROLES_SQL", "description": "PLG_BIRTHDAY_PREFERENCES_ROLES_SQL_DESC", "type": "array", "value" : ["All"] }, "birthday_sort_sql": { "name": "PLG_BIRTHDAY_PREFERENCES_SORT_SQL", "description": "PLG_BIRTHDAY_PREFERENCES_SORT_SQL_DESC", "type": "string", "value" : "DESC" } } } ====== Plugin class ====== The Plugin class extends the basic functionality provided by the abstract class //PluginAbstract// and implements the //doRender// method. In it's simplest implementation the //doRender// method could just call the PagePresenter //show// method: public static function doRender(?PagePresenter $page = null) : bool { if (!isset($page)) { throw new Exception("The page parameter must be set."); } $page->show(); return true; } All methods provided by the //PluginAbstract// can be overwritten in this plugin class and adapted to the plugin needs. The following code shows an example overwriting of the //getPluginConfig()// method in the "Birthday" overview plugin implementation: public static function getPluginConfig() : array { // get the plugin config from the parent class $config = parent::getPluginConfig(); // if the key equals 'birthday_roles_view_plugin' and the value is still the default value, retrieve the categories from the database if (array_key_exists('birthday_roles_view_plugin', $config) && $config['birthday_roles_view_plugin']['value'] === self::$defaultConfig['birthday_roles_view_plugin']['value']) { $config['birthday_roles_view_plugin']['value'] = self::getAvailableRoles(1, true); } if (array_key_exists('birthday_roles_sql', $config) && $config['birthday_roles_sql']['value'] === self::$defaultConfig['birthday_roles_sql']['value']) { $config['birthday_roles_sql']['value'] = self::getAvailableRoles(1, true); } return $config; } ======= PluginAbstract class ======= methods documentation