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');
}
=== Multiple FormPresenter elements ===
With Admidio 5 the preference page has been redesigned. It is now possible to display multiple "subcards" on one preference panel. To use this feature from a plugin the **//hasSubcards//** configuration flag must be set to //true//.
In your PluginPreferencePresenter class you can now define multiple FormPresenter elements and add different preferences to each form. The first FormPresenter can now be assigned to the smarty variable like the following example shows:
public static function createBirthdayForm(Smarty $smarty): string
{
[...]
$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->addSubmitButton(
'adm_button_save_birthday',
$gL10n->get('SYS_SAVE'),
array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
);
$formBirthday->addToSmarty($smarty);
$gCurrentSession->addFormObject($formBirthday);
[...]
Now you can define a new FormPresenter for the second "card":
$formBirthday2= new FormPresenter(
'adm_preferences_form_birthday_2',
$pluginBirthday::getPluginPath() . '/templates/preferences.plugin.birthday.2.tpl',
SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/preferences.php', array('mode' => 'save', 'panel' => 'birthday')),
null,
array('class' => 'form-preferences')
);
[...]
$formBirthday2->addSubmitButton(
'adm_button_save_birthday_2',
$gL10n->get('SYS_SAVE'),
array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
);
[...]
This form cannot be added to smarty with the //addToSmarty// method because this would override all values set by the previous form element. Therefore a new method //appendToSmarty// was implemented in the FormPresenter class (currently only available in the //plugin-manager// branch on GitHub). Use this method to add the second form to the smarty variable:
[...]
$formBirthday2->appendToSmarty($smarty);
$gCurrentSession->addFormObject($formBirthday2);
[..]
You can add as many forms as you like using this method.
When all forms are added to smarty you can use a "wrapper template" to load the form specific template files on one page. For this wrapper template you can add a smarty variable containing informations you want to show. In the following example multiple cards should be used for displaying the different forms:
[...]
//assign card titles, icons and corresponding template files
$cards = array(
array('title' => $birthdayName, 'icon' => $birthdayIcon, 'templateFile' => $birthdayPath . '/templates/preferences.plugin.birthday.tpl'),
array('title' => $birthdayName2, 'icon' => $birthdayIcon2, 'templateFile' => $birthdayPath2 . '/templates/preferences.plugin.birthday.2.tpl')
);
$smarty->assign('cards', $cards);
return $smarty->fetch($birthdayPath . '/templates/preferences.birthday.cards.tpl');
}
The corresponding wrapper template is shown below:
{foreach $cards as $index => $card}
{$card.title}
{include file=$card.templateFile dataIndex=$index}
{/foreach}
This template creates cards for each form element defined in the //$cards// variable and loads the corresponding template file. It is necessary to pass the index of the zero-based $cards array to the corresponding template file to retrieve the correct smarty data that is now also available as a zero-based array for each form variable.
These are:
* //formType//,
* //attributes//,
* //elements//,
* //javascript// and
* //hasRequiredFields//.
This following example shows a possible implementation of the first form (so with index 0):
{$javascript[$dataIndex]}
This makes it possible to show seperate forms on the same page. There are no limits to the display options whether as cards (as in the example), tabs, or accordions. With this approach each available Bootstrap/HTML display is possible through the smarty templates.
===== Entity, Service and ValueObjects classes =====
If the plugin has specific //Entity//, //Service//, or //ValueObjects// classes these should be placed in the corresponding subfolders as is done with the Admidio modules.
===== Main plugin class =====
The main [[en:entwickler:plugin_manager#plugin_class|plugin class]] (e.g.: //Birthday//) must be placed 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 database scripts update scripts (e.g.: //update_1_0.xml//) for the plugin can be placed in this folder. These should be structured like the update scripts used by Admidio to update to a new version.
Similar to Admidio's update scripts it is possible to call plugin-specific update steps. To implement these steps a final class named //UpdateStepsCode// must 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.
====== templates folder ======
To move away from the deprecated //HtmlPage// and //HtmlTable// classes plugins should exclusively use the Smarty template engine with files stored in this folder.
====== Root plugin folder ======
===== Main plugin file =====
Inside the root plugin folder a main plugin file is required. This file can contain plugin-specific logic (similar to Admidio’s module entry files) or simply initialize the [[en:entwickler:plugin_manager#plugin_class|plugin class]] as in the following example:
doRender(isset($page) ? $page : null);
} catch (Throwable $e) {
echo $e->getMessage();
}
===== Plugin configuration file =====
Each plugin must provide a JSON configuration file containing basic plugin information and configuration (see: [[en:entwickler:plugin_manager#configuration_file|Configuration file]]).
======= Configuration file =======
Each plugin has to 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 project 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. |
| "hasSubcards" | | This optional flag is used to determinate whether a plugins preference page contains multiple "cards" to display (for CSS styling purposes only). |
| "dependencies" | (X) | An array of all dependencies the plugin needs to work properly. If this is not set the Plugin Manager cannot detect whether the plugin can be installed properly. |
| "defaultConfig" | (X) | An object containing all configuration parameters used by the plugin. Every parameter should contain a "name", "description", "type" and "value" key. |
The following example shows the structure based on the "Birthday" overview plugin:
{
"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 its simplest implementation the //doRender// method can 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 //PluginAbstract// can be overridden in this plugin class and adapted to the plugin's needs. The following code shows an example override 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 ========