diff --git a/.gitignore b/.gitignore
index 9cddf76521..5898032709 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,16 +10,16 @@
!/adm_my_files/index.html
# Only allow commit of official plugins
-/adm_plugins/*
-!/adm_plugins/birthday/
-!/adm_plugins/calendar/
-!/adm_plugins/login_form/
-!/adm_plugins/random_photo/
-!/adm_plugins/announcement-list/
-!/adm_plugins/event-list/
-!/adm_plugins/latest-documents-files/
-!/adm_plugins/who-is-online/
-!/adm_plugins/category_report/
+/plugins/*
+!/plugins/birthday/
+!/plugins/calendar/
+!/plugins/login-form/
+!/plugins/random-photo/
+!/plugins/announcement-list/
+!/plugins/event-list/
+!/plugins/latest-documents-files/
+!/plugins/who-is-online/
+
# Only allow commit of official themes
/themes/*
diff --git a/adm_plugins/announcement-list/config_sample.php b/adm_plugins/announcement-list/config_sample.php
deleted file mode 100644
index a648a170f4..0000000000
--- a/adm_plugins/announcement-list/config_sample.php
+++ /dev/null
@@ -1,36 +0,0 @@
-getInt('announcements_module_enabled') > 0) {
- // read announcements from database
- $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('ANN'));
-
- $sql = 'SELECT cat.*, ann.*
- FROM ' . TBL_ANNOUNCEMENTS . ' AS ann
- INNER JOIN ' . TBL_CATEGORIES . ' AS cat
- ON cat_id = ann_cat_id
- WHERE cat_id IN (' . Database::getQmForValues($catIdParams) . ')
- ' . $plgSqlCategories . '
- ORDER BY ann_timestamp_create DESC
- LIMIT ' . $plg_announcements_count;
-
- $pdoStatement = $gDb->queryPrepared($sql, array_merge($catIdParams, $plg_categories));
- $plgAnnouncementsList = $pdoStatement->fetchAll();
-
- if ($gSettingsManager->getInt('announcements_module_enabled') === 1
- || ($gSettingsManager->getInt('announcements_module_enabled') === 2 && $gValidLogin)) {
- if ($pdoStatement->rowCount() > 0) {
- // get announcements data
- $plgAnnouncement = new Announcement($gDb);
- $announcementArray = array();
-
- foreach ($plgAnnouncementsList as $plgRow) {
- $plgAnnouncement->clear();
- $plgAnnouncement->setArray($plgRow);
-
- if ($plg_max_char_per_word > 0) {
- // Interrupt words of headline if they are too long
- $plgNewHeadline = '';
-
- $plgWords = explode(' ', $plgAnnouncement->getValue('ann_headline'));
-
- foreach ($plgWords as $plgValue) {
- if (strlen($plgValue) > $plg_max_char_per_word) {
- $plgNewHeadline .= ' ' . substr($plgValue, 0, $plg_max_char_per_word) . '-
' .
- substr($plgValue, $plg_max_char_per_word);
- } else {
- $plgNewHeadline .= ' ' . $plgValue;
- }
- }
- } else {
- $plgNewHeadline = $plgAnnouncement->getValue('ann_headline');
- }
-
- // show preview text
- if ($plgShowFullDescription === 1) {
- $plgNewDescription = $plgAnnouncement->getValue('ann_description');
- } elseif ($plg_show_preview > 0) {
- // remove all html tags except some format tags
- $plgNewDescription = strip_tags($plgAnnouncement->getValue('ann_description'));
-
- // read first x chars of text and additional 15 chars. Then search for last space and cut the text there
- $plgNewDescription = substr($plgNewDescription, 0, $plg_show_preview + 15);
- $plgNewDescription = substr($plgNewDescription, 0, strrpos($plgNewDescription, ' ')) . '
- »';
- }
-
- $announcementArray[] = array(
- 'uuid' => $plgAnnouncement->getValue('ann_uuid'),
- 'headline' => $plgNewHeadline,
- 'description' => $plgNewDescription,
- 'creationDate' => $plgAnnouncement->getValue('ann_timestamp_create', $gSettingsManager->getString('system_date'))
- );
- }
-
- $announcementListPlugin->assignTemplateVariable('announcements', $announcementArray);
- } else {
- $announcementListPlugin->assignTemplateVariable('message',$gL10n->get('SYS_NO_ENTRIES'));
- }
- } else {
- $announcementListPlugin->assignTemplateVariable('message',$gL10n->get('PLG_ANNOUNCEMENT_LIST_NO_ENTRIES_VISITORS'));
- }
- if (isset($page)) {
- echo $announcementListPlugin->html('plugin.announcement-list.tpl');
- } else {
- $announcementListPlugin->showHtmlPage('plugin.announcement-list.tpl');
- }
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/announcement-list/languages/en.xml b/adm_plugins/announcement-list/languages/en.xml
deleted file mode 100644
index ec8cf6313f..0000000000
--- a/adm_plugins/announcement-list/languages/en.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- All entries
- Announcements
- There are no announcements available for visitors. Log in to see the announcements.
-
diff --git a/adm_plugins/birthday/config_sample.php b/adm_plugins/birthday/config_sample.php
deleted file mode 100644
index c13e4d9e2f..0000000000
--- a/adm_plugins/birthday/config_sample.php
+++ /dev/null
@@ -1,72 +0,0 @@
- 0) {
- // current user must be member of at least one listed role
- if (count(array_intersect($plg_birthday_roles_view_plugin, $gCurrentUser->getRoleMemberships())) > 0) {
- $plgBirthdayShowNames = true;
- }
- } else {
- // every member could view birthdays
- $plgBirthdayShowNames = true;
- }
- } else {
- if ($plg_show_names_extern === 1) {
- // every visitor is allowed to view birthdays
- $plgBirthdayShowNames = true;
- }
- }
-
- // Check if the role condition has been set
- if (empty($plg_rolle_sql)) {
- $sqlRoleIds = 'IS NOT NULL';
- } else {
- $sqlRoleIds = 'IN (' . implode(',', $plg_rolle_sql) . ')';
- }
-
- // Check if the sort condition has been set
- if (!isset($plg_sort_sql) || $plg_sort_sql === '') {
- $sqlSort = 'DESC';
- } else {
- $sqlSort = $plg_sort_sql;
- }
-
- // if no birthdays should be shown than disable future and former birthday periods
- if (!$plgBirthdayShowNames) {
- $plg_show_zeitraum = 0;
- $plg_show_future = 0;
- }
-
- $fieldBirthday = $gProfileFields->getProperty('BIRTHDAY', 'usf_id');
-
- if ($gDbType === 'pgsql') {
- $sql = 'SELECT DISTINCT usr_id, usr_uuid, usr_login_name,
- last_name.usd_value AS last_name, first_name.usd_value AS first_name,
- birthday.bday AS birthday, birthday.bdate,
- EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - birthday.bdate) * (-1) AS days_to_bdate, -- DATE_NOW
- EXTRACT(YEAR FROM bdate) - EXTRACT(YEAR FROM TO_TIMESTAMP(bday, \'YYYY-MM-DD\')) AS age,
- email.usd_value AS email, gender.usd_value AS gender
- FROM ' . TBL_USERS . ' AS users
- INNER JOIN ( (SELECT usd_usr_id, usd_value AS bday,
- TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')) || TO_CHAR(TO_TIMESTAMP(bd1.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd1
- WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')) || TO_CHAR(TO_TIMESTAMP(bd1.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- UNION
- (SELECT usd_usr_id, usd_value AS bday,
- TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\'))-1 || TO_CHAR(TO_TIMESTAMP(bd2.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd2
- WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')- INTERVAL \'1 year\') || TO_CHAR(TO_TIMESTAMP(bd2.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- UNION
- (SELECT usd_usr_id, usd_value AS bday,
- TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\'))+1 || TO_CHAR(TO_TIMESTAMP(bd3.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd3
- WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')+ INTERVAL \'1 year\') || TO_CHAR(TO_TIMESTAMP(bd3.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- ) AS birthday
- ON birthday.usd_usr_id = usr_id
- LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
- ON last_name.usd_usr_id = usr_id
- AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
- ON first_name.usd_usr_id = usr_id
- AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS email
- ON email.usd_usr_id = usr_id
- AND email.usd_usf_id = ? -- $gProfileFields->getProperty(\'EMAIL\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS gender
- ON gender.usd_usr_id = usr_id
- AND gender.usd_usf_id = ? -- $gProfileFields->getProperty(\'GENDER\', \'usf_id\')
- LEFT JOIN ' . TBL_MEMBERS . '
- ON mem_usr_id = usr_id
- AND mem_begin <= ? -- DATE_NOW
- AND mem_end > ? -- DATE_NOW
- INNER JOIN ' . TBL_ROLES . '
- ON mem_rol_id = rol_id
- AND rol_valid = true
- INNER JOIN ' . TBL_CATEGORIES . '
- ON rol_cat_id = cat_id
- AND cat_org_id = ? -- $gCurrentOrgId
- WHERE usr_valid = true
- AND mem_rol_id ' . $sqlRoleIds . '
- ORDER BY days_to_bdate ' . $sqlSort . ', last_name, first_name';
- } else {
- $sql = 'SELECT DISTINCT usr_id, usr_uuid, usr_login_name,
- last_name.usd_value AS last_name, first_name.usd_value AS first_name,
- birthday.bday AS birthday, birthday.bdate,
- DATEDIFF(birthday.bdate, ?) AS days_to_bdate, -- DATE_NOW
- YEAR(bdate) - YEAR(bday) AS age,
- email.usd_value AS email, gender.usd_value AS gender
- FROM ' . TBL_USERS . ' AS users
- INNER JOIN ( (SELECT usd_usr_id, usd_value AS bday,
- CONCAT(YEAR(?), DATE_FORMAT(bd1.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd1
- WHERE DATEDIFF(CONCAT(YEAR(?), DATE_FORMAT(bd1.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- UNION
- (SELECT usd_usr_id, usd_value AS bday,
- CONCAT(YEAR(?)-1, DATE_FORMAT(bd2.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd2
- WHERE DATEDIFF(CONCAT(YEAR(?)-1, DATE_FORMAT(bd2.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- UNION
- (SELECT usd_usr_id, usd_value AS bday,
- CONCAT(YEAR(?)+1, DATE_FORMAT(bd3.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
- FROM ' . TBL_USER_DATA . ' AS bd3
- WHERE DATEDIFF(CONCAT(YEAR(?)+1, DATE_FORMAT(bd3.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
- BETWEEN ? AND ? -- -$plg_show_zeitraum AND $plg_show_future
- AND usd_usf_id = ?) -- $fieldBirthday
- ) AS birthday
- ON birthday.usd_usr_id = usr_id
- LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
- ON last_name.usd_usr_id = usr_id
- AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
- ON first_name.usd_usr_id = usr_id
- AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS email
- ON email.usd_usr_id = usr_id
- AND email.usd_usf_id = ? -- $gProfileFields->getProperty(\'EMAIL\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS gender
- ON gender.usd_usr_id = usr_id
- AND gender.usd_usf_id = ? -- $gProfileFields->getProperty(\'GENDER\', \'usf_id\')
- LEFT JOIN ' . TBL_MEMBERS . '
- ON mem_usr_id = usr_id
- AND mem_begin <= ? -- DATE_NOW
- AND mem_end > ? -- DATE_NOW
- INNER JOIN ' . TBL_ROLES . '
- ON mem_rol_id = rol_id
- AND rol_valid = true
- INNER JOIN ' . TBL_CATEGORIES . '
- ON rol_cat_id = cat_id
- AND cat_org_id = ? -- $gCurrentOrgId
- WHERE usr_valid = true
- AND mem_rol_id ' . $sqlRoleIds . '
- ORDER BY days_to_bdate ' . $sqlSort . ', last_name, first_name';
- }
-
- $queryParams = array(
- DATE_NOW,
- DATE_NOW, DATE_NOW, DATE_NOW, -$plg_show_zeitraum, $plg_show_future, $fieldBirthday,
- DATE_NOW, DATE_NOW, DATE_NOW, -$plg_show_zeitraum, $plg_show_future, $fieldBirthday,
- DATE_NOW, DATE_NOW, DATE_NOW, -$plg_show_zeitraum, $plg_show_future, $fieldBirthday,
- $gProfileFields->getProperty('LAST_NAME', 'usf_id'),
- $gProfileFields->getProperty('FIRST_NAME', 'usf_id'),
- $gProfileFields->getProperty('EMAIL', 'usf_id'),
- $gProfileFields->getProperty('GENDER', 'usf_id'),
- DATE_NOW,
- DATE_NOW,
- $gCurrentOrgId
- // TODO add more params
- );
- $birthdayStatement = $gDb->queryPrepared($sql, $queryParams);
-
- $numberBirthdays = $birthdayStatement->rowCount();
-
- if ($numberBirthdays > 0) {
- if ($plgBirthdayShowNames) {
- // how many birthdays should be displayed (as a maximum)
- $birthdayCount = null;
- $birthdayArray = array();
-
- while (($row = $birthdayStatement->fetch()) && $birthdayCount < $plg_show_display_limit) {
- // the display type of the name
- switch ($plg_show_names) {
- case 1: // first name, last name
- $plgShowName = $row['first_name'] . ' ' . $row['last_name'];
- break;
- case 2: // last name, first name
- $plgShowName = $row['last_name'] . ', ' . $row['first_name'];
- break;
- case 3: // first name
- $plgShowName = $row['first_name'];
- break;
- case 4: // Loginname
- $plgShowName = $row['usr_login_name'];
- break;
- default: // first name, last name
- $plgShowName = $row['first_name'] . ' ' . $row['last_name'];
- }
-
- // from a specified age, only the last name with salutation is displayed for logged out visitors
- if (!$gValidLogin && $plg_show_alter_anrede <= $row['age']) {
- if ($row['gender'] > 1) {
- $plgShowName = $gL10n->get('PLG_BIRTHDAY_WOMAN_VAR', array($row['last_name']));
- } else {
- $plgShowName = $gL10n->get('PLG_BIRTHDAY_MAN_VAR', array($row['last_name']));
- }
- }
-
- // show name and e-mail link for registered users
- if ($gValidLogin) {
- $plgShowName = '' . $plgShowName . '';
-
- // E-Mail-Adresse ist hinterlegt und soll auch bei eingeloggten Benutzern verlinkt werden
- if ((string)$row['email'] !== '' && $plg_show_email_extern < 2) {
- $plgShowName .= '
- ' .
- '';
- }
- } elseif ($plg_show_email_extern === 1 && strlen($row['email']) > 0) {
- $plgShowName .= '
- ' .
- '';
- }
-
- // set css class and string for birthday today, in the future or in the past
- $birthdayDate = DateTime::createFromFormat('Y-m-d', $row['birthday']);
-
- if ($row['days_to_bdate'] < 0) {
- $plgCssClass = 'plgBirthdayNameHighlightAgo';
- if ($row['days_to_bdate'] == -1) {
- $birthdayText = 'PLG_BIRTHDAY_YESTERDAY';
- $plgDays = ' ';
- } else {
- $birthdayText = 'PLG_BIRTHDAY_PAST';
- $plgDays = -$row['days_to_bdate'];
- }
- } elseif ($row['days_to_bdate'] > 0) {
- $plgCssClass = 'plgBirthdayNameHighlightFuture';
- if ($row['days_to_bdate'] == 1) {
- $birthdayText = 'PLG_BIRTHDAY_TOMORROW';
- $plgDays = ' ';
- } else {
- $birthdayText = 'PLG_BIRTHDAY_FUTURE';
- $plgDays = $row['days_to_bdate'];
- }
- } else {
- $plgCssClass = 'plgBirthdayNameHighlight';
- $birthdayText = 'PLG_BIRTHDAY_TODAY';
- $plgDays = $row['age'];
- }
-
- // don't show age of birthday person if preference is set
- if ($plg_show_age === 0 || !$gValidLogin) {
- $birthdayText .= '_NO_AGE';
- }
-
- $birthdayArray[] = array(
- 'userText' => $gL10n->get($birthdayText, array($plgShowName, $plgDays, $row['age'], $birthdayDate->format($gSettingsManager->getString('system_date'))))
- );
-
- // counting displayed birthdays
- $birthdayCount++;
- }
-
- $birthdayPlugin->assignTemplateVariable('birthdays', $birthdayArray);
- } else {
- if ($numberBirthdays === 1) {
- $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_ONE_MEMBER'));
- } else {
- $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_MORE_MEMBERS', array($numberBirthdays)));
- }
- }
- } else {
- // If the configuration is set accordingly, a message is output if no member has a birthday today
- if (!$plg_show_hinweis_keiner) {
- $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_NO_MEMBERS'));
- }
- }
-
- if (isset($page)) {
- echo $birthdayPlugin->html('plugin.birthday.tpl');
- } else {
- $birthdayPlugin->showHtmlPage('plugin.birthday.tpl');
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/birthday/languages/en.xml b/adm_plugins/birthday/languages/en.xml
deleted file mode 100644
index ee2ee0db0a..0000000000
--- a/adm_plugins/birthday/languages/en.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- #VAR1# will be #VAR3# years old in #VAR2# days, on the #VAR4#
- #VAR1# has their birthday in #VAR2# days
- Birthdays
- Mr. #VAR1#
- Today #VAR1# members have birthday.
- Today is not a member's birthday.
- Today is a member's birthday.
- #VAR1# became #VAR3# years old #VAR2# days before, on the #VAR4#
- #VAR1# had their birthday #VAR2# days ago
- #VAR1# will be today #VAR2#
- #VAR1# has their birthday today
- #VAR1# will be tomorrow #VAR3# years old, on the #VAR4#
- #VAR1# has their birthday tomorrow
- Mrs. #VAR1#
- #VAR1# became #VAR3# years old yesterday on the #VAR4#
- #VAR1# had their birthday yesterday
-
diff --git a/adm_plugins/calendar/config_sample.php b/adm_plugins/calendar/config_sample.php
deleted file mode 100644
index 64dbdb9b71..0000000000
--- a/adm_plugins/calendar/config_sample.php
+++ /dev/null
@@ -1,65 +0,0 @@
- 0) {
- // current user must be member of at least one listed role
- if (count(array_intersect($plg_calendar_roles_view_plugin, $gCurrentUser->getRoleMemberships())) > 0) {
- $plgCalendarShowNames = true;
- }
- } else {
- // every member could view birthdays
- $plgCalendarShowNames = true;
- }
- } else {
- if ($plg_geb_login === 0) {
- // every visitor is allowed to view birthdays
- $plgCalendarShowNames = true;
- }
- }
-
- // check if role conditions where set
- if (empty($plg_rolle_sql)) {
- $sqlRoleIds = 'IS NOT NULL';
- } else {
- $sqlRoleIds = 'IN (' . implode(',', $plg_rolle_sql) . ')';
- }
-
- header('Content-Type: text/html; charset=utf-8');
-
- // initialize some variables
- $gebLink = '';
- $plgLink = '';
- $currentMonth = '';
- $currentYear = '';
- $today = 0;
-
- if ($getDateId !== '') {
- // Read Date ID or generate current month and year
- $currentMonth = substr($getDateId, 0, 2);
- $currentYear = substr($getDateId, 2, 4);
- $_SESSION['plugin_calendar_last_month'] = $currentMonth . $currentYear;
- } elseif (isset($_SESSION['plugin_calendar_last_month'])) {
- // Show last selected month
- $currentMonth = substr($_SESSION['plugin_calendar_last_month'], 0, 2);
- $currentYear = substr($_SESSION['plugin_calendar_last_month'], 2, 4);
- } else {
- // show current month
- $currentMonth = date('m');
- $currentYear = date('Y');
- }
-
- if ($currentMonth === date('m') && $currentYear === date('Y')) {
- $today = (int)date('d');
- }
-
- $lastDayCurrentMonth = (int)date('t', mktime(0, 0, 0, $currentMonth, 1, $currentYear));
- $dateMonthStart = $currentYear . '-' . $currentMonth . '-01 00:00:01'; // add 1 second to ignore all day events that end at 00:00:00
- $dateMonthEnd = $currentYear . '-' . $currentMonth . '-' . $lastDayCurrentMonth . ' 23:59:59';
- $eventsMonthDayArray = array();
- $birthdaysMonthDayArray = array();
-
- // query of all events
- if ($plg_ter_aktiv) {
- $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('EVT'));
- $queryParams = array_merge($catIdParams, array($dateMonthEnd, $dateMonthStart));
-
- // check if special calendars should be shown
- if (in_array('all', $plg_kal_cat, true)) {
- // show all calendars
- $sqlSyntax = '';
- } else {
- // show only calendars of the parameter $plg_kal_cat
- $sqlSyntax = ' AND cat_name IN (' . Database::getQmForValues($plg_kal_cat) . ')';
- $queryParams = array_merge($queryParams, $plg_kal_cat);
- }
-
- $sql = 'SELECT DISTINCT dat_id, dat_cat_id, cat_name, dat_begin, dat_end, dat_all_day, dat_location, dat_headline
- FROM ' . TBL_EVENTS . '
- INNER JOIN ' . TBL_CATEGORIES . '
- ON cat_id = dat_cat_id
- WHERE cat_id IN (' . Database::getQmForValues($catIdParams) . ')
- AND dat_begin <= ? -- $dateMonthEnd
- AND dat_end >= ? -- $dateMonthStart
- ' . $sqlSyntax . '
- ORDER BY dat_begin ASC';
- $datesStatement = $gDb->queryPrepared($sql, $queryParams);
-
- while ($row = $datesStatement->fetch()) {
- $startDate = new DateTime($row['dat_begin']);
- $endDate = new DateTime($row['dat_end']);
-
- // set custom name of plugin for calendar or use default Admidio name
- if ($plg_kal_cat_show) {
- if ($row['cat_name'][3] === '_') {
- $calendarName = $gL10n->get($row['cat_name']);
- } else {
- $calendarName = $row['cat_name'];
- }
- $row['dat_headline'] = $calendarName . ': ' . $row['dat_headline'];
- }
-
- if ($startDate->format('Y-m-d') === $endDate->format('Y-m-d')) {
- // event only within one day
- $eventsMonthDayArray[$startDate->format('j')][] = array(
- 'dat_id' => $row['dat_id'],
- 'time' => $startDate->format($gSettingsManager->getString('system_time')),
- 'all_day' => $row['dat_all_day'],
- 'location' => $row['dat_location'],
- 'headline' => $row['dat_headline'],
- 'one_day' => true
- );
- } else {
- // event within several days
-
- if ($startDate->format('m') !== $currentMonth) {
- $firstDay = 1;
- } else {
- $firstDay = $startDate->format('j');
- }
-
- if ($endDate->format('m') !== $currentMonth) {
- $lastDay = $lastDayCurrentMonth;
- } else {
- $lastDay = $endDate->format('j');
- }
-
- // now add event to every relevant day of month
- for ($i = $firstDay; $i <= $lastDay; ++$i) {
- $eventsMonthDayArray[$i][] = array(
- 'dat_id' => $row['dat_id'],
- 'time' => $startDate->format($gSettingsManager->getString('system_time')),
- 'all_day' => $row['dat_all_day'],
- 'location' => $row['dat_location'],
- 'headline' => $row['dat_headline'],
- 'one_day' => false
- );
- }
- }
- }
- }
-
- // query of all birthdays
- if ($plg_geb_aktiv) {
- if (DB_ENGINE === Database::PDO_ENGINE_PGSQL) {
- $sqlYearOfBirthday = ' EXTRACT(YEAR FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
- $sqlMonthOfBirthday = ' EXTRACT(MONTH FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
- $sqlDayOfBirthday = ' EXTRACT(DAY FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
- } else {
- $sqlYearOfBirthday = ' YEAR(birthday.usd_value) ';
- $sqlMonthOfBirthday = ' MONTH(birthday.usd_value) ';
- $sqlDayOfBirthday = ' DayOfMonth(birthday.usd_value) ';
- }
-
- switch ($plg_geb_displayNames) {
- case 1:
- $sqlOrderName = 'first_name';
- break;
- case 2:
- $sqlOrderName = 'last_name';
- break;
- case 0: // fallthrough
- default:
- $sqlOrderName = 'last_name, first_name';
- }
-
- // database query for all birthdays of this month
- $sql = 'SELECT DISTINCT
- usr_id, last_name.usd_value AS last_name, first_name.usd_value AS first_name, birthday.usd_value AS birthday,
- ' . $sqlYearOfBirthday . ' AS birthday_year, ' . $sqlMonthOfBirthday . ' AS birthday_month,
- ' . $sqlDayOfBirthday . ' AS birthday_day
- FROM ' . TBL_MEMBERS . '
- INNER JOIN ' . TBL_ROLES . '
- ON rol_id = mem_rol_id
- INNER JOIN ' . TBL_CATEGORIES . '
- ON cat_id = rol_cat_id
- INNER JOIN ' . TBL_USERS . '
- ON usr_id = mem_usr_id
- INNER JOIN ' . TBL_USER_DATA . ' AS birthday
- ON birthday.usd_usr_id = usr_id
- AND birthday.usd_usf_id = ? -- $gProfileFields->getProperty(\'BIRTHDAY\', \'usf_id\')
- AND ' . $sqlMonthOfBirthday . ' = ? -- $currentMonth
- LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
- ON last_name.usd_usr_id = usr_id
- AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
- LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
- ON first_name.usd_usr_id = usr_id
- AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
- WHERE usr_valid = true
- AND cat_org_id = ? -- $gCurrentOrgId
- AND rol_id ' . $sqlRoleIds . '
- AND mem_begin <= ? -- DATE_NOW
- AND mem_end > ? -- DATE_NOW
- ORDER BY birthday_year DESC, birthday_month DESC, birthday_day DESC, ' . $sqlOrderName;
-
- $queryParams = array(
- $gProfileFields->getProperty('BIRTHDAY', 'usf_id'),
- $currentMonth,
- $gProfileFields->getProperty('LAST_NAME', 'usf_id'),
- $gProfileFields->getProperty('FIRST_NAME', 'usf_id'),
- $gCurrentOrgId,
- DATE_NOW,
- DATE_NOW
- );
- $birthdayStatement = $gDb->queryPrepared($sql, $queryParams);
-
- while ($row = $birthdayStatement->fetch()) {
- $birthdayDate = new DateTime($row['birthday']);
-
- switch ($plg_geb_displayNames) {
- case 1:
- $name = $row['first_name'];
- break;
- case 2:
- $name = $row['last_name'];
- break;
- case 0: // fallthrough
- default:
- $name = $row['last_name'] . ($row['last_name'] ? ', ' : '') . $row['first_name'];
- }
-
- $birthdaysMonthDayArray[$birthdayDate->format('j')][] = array(
- 'year' => $birthdayDate->format('Y'),
- 'age' => $currentYear - $birthdayDate->format('Y'),
- 'name' => $name
- );
- }
- }
-
- // Kalender erstellen
- $firstWeekdayOfMonth = (int)date('w', mktime(0, 0, 0, $currentMonth, 1, $currentYear));
- $months = explode(',', $gL10n->get('PLG_CALENDAR_MONTH'));
-
- if ($firstWeekdayOfMonth === 0) {
- $firstWeekdayOfMonth = 7;
- }
-
- $tableContent = '
';
- $i = 1;
- while ($i < $firstWeekdayOfMonth) {
- $tableContent .= '| | ';
- ++$i;
- }
-
- $currentDay = 1;
- $boolNewStart = false;
-
- while ($currentDay <= $lastDayCurrentMonth) {
- $terLink = '';
- $gebLink = '';
- $htmlContent = '';
- $textContent = '';
- $hasEvents = false;
- $hasBirthdays = false;
- $countEvents = 0;
-
- $dateObj = DateTime::createFromFormat('Y-m-j', $currentYear . '-' . $currentMonth . '-' . $currentDay);
-
- // add events to the calendar
- if ($plg_ter_aktiv) {
- // only show events in dependence of the events module view settings
- if (array_key_exists($currentDay, $eventsMonthDayArray)
- && ($gSettingsManager->getInt('events_module_enabled') === 1
- || ($gSettingsManager->getInt('events_module_enabled') === 2 && $gValidLogin))) {
- $hasEvents = true;
-
- foreach ($eventsMonthDayArray[$currentDay] as $eventArray) {
- if ($eventArray['location'] !== '') {
- $eventArray['location'] = ', ' . $eventArray['location'];
- }
-
- if ($htmlContent !== '') {
- $htmlContent .= '
';
- }
- if ($eventArray['all_day'] == 1) {
- if ($eventArray['one_day']) {
- $htmlContent .= '' . $gL10n->get('SYS_ALL_DAY') . ' ' . $eventArray['headline'] . $eventArray['location'];
- $textContent .= $gL10n->get('SYS_ALL_DAY') . ' ' . $eventArray['headline'] . $eventArray['location'];
- } else {
- $htmlContent .= '' . $gL10n->get('PLG_CALENDAR_SEVERAL_DAYS') . ' ' . $eventArray['headline'] . $eventArray['location'];
- $textContent .= $gL10n->get('PLG_CALENDAR_SEVERAL_DAYS') . ' ' . $eventArray['headline'] . $eventArray['location'];
- }
- } else {
- $htmlContent .= '' . $eventArray['time'] . ' ' . $gL10n->get('SYS_CLOCK') . ' ' . $eventArray['headline'] . $eventArray['location'];
- $textContent .= $eventArray['time'] . ' ' . $gL10n->get('SYS_CLOCK') . ' ' . $eventArray['headline'] . $eventArray['location'];
- }
- ++$countEvents;
- }
-
- if ($countEvents > 0) {
- $plgLink = SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/events/events.php', array('date_from' => $dateObj->format('Y-m-d'), 'date_to' => $dateObj->format('Y-m-d')));
- }
- }
- }
-
- // add users birthdays to the calendar
- if ($plg_geb_aktiv) {
- if (array_key_exists($currentDay, $birthdaysMonthDayArray) && $plgCalendarShowNames) {
- foreach ($birthdaysMonthDayArray[$currentDay] as $birthdayArray) {
- $hasBirthdays = true;
-
- if ($htmlContent !== '') {
- $htmlContent .= '
';
- $textContent .= ', ';
- }
-
- if ($plg_geb_icon) {
- $icon = '';
- } else {
- $icon = '';
- }
-
- $htmlContent .= $icon . $birthdayArray['name'] . ' (' . $birthdayArray['age'] . ')';
- $textContent .= $birthdayArray['name'] . ' (' . $birthdayArray['age'] . ')';
- }
- }
- }
-
- // First pre-assignment of the weekday classes
- $plgLinkClassSaturday = 'plgCalendarSaturday';
- $plgLinkClassSunday = 'plgCalendarSunday';
- $plgLinkClassWeekday = 'plgCalendarDay';
-
- if (!$hasEvents && $hasBirthdays) { // no events but birthdays
- $plgLinkClass = 'geb';
- $plgLinkClassSaturday .= ' plgCalendarBirthDay';
- $plgLinkClassSunday .= ' plgCalendarBirthDay';
- $plgLinkClassWeekday .= ' plgCalendarBirthDay';
- }
-
- if ($hasEvents && !$hasBirthdays) { // events but no birthdays
- $plgLinkClass = 'date';
- $plgLinkClassSaturday .= ' plgCalendarDateDay';
- $plgLinkClassSunday .= ' plgCalendarDateDay';
- $plgLinkClassWeekday .= ' plgCalendarDateDay';
- }
-
- if ($hasEvents && $hasBirthdays) { // events and birthdays
- $plgLinkClass = 'merge';
- $plgLinkClassSaturday .= ' plgCalendarMergeDay';
- $plgLinkClassSunday .= ' plgCalendarMergeDay';
- $plgLinkClassWeekday .= ' plgCalendarMergeDay';
- }
-
- if ($boolNewStart) {
- $tableContent .= '
';
- $boolNewStart = false;
- }
- $rest = ($currentDay + $firstWeekdayOfMonth - 1) % 7;
- if ($currentDay === $today) {
- $tableContent .= '| ';
- } elseif ($rest === 6) {
- $tableContent .= ' | ';
- } elseif ($rest === 0) {
- $tableContent .= ' | ';
- } else {
- $tableContent .= ' | ';
- }
-
- if ($currentDay === $today || $hasEvents || $hasBirthdays) {
- if (!$hasEvents && $hasBirthdays) {
- // Switch off link URL for birthday by #.
- $plgLink = '#';
- }
-
- if ($hasEvents || $hasBirthdays) {
- if ($terLink !== '' && $gebLink !== '') {
- $gebLink = '&' . $gebLink;
- }
-
- // plg_link_class bestimmt das Erscheinungsbild des jeweiligen Links
- $tableContent .= '' . $currentDay . '';
- } elseif ($currentDay === $today) {
- $tableContent .= '' . $currentDay . '';
- }
- } elseif ($rest === 6) {
- $tableContent .= '' . $currentDay . '';
- } elseif ($rest === 0) {
- $tableContent .= '' . $currentDay . '';
- } else {
- $tableContent .= $currentDay;
- }
- $tableContent .= ' | ';
- if ($rest === 0 || $currentDay === $lastDayCurrentMonth) {
- $tableContent .= '
';
- $boolNewStart = true;
- }
-
- ++$currentDay;
- }
-
- $calendarPlugin->assignTemplateVariable('pluginFolder', $pluginFolder);
- $calendarPlugin->assignTemplateVariable('monthYearHeadline', $months[(int) $currentMonth - 1] . ' ' . $currentYear);
- $calendarPlugin->assignTemplateVariable('monthYear', $currentMonth . $currentYear);
- $calendarPlugin->assignTemplateVariable('currentMonthYear', date('mY'));
- $calendarPlugin->assignTemplateVariable('dateIdLastMonth', date('mY', mktime(0, 0, 0, $currentMonth - 1, 1, $currentYear)));
- $calendarPlugin->assignTemplateVariable('dateIdNextMonth', date('mY', mktime(0, 0, 0, $currentMonth + 1, 1, $currentYear)));
- $calendarPlugin->assignTemplateVariable('tableContent', $tableContent);
-
- if (isset($page) || $getDateId !== '') {
- echo $calendarPlugin->html('plugin.calendar.tpl');
- } else {
- $calendarPlugin->showHtmlPage('plugin.calendar.tpl');
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/calendar/languages/en.xml b/adm_plugins/calendar/languages/en.xml
deleted file mode 100644
index bfa4ded60b..0000000000
--- a/adm_plugins/calendar/languages/en.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- Current month
- January,February,March,April,May,June,July,August,September,October,November,December
- Mo
- Tu
- We
- Th
- Fr
- Sa
- Su
- Several days
-
diff --git a/adm_plugins/event-list/config_sample.php b/adm_plugins/event-list/config_sample.php
deleted file mode 100644
index 184266abaa..0000000000
--- a/adm_plugins/event-list/config_sample.php
+++ /dev/null
@@ -1,41 +0,0 @@
-getInt('events_module_enabled') > 0) {
- // read events from database
- $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('EVT'));
-
- $sql = 'SELECT cat.*, evt.*
- FROM ' . TBL_EVENTS . ' AS evt
- INNER JOIN ' . TBL_CATEGORIES . ' AS cat
- ON cat_id = dat_cat_id
- WHERE cat_id IN (' . Database::getQmForValues($catIdParams) . ')
- AND dat_end >= ? -- DATETIME_NOW
- ' . $plgSqlCategories . '
- ORDER BY dat_begin
- LIMIT ' . $plg_max_number_events_shown;
-
- $pdoStatement = $gDb->queryPrepared($sql, array_merge($catIdParams, array(DATETIME_NOW), $plg_kal_cat));
- $plgEventList = $pdoStatement->fetchAll();
-
- if ($gSettingsManager->getInt('events_module_enabled') === 1
- || ($gSettingsManager->getInt('events_module_enabled') === 2 && $gValidLogin)) {
- if ($pdoStatement->rowCount() > 0) {
- // get announcements data
- $plgEvent = new Event($gDb);
- $eventArray = array();
-
- foreach ($plgEventList as $plgRow) {
- $plgEvent->clear();
- $plgEvent->setArray($plgRow);
-
- if ($plg_max_char_per_word > 0) {
- $plgNewHeadline = '';
-
- // Pause words if they are too long
- $plgWords = explode(' ', $plgEvent->getValue('dat_headline'));
-
- foreach ($plgWords as $plgValue) {
- if (strlen($plgValue) > $plg_max_char_per_word) {
- $plgNewHeadline .= ' ' . substr($plgValue, 0, $plg_max_char_per_word) . '-
' .
- substr($plgValue, $plg_max_char_per_word);
- } else {
- $plgNewHeadline .= ' ' . $plgValue;
- }
- }
- } else {
- $plgNewHeadline = $plgEvent->getValue('dat_headline');
- }
-
- // show preview text
- if ($plgShowFullDescription === 1) {
- $plgNewDescription = $plgEvent->getValue('dat_description');
- } elseif ($plg_events_show_preview > 0) {
- // remove all html tags except some format tags
- $plgNewDescription = strip_tags($plgEvent->getValue('dat_description'));
-
- // read first x chars of text and additional 15 chars. Then search for last space and cut the text there
- $plgNewDescription = substr($plgNewDescription, 0, $plg_events_show_preview + 15);
- $plgNewDescription = substr($plgNewDescription, 0, strrpos($plgNewDescription, ' ')) . '
- »';
- }
-
- $eventArray[] = array(
- 'uuid' => $plgEvent->getValue('dat_uuid'),
- 'dateTimePeriod' => $plgEvent->getDateTimePeriod($plg_show_date_end),
- 'headline' => $plgNewHeadline,
- 'description' => $plgNewDescription
- );
- }
-
- $eventListPlugin->assignTemplateVariable('events', $eventArray);
- } else {
- $eventListPlugin->assignTemplateVariable('message',$gL10n->get('SYS_NO_ENTRIES'));
- }
- } else {
- $eventListPlugin->assignTemplateVariable('message',$gL10n->get('PLG_EVENT_LIST_NO_ENTRIES_VISITORS'));
- }
-
- if (isset($page)) {
- echo $eventListPlugin->html('plugin.event-list.tpl');
- } else {
- $eventListPlugin->showHtmlPage('plugin.event-list.tpl');
- }
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/event-list/languages/en.xml b/adm_plugins/event-list/languages/en.xml
deleted file mode 100644
index 1a8d72b8e9..0000000000
--- a/adm_plugins/event-list/languages/en.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- All events
- Events
- There are no events available for visitors. Log in to see the events.
-
diff --git a/adm_plugins/latest-documents-files/config_sample.php b/adm_plugins/latest-documents-files/config_sample.php
deleted file mode 100644
index 1141e77f71..0000000000
--- a/adm_plugins/latest-documents-files/config_sample.php
+++ /dev/null
@@ -1,24 +0,0 @@
-readDataByColumns(array('fol_org_id' => $gCurrentOrgId,
- 'fol_fol_id_parent' => 'NULL',
- 'fol_type' => 'DOCUMENTS'));
- $downloadFolder = $rootFolder->getValue('fol_path') . '/' . $rootFolder->getValue('fol_name');
-
- // read all downloads from database and then check the rights for each download
- $sql = 'SELECT fil_timestamp, fil_name, fil_usr_id, fol_name, fol_path, fil_id, fil_fol_id, fil_uuid
- FROM ' . TBL_FILES . '
- INNER JOIN ' . TBL_FOLDERS . '
- ON fol_id = fil_fol_id
- WHERE fol_org_id = ? -- $gCurrentOrgId
- ' . $sqlCondition . '
- ORDER BY fil_timestamp DESC';
-
- $filesStatement = $gDb->queryPrepared($sql, array($gCurrentOrgId));
-
- if ($filesStatement->rowCount() > 0) {
- $documentsFilesArray = array();
-
- while ($rowFile = $filesStatement->fetch()) {
- try {
- // get recordset of current file from database
- $file = new File($gDb);
- $file->getFileForDownload($rowFile['fil_uuid']);
-
- // get filename without extension and extension separately
- $fileName = pathinfo($rowFile['fil_name'], PATHINFO_FILENAME);
- $fullFolderFileName = $rowFile['fol_path'] . '/' . $rowFile['fol_name'] . '/' . $rowFile['fil_name'];
- $tooltip = str_replace($downloadFolder, $gL10n->get('SYS_DOCUMENTS_FILES'), $fullFolderFileName);
- ++$countVisibleDownloads;
-
- // if max chars are set then limit characters of shown filename
- if ($plgMaxCharsFilename > 0 && strlen($fileName) > $plgMaxCharsFilename) {
- $fileName = substr($fileName, 0, $plgMaxCharsFilename) . '...';
- }
-
- // if set in config file then show timestamp of file upload
- if ($plg_show_upload_timestamp) {
- // Vorname und Nachname abfragen (Upload der Datei)
- $user = new User($gDb, $gProfileFields, $rowFile['fil_usr_id']);
-
- $tooltip .= '
' . $gL10n->get('PLG_LATEST_FILES_UPLOAD_FROM_AT', array($user->getValue('FIRST_NAME') . ' ' . $user->getValue('LAST_NAME'), $file->getValue('fil_timestamp')));
- }
-
- $documentsFilesArray[] = array(
- 'uuid' => $rowFile['fil_uuid'],
- 'icon' => $file->getIcon(),
- 'fileName' => $fileName,
- 'fileExtension' => $file->getFileExtension(),
- 'tooltip' => $tooltip
- );
-
- if ($countVisibleDownloads === $plgCountFiles) {
- break;
- }
- } catch (Exception $e) {
- // do nothing and go to next file
- }
- }
- $latestDocumentsFilesPlugin->assignTemplateVariable('documentsFiles', $documentsFilesArray);
- }
- }
-
- if ($countVisibleDownloads === 0) {
- if ($gValidLogin) {
- $latestDocumentsFilesPlugin->assignTemplateVariable('message',$gL10n->get('PLG_LATEST_FILES_NO_DOWNLOADS_AVAILABLE'));
- } else {
- $latestDocumentsFilesPlugin->assignTemplateVariable('message',$gL10n->get('SYS_FOLDER_NO_FILES_VISITOR'));
- }
- }
-
- if (isset($page)) {
- echo $latestDocumentsFilesPlugin->html('plugin.latest-documents-files.tpl');
- } else {
- $latestDocumentsFilesPlugin->showHtmlPage('plugin.latest-documents-files.tpl');
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/latest-documents-files/languages/bg.xml b/adm_plugins/latest-documents-files/languages/bg.xml
deleted file mode 100644
index f18963078b..0000000000
--- a/adm_plugins/latest-documents-files/languages/bg.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Документи и файлове
- Още документи и файлове
- Няма налични документи и файлове.
- Качен от #VAR1# на #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/da.xml b/adm_plugins/latest-documents-files/languages/da.xml
deleted file mode 100644
index 784994447b..0000000000
--- a/adm_plugins/latest-documents-files/languages/da.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumenter & filer
- Flere dokumenter & filer
- Der er ikke nogen dokumenter eller filer tilgængelige.
- Uploadet af #VAR1# den #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/de-DE.xml b/adm_plugins/latest-documents-files/languages/de-DE.xml
deleted file mode 100644
index e20097be4d..0000000000
--- a/adm_plugins/latest-documents-files/languages/de-DE.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumente & Dateien
- Weitere Dokumente & Dateien
- Es stehen keine Dateien zum Download zur Verfügung.
- Hochgeladen von #VAR1# am #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/de.xml b/adm_plugins/latest-documents-files/languages/de.xml
deleted file mode 100644
index 6283f1ba24..0000000000
--- a/adm_plugins/latest-documents-files/languages/de.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumente & Dateien
- Weitere Dokumente & Dateien
- Es stehen keine Dokumente oder Dateien zur Verfügung.
- Hochgeladen von #VAR1# am #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/el.xml b/adm_plugins/latest-documents-files/languages/el.xml
deleted file mode 100644
index 57ab958fa3..0000000000
--- a/adm_plugins/latest-documents-files/languages/el.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Έγγραφα & αρχεία
- Περισσότερα Έγγραφα & αρχεία
- Δεν υπάρχουν διαθέσιμα έγγραφα ή αρχεία.
- Ανέβηκε από #VAR1# στις #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/en.xml b/adm_plugins/latest-documents-files/languages/en.xml
deleted file mode 100644
index 38719db6b8..0000000000
--- a/adm_plugins/latest-documents-files/languages/en.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Documents & files
- More documents & files
- There are no documents or files available.
- Uploaded by #VAR1# on #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/es.xml b/adm_plugins/latest-documents-files/languages/es.xml
deleted file mode 100644
index af71a5e0c2..0000000000
--- a/adm_plugins/latest-documents-files/languages/es.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Descargas
- Más descargas
- No hay archivos disponibles para descargar.
- Subido de #VAR1# el #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/et.xml b/adm_plugins/latest-documents-files/languages/et.xml
deleted file mode 100644
index 3df1d13559..0000000000
--- a/adm_plugins/latest-documents-files/languages/et.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumendid & failid
- Rohkem dokumente & failid
- Dokumente ega faile pole saadaval.
- #VAR1# laadis üles #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/fi.xml b/adm_plugins/latest-documents-files/languages/fi.xml
deleted file mode 100755
index 5ce2f7b916..0000000000
--- a/adm_plugins/latest-documents-files/languages/fi.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumentteja & tiedostoja
- Lisää dokumentteja & tiedostoja
- Asiakirjoja tai tiedostoja ei ole saatavilla.
- Lataaja #VAR1# pävämäärällä #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/fr.xml b/adm_plugins/latest-documents-files/languages/fr.xml
deleted file mode 100644
index 3069f2a750..0000000000
--- a/adm_plugins/latest-documents-files/languages/fr.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Documents & des dossiers
- Plus de documents & des dossiers
- Il n\'y a pas de documents ou fichiers disponibles.
- Téléchargé de #VAR1# le #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/hu.xml b/adm_plugins/latest-documents-files/languages/hu.xml
deleted file mode 100644
index b57ee49cbe..0000000000
--- a/adm_plugins/latest-documents-files/languages/hu.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumentumok és fájlok
- További dokumentumok és fájlok
- Nincsenek dokumentumok vagy fájlok.
- Feltöltötte #VAR1# a #VAR2# időpontban
-
diff --git a/adm_plugins/latest-documents-files/languages/nb.xml b/adm_plugins/latest-documents-files/languages/nb.xml
deleted file mode 100644
index 2ca7641776..0000000000
--- a/adm_plugins/latest-documents-files/languages/nb.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumenter og filer
- Flere dokumenter og filer
- Ingen dokumenter tilgjengelig
- Lastet opp av #VAR1# den #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/nl.xml b/adm_plugins/latest-documents-files/languages/nl.xml
deleted file mode 100644
index 29015d8bff..0000000000
--- a/adm_plugins/latest-documents-files/languages/nl.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Documenten & bestanden
- Meer documenten & bestanden
- Er zijn geen documenten of bestanden beschikbaar.
- Geüpload door #VAR1# op #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/pl.xml b/adm_plugins/latest-documents-files/languages/pl.xml
deleted file mode 100644
index 115a59a23a..0000000000
--- a/adm_plugins/latest-documents-files/languages/pl.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokumenty i pliki
- Więcej dokumentów i plików
- Brak dostępnych dokumentów i plików.
- Przesłane przez #VAR1# w dniu #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/pt-BR.xml b/adm_plugins/latest-documents-files/languages/pt-BR.xml
deleted file mode 100644
index 1226537142..0000000000
--- a/adm_plugins/latest-documents-files/languages/pt-BR.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Downloads
- Mais downloads
- Não há arquivos disponíveis para baixar.
- Enviado por #VAR1# em #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/pt.xml b/adm_plugins/latest-documents-files/languages/pt.xml
deleted file mode 100644
index 0cad719a7d..0000000000
--- a/adm_plugins/latest-documents-files/languages/pt.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Documentos e Ficheiros
- Mais documentos e ficheiros
- Não existem documentos ou ficheiros disponíveis
- Enviado por #VAR1# em #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/ru.xml b/adm_plugins/latest-documents-files/languages/ru.xml
deleted file mode 100644
index 9a01807cc3..0000000000
--- a/adm_plugins/latest-documents-files/languages/ru.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Документы и файлы
- Больше документов и файлов
- Нет документов или файлов.
- Загружено #VAR1# #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/sv.xml b/adm_plugins/latest-documents-files/languages/sv.xml
deleted file mode 100644
index 101584f029..0000000000
--- a/adm_plugins/latest-documents-files/languages/sv.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Dokument & filer
- Fler dokument & filer
- Det finns inga dokument eller filer tillgängliga.
- Uppladdat av #VAR1# på #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/uk.xml b/adm_plugins/latest-documents-files/languages/uk.xml
deleted file mode 100644
index 73a4672495..0000000000
--- a/adm_plugins/latest-documents-files/languages/uk.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- Документи & файли
- Більше документів & файлів
- Немає доступних документів чи файлів.
- Завантажено #VAR1# #VAR2#
-
diff --git a/adm_plugins/latest-documents-files/languages/zh.xml b/adm_plugins/latest-documents-files/languages/zh.xml
deleted file mode 100644
index f66c1319b7..0000000000
--- a/adm_plugins/latest-documents-files/languages/zh.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- 下载
- 更多下载
- 没有可供下载的文件。
- 上传者: #VAR1# 在 #VAR2#
-
diff --git a/adm_plugins/login_form/config_sample.php b/adm_plugins/login_form/config_sample.php
deleted file mode 100644
index a724e42eae..0000000000
--- a/adm_plugins/login_form/config_sample.php
+++ /dev/null
@@ -1,38 +0,0 @@
- $gL10n->get('PLG_LOGIN_NEW_ONLINE_MEMBER'),
- '50' => $gL10n->get('PLG_LOGIN_ONLINE_MEMBER'),
- '100' => $gL10n->get('PLG_LOGIN_SENIOR_ONLINE_MEMBER'),
- '200' => $gL10n->get('PLG_LOGIN_HONORARY_MEMBER')
- );
diff --git a/adm_plugins/login_form/languages/bg.xml b/adm_plugins/login_form/languages/bg.xml
deleted file mode 100644
index 7f07d30bbd..0000000000
--- a/adm_plugins/login_form/languages/bg.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Активен от
- Хоноруван участник
- Последно влизане
- Нов онлайн участник
- Брой влизания
- Онлайн участник
- Старши онлайн участник
-
diff --git a/adm_plugins/login_form/languages/da.xml b/adm_plugins/login_form/languages/da.xml
deleted file mode 100644
index 51526f8023..0000000000
--- a/adm_plugins/login_form/languages/da.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiv siden
- Æresmedlem
- Sidste login
- Nyt online medlem
- Antal logins
- Online medlem
- Senior online medlem
-
diff --git a/adm_plugins/login_form/languages/de-DE.xml b/adm_plugins/login_form/languages/de-DE.xml
deleted file mode 100644
index 2d979f05c2..0000000000
--- a/adm_plugins/login_form/languages/de-DE.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiv seit
- Ehrenmitglied
- Letzter Login
- Neues Onlinemitglied
- Anzahl Logins
- Onlinemitglied
- Senior Onlinemitglied
-
diff --git a/adm_plugins/login_form/languages/de.xml b/adm_plugins/login_form/languages/de.xml
deleted file mode 100644
index 2d979f05c2..0000000000
--- a/adm_plugins/login_form/languages/de.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiv seit
- Ehrenmitglied
- Letzter Login
- Neues Onlinemitglied
- Anzahl Logins
- Onlinemitglied
- Senior Onlinemitglied
-
diff --git a/adm_plugins/login_form/languages/el.xml b/adm_plugins/login_form/languages/el.xml
deleted file mode 100644
index 7f68b8dd43..0000000000
--- a/adm_plugins/login_form/languages/el.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Ενεργός από
- Επίτιμο μέλος
- Τελευταία είσοδος
- Νέο online μέλος
- Αριθμός εισόδων
- Online μέλος
- Παλαιότερο online μέλος
-
diff --git a/adm_plugins/login_form/languages/en.xml b/adm_plugins/login_form/languages/en.xml
deleted file mode 100644
index c84d77c370..0000000000
--- a/adm_plugins/login_form/languages/en.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Active since
- Honorary member
- Last login
- New online member
- Number of logins
- Online member
- Senior online member
-
diff --git a/adm_plugins/login_form/languages/es.xml b/adm_plugins/login_form/languages/es.xml
deleted file mode 100644
index 53f2483a74..0000000000
--- a/adm_plugins/login_form/languages/es.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Activo desde
- Miembro honorario
- Última sesión
- Nuevo miembro en línea
- Número de sesiónes
- Miembro en línea
- Miembro mayor en línea
-
diff --git a/adm_plugins/login_form/languages/et.xml b/adm_plugins/login_form/languages/et.xml
deleted file mode 100644
index b2ec6f219a..0000000000
--- a/adm_plugins/login_form/languages/et.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiivne alates
- Auliige
- Viimane sisselogimine
- Uus võrguliige
- Sisselogimiste arv
- Online liige
- Vanem võrguliige
-
diff --git a/adm_plugins/login_form/languages/fi.xml b/adm_plugins/login_form/languages/fi.xml
deleted file mode 100755
index 903c167c68..0000000000
--- a/adm_plugins/login_form/languages/fi.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiivinen
- Kunniajäsen
- Viime kirjautuminen
- uusi on-line jäsen
- Kirjautumisia
- On-line jäsen
- Vanhempi on-line jäsen
-
diff --git a/adm_plugins/login_form/languages/fr.xml b/adm_plugins/login_form/languages/fr.xml
deleted file mode 100644
index 6903ebc2c3..0000000000
--- a/adm_plugins/login_form/languages/fr.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Actif depuis
- Membre honoraire
- Dernière connexion
- Nouveau membre en ligne
- Nombre de connexions
- Membre en ligne
- Membre senior en ligne
-
diff --git a/adm_plugins/login_form/languages/hu.xml b/adm_plugins/login_form/languages/hu.xml
deleted file mode 100644
index bb58ab8a77..0000000000
--- a/adm_plugins/login_form/languages/hu.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktív
- Tiszteletbeli tag
- Utolsó bejelentkezés
- Új online tag
- Bejelentkezések száma
- Online tag
- Rangidős online tag
-
diff --git a/adm_plugins/login_form/languages/it.xml b/adm_plugins/login_form/languages/it.xml
deleted file mode 100644
index 2ae9f6ee3d..0000000000
--- a/adm_plugins/login_form/languages/it.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Attivo da
- Membro onorario
- Ultimo accesso
- Nuovo membro online
- Numero di accessi
- Membro online
- Senior membro online
-
diff --git a/adm_plugins/login_form/languages/nb.xml b/adm_plugins/login_form/languages/nb.xml
deleted file mode 100644
index 0f97241d24..0000000000
--- a/adm_plugins/login_form/languages/nb.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiv siden
- Æresmedlem
- Siste innlogging
- Nytt onlinemedlem
- Antall pålogginger
- Onlinemedlem
- Senior onlinemedlem
-
diff --git a/adm_plugins/login_form/languages/nl.xml b/adm_plugins/login_form/languages/nl.xml
deleted file mode 100644
index 53a00e356a..0000000000
--- a/adm_plugins/login_form/languages/nl.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Actief sinds
- Erelid
- Laatst ingelogd
- Nieuw lid online
- Aantal keer ingelogd
- Online lid
- Online senior lid
-
diff --git a/adm_plugins/login_form/languages/pl.xml b/adm_plugins/login_form/languages/pl.xml
deleted file mode 100644
index c79c0b5e22..0000000000
--- a/adm_plugins/login_form/languages/pl.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktywny/a od
- Honorowy członek
- Ostatnie logowanie
- Nowy członek online
- Ilość logowań
- Członek online
- Przełożony online
-
diff --git a/adm_plugins/login_form/languages/pt-BR.xml b/adm_plugins/login_form/languages/pt-BR.xml
deleted file mode 100644
index b3263cdb08..0000000000
--- a/adm_plugins/login_form/languages/pt-BR.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Ativo desde
- Membro honorário
- Última Sessão
- Novo Membro Ativo
- Número de Sessões
- Membro Ativo
- Membro Sénior Ativo
-
diff --git a/adm_plugins/login_form/languages/pt.xml b/adm_plugins/login_form/languages/pt.xml
deleted file mode 100644
index f4337829b1..0000000000
--- a/adm_plugins/login_form/languages/pt.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Ativo desde
- Membro honorário
- Última sessão
- Novo membro on-line
- Número de sessões
- Membro on-line
- Membro sénior on-line
-
diff --git a/adm_plugins/login_form/languages/ru.xml b/adm_plugins/login_form/languages/ru.xml
deleted file mode 100644
index 3697229b65..0000000000
--- a/adm_plugins/login_form/languages/ru.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Активен с
- Почетный участник
- Последний вход
- Новый участник
- Количество входов
- Участник
- Старший участник
-
diff --git a/adm_plugins/login_form/languages/sv.xml b/adm_plugins/login_form/languages/sv.xml
deleted file mode 100644
index cd2810720f..0000000000
--- a/adm_plugins/login_form/languages/sv.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Aktiv från
- Hedersmedlem
- Senaste login
- Ny online-medlem
- Antal logins
- Online-medlem
- Senior online-medlem
-
diff --git a/adm_plugins/login_form/languages/uk.xml b/adm_plugins/login_form/languages/uk.xml
deleted file mode 100644
index 632bcdc5df..0000000000
--- a/adm_plugins/login_form/languages/uk.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Активний з
- Почесний член
- Останній вхід
- Новий онлайн-учасник
- Кількість логінів
- Онлайн-учасник
- Старший онлайн-учасник
-
diff --git a/adm_plugins/login_form/languages/zh.xml b/adm_plugins/login_form/languages/zh.xml
deleted file mode 100644
index 14e4a47b2b..0000000000
--- a/adm_plugins/login_form/languages/zh.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- 活跃至今
- 荣誉会员
- 上次登录
- 新的在线会员
- 登录次数
- 在线会员
- 高级在线会员
-
diff --git a/adm_plugins/random_photo/config_sample.php b/adm_plugins/random_photo/config_sample.php
deleted file mode 100644
index 686b2e1fd1..0000000000
--- a/adm_plugins/random_photo/config_sample.php
+++ /dev/null
@@ -1,43 +0,0 @@
-getInt('photo_module_enabled') > 0) {
- if ($gSettingsManager->getInt('photo_module_enabled') === 1
- || ($gSettingsManager->getInt('photo_module_enabled') === 2 && $gValidLogin)) {
- // call photo albums
- $sql = 'SELECT *
- FROM ' . TBL_PHOTOS . '
- WHERE pho_org_id = ? -- $gCurrentOrgId
- AND pho_locked = false
- AND pho_quantity > 0
- ORDER BY pho_begin DESC';
-
- // optional set a limit which albums should be scanned
- if ($plg_photos_albums > 0) {
- $sql .= ' LIMIT ' . $plg_photos_albums;
- }
-
- $albumStatement = $gDb->queryPrepared($sql, array($gCurrentOrgId));
- $albumList = $albumStatement->fetchAll();
-
- $i = 0;
- $photoNr = 0;
- $photoServerPath = '';
- $linkText = '';
- $album = new Album($gDb);
-
- // loop, if an image is not found directly, but limit to 20 passes
- while (!is_file($photoServerPath) && $i < 20 && $albumStatement->rowCount() > 0) {
- $album->setArray($albumList[mt_rand(0, $albumStatement->rowCount() - 1)]);
-
- // optionally select an image randomly
- if ($plg_photos_picnr === 0) {
- $photoNr = mt_rand(1, (int)$album->getValue('pho_quantity'));
- } else {
- $photoNr = $plg_photos_picnr;
- }
-
- // Compose image path
- $photoServerPath = ADMIDIO_PATH . FOLDER_DATA . '/photos/' . $album->getValue('pho_begin', 'Y-m-d') . '_' . (int)$album->getValue('pho_id') . '/' . $photoNr . '.jpg';
- ++$i;
- }
-
- if ($plg_photos_show_link && $plg_max_char_per_word > 0) {
- // Wrap link text if necessary
- $words = explode(' ', $album->getValue('pho_name'));
-
- foreach ($words as $word) {
- if (strlen($word) > $plg_max_char_per_word) {
- $linkText .= substr($word, 0, $plg_max_char_per_word) . '-
' .
- substr($word, $plg_max_char_per_word) . ' ';
- } else {
- $linkText .= $word . ' ';
- }
- }
- } else {
- $linkText = $album->getValue('pho_name');
- }
-
- $randomPhotoPlugin->assignTemplateVariable('photoUUID', $album->getValue('pho_uuid'));
- $randomPhotoPlugin->assignTemplateVariable('photoNr', $photoNr);
- $randomPhotoPlugin->assignTemplateVariable('photoTitle', $linkText);
- $randomPhotoPlugin->assignTemplateVariable('photoMaxWidth', $plg_photos_max_width);
- $randomPhotoPlugin->assignTemplateVariable('photoMaxHeight', $plg_photos_max_height);
- $randomPhotoPlugin->assignTemplateVariable('photoShowLink', $plg_photos_show_link);
- } else {
- $randomPhotoPlugin->assignTemplateVariable('message',$gL10n->get('PLG_RANDOM_PHOTO_NO_ENTRIES_VISITORS'));
- }
-
- if (isset($page)) {
- echo $randomPhotoPlugin->html('plugin.random-photo.tpl');
- } else {
- $randomPhotoPlugin->showHtmlPage('plugin.random-photo.tpl');
- }
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/random_photo/languages/en.xml b/adm_plugins/random_photo/languages/en.xml
deleted file mode 100644
index 42557cbcb1..0000000000
--- a/adm_plugins/random_photo/languages/en.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- There are no photos available for visitors. Log in to see the photos.
-
diff --git a/adm_plugins/who-is-online/config_sample.php b/adm_plugins/who-is-online/config_sample.php
deleted file mode 100644
index 2ffb93cea0..0000000000
--- a/adm_plugins/who-is-online/config_sample.php
+++ /dev/null
@@ -1,37 +0,0 @@
-sub($minutesOffset)->format('Y-m-d H:i:s');
-
- // Find user IDs of all sessions that are in the specified current and reference time
- $sql = 'SELECT ses_usr_id, usr_uuid, usr_login_name
- FROM ' . TBL_SESSIONS . '
- LEFT JOIN ' . TBL_USERS . '
- ON usr_id = ses_usr_id
- WHERE ses_timestamp BETWEEN ? AND ? -- $refDate AND DATETIME_NOW
- AND ses_org_id = ? -- $gCurrentOrgId';
- $queryParams = array($refDate, DATETIME_NOW, $gCurrentOrgId);
- if (!$plg_show_visitors) {
- $sql .= '
- AND ses_usr_id IS NOT NULL';
- }
- if (!$plg_show_self && $gValidLogin) {
- $sql .= '
- AND ses_usr_id <> ? -- $gCurrentUserId';
- $queryParams[] = $gCurrentUserId;
- }
- $sql .= '
- ORDER BY ses_usr_id';
- $onlineUsersStatement = $gDb->queryPrepared($sql, $queryParams);
-
- if ($onlineUsersStatement->rowCount() > 0) {
- $usrIdMerker = 0;
- $countMembers = 0;
- $countVisitors = 0;
- $allVisibleOnlineUsers = array();
- $textOnlineVisitors = '';
-
- while ($row = $onlineUsersStatement->fetch()) {
- if ($row['ses_usr_id'] > 0) {
- if (((int)$row['ses_usr_id'] !== $usrIdMerker)
- && ($plg_show_members == 1 || $gValidLogin)) {
- $allVisibleOnlineUsers[] = '' . $row['usr_login_name'] . '';
- $usrIdMerker = (int)$row['ses_usr_id'];
- }
- ++$countMembers;
- } else {
- ++$countVisitors;
- }
- }
-
- if (!$gValidLogin && $plg_show_members == 2 && $countMembers > 0) {
- if ($countMembers > 1) {
- $allVisibleOnlineUsers[] = $gL10n->get('PLG_ONLINE_VAR_NUM_MEMBERS', array($countMembers));
- } else {
- $allVisibleOnlineUsers[] = $gL10n->get('PLG_ONLINE_VAR_NUM_MEMBER', array($countMembers));
- }
- }
-
- if ($plg_show_visitors && $countVisitors > 0) {
- $allVisibleOnlineUsers[] = $gL10n->get('PLG_ONLINE_VAR_NUM_VISITORS', array($countVisitors));
- }
-
- if ($plg_show_users_side_by_side) {
- $textOnlineVisitors = implode(', ', $allVisibleOnlineUsers);
- } else {
- $textOnlineVisitors = '
' . implode('
', $allVisibleOnlineUsers);
- }
-
- if ($onlineUsersStatement->rowCount() === 1) {
- $whoIsOnlinePlugin->assignTemplateVariable('message', $gL10n->get('PLG_ONLINE_VAR_ONLINE_IS', array($textOnlineVisitors)));
- } else {
- $whoIsOnlinePlugin->assignTemplateVariable('message', $gL10n->get('PLG_ONLINE_VAR_ONLINE_ARE', array($textOnlineVisitors)));
- }
- } else {
- $whoIsOnlinePlugin->assignTemplateVariable('message', $gL10n->get('PLG_ONLINE_NO_VISITORS_ON_WEBSITE'));
- }
-
- if (isset($page)) {
- echo $whoIsOnlinePlugin->html('plugin.who-is-online.tpl');
- } else {
- $whoIsOnlinePlugin->showHtmlPage('plugin.who-is-online.tpl');
- }
-} catch (Throwable $e) {
- echo $e->getMessage();
-}
diff --git a/adm_plugins/who-is-online/languages/bg.xml b/adm_plugins/who-is-online/languages/bg.xml
deleted file mode 100644
index 0d524f8c8e..0000000000
--- a/adm_plugins/who-is-online/languages/bg.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Кой е онлайн?
- Няма онлайн потребители..
- #VAR1# посетители
- #VAR1# члет
- #VAR1# членове
- Онлайн има #VAR1#
- Онлайн е #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/da.xml b/adm_plugins/who-is-online/languages/da.xml
deleted file mode 100644
index e007cf678c..0000000000
--- a/adm_plugins/who-is-online/languages/da.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- On-line
- Lige nu er der ikke nogen besøgende på hjemmesiden
- #VAR1# Gæster
- #VAR1# Medlem
- #VAR1# Medlemmer
- Online er #VAR1#
- Online er #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/de-DE.xml b/adm_plugins/who-is-online/languages/de-DE.xml
deleted file mode 100644
index 1ef9dd4238..0000000000
--- a/adm_plugins/who-is-online/languages/de-DE.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Wer ist Online?
- Zur Zeit sind keine Besucher auf der Webseite.
- #VAR1# Besucher
- #VAR1# Mitglied
- #VAR1# Mitglieder
- Online sind #VAR1#
- Online ist #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/de.xml b/adm_plugins/who-is-online/languages/de.xml
deleted file mode 100644
index 45b2250872..0000000000
--- a/adm_plugins/who-is-online/languages/de.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Wer ist Online?
- Zur Zeit sind keine Besucher auf der Webseite.
- #VAR1# Besucher
- #VAR1# Mitglied
- #VAR1# Mitglieder
- Online sind #VAR1#
- Online ist #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/el.xml b/adm_plugins/who-is-online/languages/el.xml
deleted file mode 100644
index 324cc0f491..0000000000
--- a/adm_plugins/who-is-online/languages/el.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Ποιος είναι online;
- Δεν υπάρχουν επισκέπτες αυτή τη στιγμή.
- #VAR1# Επισκέπτες
- #VAR1# μέλος
- #VAR1# μέλη
- Online είναι #VAR1#
- Online είναι #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/en.xml b/adm_plugins/who-is-online/languages/en.xml
deleted file mode 100644
index d9faf8ec4e..0000000000
--- a/adm_plugins/who-is-online/languages/en.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Who is online?
- No visitors are on the website..
- #VAR1# Visitors
- #VAR1# member
- #VAR1# members
- Online are #VAR1#
- Online is #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/es.xml b/adm_plugins/who-is-online/languages/es.xml
deleted file mode 100644
index 02a5800964..0000000000
--- a/adm_plugins/who-is-online/languages/es.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- En línea
- En este momento no hay visitantes en el sitio web.
- #VAR1# visitantes
- #VAR1# Membro
- #VAR1# Membros
-
diff --git a/adm_plugins/who-is-online/languages/et.xml b/adm_plugins/who-is-online/languages/et.xml
deleted file mode 100644
index 8a5dc96d3c..0000000000
--- a/adm_plugins/who-is-online/languages/et.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Kes on Internetis?
- Veebisaidil pole külastajaid..
- #VAR1# Külastajad
- #VAR1# liige
- #VAR1# liikmed
- Internetis on #VAR1#
- Internetis on #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/fi.xml b/adm_plugins/who-is-online/languages/fi.xml
deleted file mode 100755
index d6c7f2e16e..0000000000
--- a/adm_plugins/who-is-online/languages/fi.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Kuka on kirjautuneena?
- Ei vieraita sivustolla..
- #VAR1# Vieraita
- #VAR1# jäsen
- #VAR1# jäsentä
- #VAR1# linjoilla
- #VAR1# on linjoilla
-
diff --git a/adm_plugins/who-is-online/languages/fr.xml b/adm_plugins/who-is-online/languages/fr.xml
deleted file mode 100644
index 06915baed4..0000000000
--- a/adm_plugins/who-is-online/languages/fr.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Qui est en ligne ?
- Il n\'y a actuellement aucun visiteur sur le site Web.
- #VAR1# visiteurs
- #VAR1# Membre
- #VAR1# Membres
- En ligne sont #VAR1#
- En ligne est #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/hu.xml b/adm_plugins/who-is-online/languages/hu.xml
deleted file mode 100644
index f8179bf6ce..0000000000
--- a/adm_plugins/who-is-online/languages/hu.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Kik vannak online?
- A honlapon nincsenek látogatók..
- #VAR1# Látogató
- #VAR1# tag
- #VAR1# tag
- Online van #VAR1#
- Online van #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/nb.xml b/adm_plugins/who-is-online/languages/nb.xml
deleted file mode 100644
index ca96f0d4b7..0000000000
--- a/adm_plugins/who-is-online/languages/nb.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Online
- Foreløpig er det ingen besøkende til nettstedet.
- #VAR1# Besøkende
- #VAR1# Medlem
- #VAR1# Medlemmer
- #VAR1# er online
- #VAR1# er online
-
diff --git a/adm_plugins/who-is-online/languages/nl.xml b/adm_plugins/who-is-online/languages/nl.xml
deleted file mode 100644
index 6119a2b2eb..0000000000
--- a/adm_plugins/who-is-online/languages/nl.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Wie is online?
- Op dit moment zijn geen bezoekers op de website.
- #VAR1# bezoekers
- #VAR1# lid
- #VAR1# leden
- Online zijn #VAR1#
- Online is #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/pl.xml b/adm_plugins/who-is-online/languages/pl.xml
deleted file mode 100644
index 95a44da52e..0000000000
--- a/adm_plugins/who-is-online/languages/pl.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Kto jest on-line?
- Nie ma odwiedzających na stronie.
- #VAR1# Odwiedzających
- #VAR1# Członek
- #VAR1# Członków
- #VAR1# jest On-line
- #VAR1# są On-line
-
diff --git a/adm_plugins/who-is-online/languages/pt-BR.xml b/adm_plugins/who-is-online/languages/pt-BR.xml
deleted file mode 100644
index b30a1a3508..0000000000
--- a/adm_plugins/who-is-online/languages/pt-BR.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Quem está on-line?
- Atualmente, não há nenhum visitante no site da Web.
- #VAR1# Visitantes
- #VAR1# Membro
- #VAR1# Membros
- Estão #VAR1# on-line
- Está #VAR1# on-line
-
diff --git a/adm_plugins/who-is-online/languages/pt.xml b/adm_plugins/who-is-online/languages/pt.xml
deleted file mode 100644
index f40cc16bb0..0000000000
--- a/adm_plugins/who-is-online/languages/pt.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Quem está on-line?
- Sem visitantes no site da Web.
- #VAR1# Visitantes
- #VAR1# membro
- #VAR1# membros
- Estão #VAR1# on-line
- Está #VAR1# on-line
-
diff --git a/adm_plugins/who-is-online/languages/ru.xml b/adm_plugins/who-is-online/languages/ru.xml
deleted file mode 100644
index ac25133a0b..0000000000
--- a/adm_plugins/who-is-online/languages/ru.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Кто онлайн?
- На сайте нет посетителей.
- #VAR1# посетителей
- #VAR1# участник
- #VAR1# участников
- Онлайн #VAR1#
- Онлайн #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/sv.xml b/adm_plugins/who-is-online/languages/sv.xml
deleted file mode 100644
index 88ff4907cc..0000000000
--- a/adm_plugins/who-is-online/languages/sv.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Vem är online?
- Inga besökare på webbplatsen ..
- #VAR1# besökare
- #VAR1# medlem
- #VAR1# medlemmar
- Online är #VAR1#
- Online är #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/uk.xml b/adm_plugins/who-is-online/languages/uk.xml
deleted file mode 100644
index 52b65b489e..0000000000
--- a/adm_plugins/who-is-online/languages/uk.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Хто онлайн?
- На сайті немає відвідувачів.
- #VAR1# Відвідувачів
- #VAR1# учасник
- #VAR1# учасників
- Онлайн #VAR1#
- Онлайн #VAR1#
-
diff --git a/adm_plugins/who-is-online/languages/zh.xml b/adm_plugins/who-is-online/languages/zh.xml
deleted file mode 100644
index bf89610f73..0000000000
--- a/adm_plugins/who-is-online/languages/zh.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- 当前谁在线?
- 还没有访客。
- #VAR1# 访客
- #VAR1# 会员
- #VAR1# 会员
-
diff --git a/composer.json b/composer.json
index 6cadf6b7f2..5c5139bf71 100644
--- a/composer.json
+++ b/composer.json
@@ -66,15 +66,15 @@
"!adm_my_files/config_example.php",
"!adm_my_files/ecard_templates",
"!adm_my_files/mail_templates",
- "adm_plugins/*",
- "!adm_plugins/announcement-list",
- "!adm_plugins/birthday",
- "!adm_plugins/calendar",
- "!adm_plugins/event-list",
- "!adm_plugins/latest-documents-files",
- "!adm_plugins/login_form",
- "!adm_plugins/random_photo",
- "!adm_plugins/who-is-online",
+ "plugins/*",
+ "!plugins/announcement-list",
+ "!plugins/birthday",
+ "!plugins/calendar",
+ "!plugins/event-list",
+ "!plugins/latest-documents-files",
+ "!plugins/login-form",
+ "!plugins/random-photo",
+ "!plugins/who-is-online",
"demo_data",
"!vendor/*",
"vendor/maennchen/zipstream-php/test",
diff --git a/composer.lock b/composer.lock
index 9bfbf9da4d..ef980fc70d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "eefd88e82bdb246e8126f1287c84737b",
+ "content-hash": "9963b82f17dc3d560c114b2bd9b3cf4e",
"packages": [
{
"name": "bjeavons/zxcvbn-php",
@@ -64,16 +64,16 @@
},
{
"name": "brick/math",
- "version": "0.14.1",
+ "version": "0.14.2",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "f05858549e5f9d7bb45875a75583240a38a281d0"
+ "reference": "55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0",
- "reference": "f05858549e5f9d7bb45875a75583240a38a281d0",
+ "url": "https://api.github.com/repos/brick/math/zipball/55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2",
+ "reference": "55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2",
"shasum": ""
},
"require": {
@@ -112,7 +112,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
- "source": "https://github.com/brick/math/tree/0.14.1"
+ "source": "https://github.com/brick/math/tree/0.14.2"
},
"funding": [
{
@@ -120,7 +120,7 @@
"type": "github"
}
],
- "time": "2025-11-24T14:40:29+00:00"
+ "time": "2026-01-30T14:03:11+00:00"
},
{
"name": "composer/pcre",
@@ -2674,16 +2674,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v7.4.3",
+ "version": "v7.4.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "a70c745d4cea48dbd609f4075e5f5cbce453bd52"
+ "reference": "446d0db2b1f21575f1284b74533e425096abdfb6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a70c745d4cea48dbd609f4075e5f5cbce453bd52",
- "reference": "a70c745d4cea48dbd609f4075e5f5cbce453bd52",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/446d0db2b1f21575f1284b74533e425096abdfb6",
+ "reference": "446d0db2b1f21575f1284b74533e425096abdfb6",
"shasum": ""
},
"require": {
@@ -2732,7 +2732,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v7.4.3"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.5"
},
"funding": [
{
@@ -2752,7 +2752,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-23T14:23:49+00:00"
+ "time": "2026-01-27T16:16:02+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -3012,7 +3012,6 @@
"ext-iconv": "*",
"ext-json": "*",
"ext-pdo": "*",
- "ext-posix": "*",
"ext-simplexml": "*",
"ext-zip": "*"
},
@@ -3020,5 +3019,5 @@
"platform-overrides": {
"php": "8.2.0"
},
- "plugin-api-version": "2.9.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/install/db_scripts/db.sql b/install/db_scripts/db.sql
index a616bbf642..5117f82eb9 100644
--- a/install/db_scripts/db.sql
+++ b/install/db_scripts/db.sql
@@ -161,6 +161,7 @@ CREATE TABLE %PREFIX%_components
com_update_step integer NOT NULL DEFAULT 0,
com_update_completed boolean NOT NULL DEFAULT true,
com_timestamp_installed timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ com_overview_plugin boolean NOT NULL DEFAULT false,
PRIMARY KEY (com_id)
)
ENGINE = InnoDB
diff --git a/install/db_scripts/update_5_1.xml b/install/db_scripts/update_5_1.xml
index 2e139053f7..15163fee0a 100644
--- a/install/db_scripts/update_5_1.xml
+++ b/install/db_scripts/update_5_1.xml
@@ -1,5 +1,16 @@
UpdateStepsCode::updateStep51AddSocialNetworkProfileFields
+ INSERT INTO %PREFIX%_components (com_type, com_name, com_name_intern, com_version, com_beta)
+ VALUES ('MODULE', 'SYS_PLUGIN_MANAGER', 'PLUGINS', '5.0.0', 0)
+ INSERT INTO %PREFIX%_menu (men_com_id, men_men_id_parent, men_uuid, men_node, men_order, men_standard, men_name_intern, men_url, men_icon, men_name, men_description)
+ VALUES ((SELECT com_id FROM %PREFIX%_components WHERE com_name_intern = 'PLUGINS'), 2, %UUID%, false, 5, true, 'plugins', '/modules/plugins.php', 'puzzle', 'SYS_PLUGIN_MANAGER', 'SYS_PLUGIN_MANAGER_DESC')
+ UPDATE %PREFIX%_menu SET men_url = REPLACE(men_url, '/adm_plugins', 'plugins') WHERE men_url LIKE '%/adm_plugins%'
+ ALTER TABLE %PREFIX%_components ADD COLUMN com_overview_plugin boolean not null default false
+ UpdateStepsCode::updateStep51InstallOverviewPlugins
+ UpdateStepsCode::updateStep51CheckFor3rdPartyPlugins
+ ALTER TABLE %PREFIX%_preferences MODIFY COLUMN prf_name varchar(100) NOT NULL
+ ALTER TABLE %PREFIX%_preferences ALTER COLUMN prf_name TYPE varchar(100)
+ ALTER TABLE %PREFIX%_preferences ALTER COLUMN prf_name SET NOT NULL
stop
diff --git a/install/install_steps/start_installation.php b/install/install_steps/start_installation.php
index 46f72944fe..6f8e679c7c 100644
--- a/install/install_steps/start_installation.php
+++ b/install/install_steps/start_installation.php
@@ -9,6 +9,7 @@
***********************************************************************************************
*/
+use Admidio\Infrastructure\Plugins\PluginManager;
use Admidio\Components\Entity\ComponentUpdate;
use Admidio\Infrastructure\Exception;
use Admidio\Infrastructure\Utils\PasswordUtils;
@@ -72,24 +73,25 @@
// create all modules components
$sql = 'INSERT INTO ' . TBL_COMPONENTS . '
(com_type, com_name, com_name_intern, com_version, com_beta)
- VALUES (\'MODULE\', \'SYS_ANNOUNCEMENTS\', \'ANNOUNCEMENTS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_CATEGORIES\', \'CATEGORIES\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_CATEGORY_REPORT\', \'CATEGORY-REPORT\',\'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_EVENTS\', \'EVENTS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_DOCUMENTS_FILES\', \'DOCUMENTS-FILES\',\'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_INVENTORY\', \'INVENTORY\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_FORUM\', \'FORUM\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_WEBLINKS\', \'LINKS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_GROUPS_ROLES\', \'GROUPS-ROLES\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_CONTACTS\', \'CONTACTS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_MESSAGES\', \'MESSAGES\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_MENU\', \'MENU\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_ORGANIZATION\', \'ORGANIZATIONS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_PHOTOS\', \'PHOTOS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_SETTINGS\', \'PREFERENCES\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_PROFILE\', \'PROFILE\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_REGISTRATION\', \'REGISTRATION\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')
- , (\'MODULE\', \'SYS_ROOM_MANAGEMENT\', \'ROOMS\', \'' . ADMIDIO_VERSION . '\', ' . ADMIDIO_VERSION_BETA . ')';
+ VALUES (\'MODULE\', \'SYS_ANNOUNCEMENTS\', \'ANNOUNCEMENTS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_CATEGORIES\', \'CATEGORIES\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_CATEGORY_REPORT\', \'CATEGORY-REPORT\',\''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_EVENTS\', \'EVENTS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_DOCUMENTS_FILES\', \'DOCUMENTS-FILES\',\''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_INVENTORY\', \'INVENTORY\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_FORUM\', \'FORUM\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_WEBLINKS\', \'LINKS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_GROUPS_ROLES\', \'GROUPS-ROLES\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_CONTACTS\', \'CONTACTS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_MESSAGES\', \'MESSAGES\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_MENU\', \'MENU\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_ORGANIZATION\', \'ORGANIZATIONS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_PHOTOS\', \'PHOTOS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_SETTINGS\', \'PREFERENCES\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_PROFILE\', \'PROFILE\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_REGISTRATION\', \'REGISTRATION\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_ROOM_MANAGEMENT\', \'ROOMS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')
+ , (\'MODULE\', \'SYS_PLUGIN_MANAGER\', \'PLUGINS\', \''.ADMIDIO_VERSION.'\', '.ADMIDIO_VERSION_BETA.')';
$db->query($sql); // TODO add more params
// create organization independent categories
@@ -265,24 +267,42 @@
VALUES (NULL, NULL, \'' . Uuid::uuid4() . '\', true, 1, true, \'modules\', NULL, \'\', \'SYS_MODULES\', \'\')
, (NULL, NULL, \'' . Uuid::uuid4() . '\', true, 2, true, \'administration\', NULL, \'\', \'SYS_ADMINISTRATION\', \'\')
, (NULL, NULL, \'' . Uuid::uuid4() . '\', true, 3, true, \'extensions\', NULL, \'\', \'SYS_EXTENSIONS\', \'\')
- , (NULL, 1, \'' . Uuid::uuid4() . '\', false, 1, true, \'overview\', \'' . FOLDER_MODULES . '/overview.php\', \'bi-house-door-fill\', \'SYS_OVERVIEW\', \'\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'ANNOUNCEMENTS\'), 1, \'' . Uuid::uuid4() . '\', false, 2, true, \'announcements\', \'' . FOLDER_MODULES . '/announcements.php\', \'newspaper\', \'SYS_ANNOUNCEMENTS\', \'SYS_ANNOUNCEMENTS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'EVENTS\'), 1, \'' . Uuid::uuid4() . '\', false, 3, true, \'events\', \'' . FOLDER_MODULES . '/events/events.php\', \'calendar-week-fill\', \'SYS_EVENTS\', \'SYS_EVENTS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'MESSAGES\'), 1, \'' . Uuid::uuid4() . '\', false, 4, true, \'messages\', \'' . FOLDER_MODULES . '/messages/messages.php\', \'envelope-fill\', \'SYS_MESSAGES\', \'SYS_MESSAGES_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'GROUPS-ROLES\'), 1, \'' . Uuid::uuid4() . '\', false, 5, true, \'groups-roles\', \'' . FOLDER_MODULES . '/groups-roles/groups_roles.php\', \'people-fill\', \'SYS_GROUPS_ROLES\', \'SYS_GROUPS_ROLES_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'CONTACTS\'), 1, \'' . Uuid::uuid4() . '\', false, 6, true, \'contacts\', \'' . FOLDER_MODULES . '/contacts/contacts.php\', \'person-vcard-fill\', \'SYS_CONTACTS\', \'SYS_CONTACTS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'DOCUMENTS-FILES\'), 1, \'' . Uuid::uuid4() . '\', false, 7, true, \'documents-files\', \'' . FOLDER_MODULES . '/documents-files.php\', \'file-earmark-arrow-down-fill\', \'SYS_DOCUMENTS_FILES\', \'SYS_DOCUMENTS_FILES_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'INVENTORY\'), 1, \'' . Uuid::uuid4() . '\', false, 8, true, \'inventory\', \'' . FOLDER_MODULES . '/inventory.php\', \'box-seam-fill\', \'SYS_INVENTORY\', \'SYS_INVENTORY_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'PHOTOS\'), 1, \'' . Uuid::uuid4() . '\', false, 9, true, \'photo\', \'' . FOLDER_MODULES . '/photos/photos.php\', \'image-fill\', \'SYS_PHOTOS\', \'SYS_PHOTOS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'CATEGORY-REPORT\'), 1, \'' . Uuid::uuid4() . '\', false, 10, true, \'category-report\', \'' . FOLDER_MODULES . '/category-report/category_report.php\', \'list-stars\', \'SYS_CATEGORY_REPORT\', \'SYS_CATEGORY_REPORT_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'LINKS\'), 1, \'' . Uuid::uuid4() . '\', false, 11, true, \'weblinks\', \'' . FOLDER_MODULES . '/links/links.php\', \'link-45deg\', \'SYS_WEBLINKS\', \'SYS_WEBLINKS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'FORUM\'), 1, \'' . Uuid::uuid4() . '\', false, 12, true, \'forum\', \'' . FOLDER_MODULES . '/forum.php\', \'chat-dots-fill\', \'SYS_FORUM\', \'SYS_FORUM_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'PREFERENCES\'), 2, \'' . Uuid::uuid4() . '\', false, 1, true, \'orgprop\', \'' . FOLDER_MODULES . '/preferences.php\', \'gear-fill\', \'SYS_SETTINGS\', \'ORG_ORGANIZATION_PROPERTIES_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'REGISTRATION\'), 2, \'' . Uuid::uuid4() . '\', false, 2, true, \'registration\', \'' . FOLDER_MODULES . '/registration.php\', \'card-checklist\', \'SYS_REGISTRATIONS\', \'SYS_MANAGE_NEW_REGISTRATIONS_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'MENU\'), 2, \'' . Uuid::uuid4() . '\', false, 3, true, \'menu\', \'' . FOLDER_MODULES . '/menu.php\', \'menu-button-wide-fill\', \'SYS_MENU\', \'SYS_MENU_DESC\')
- , ((SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name_intern = \'ORGANIZATIONS\'), 2, \'' . Uuid::uuid4() . '\', false, 4, true, \'organization\', \'' . FOLDER_MODULES . '/organizations.php\', \'diagram-3-fill\', \'SYS_ORGANIZATION\', \'SYS_ORGANIZATION_DESC\')';
+ , (NULL, 1, \'' . Uuid::uuid4() . '\', false, 1, true, \'overview\', \''.FOLDER_MODULES.'/overview.php\', \'bi-house-door-fill\', \'SYS_OVERVIEW\', \'\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'ANNOUNCEMENTS\'), 1, \'' . Uuid::uuid4() . '\', false, 2, true, \'announcements\', \''.FOLDER_MODULES.'/announcements.php\', \'newspaper\', \'SYS_ANNOUNCEMENTS\', \'SYS_ANNOUNCEMENTS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'EVENTS\'), 1, \'' . Uuid::uuid4() . '\', false, 3, true, \'events\', \''.FOLDER_MODULES.'/events/events.php\', \'calendar-week-fill\', \'SYS_EVENTS\', \'SYS_EVENTS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'MESSAGES\'), 1, \'' . Uuid::uuid4() . '\', false, 4, true, \'messages\', \''.FOLDER_MODULES.'/messages/messages.php\', \'envelope-fill\', \'SYS_MESSAGES\', \'SYS_MESSAGES_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'GROUPS-ROLES\'), 1, \'' . Uuid::uuid4() . '\', false, 5, true, \'groups-roles\', \''.FOLDER_MODULES.'/groups-roles/groups_roles.php\', \'people-fill\', \'SYS_GROUPS_ROLES\', \'SYS_GROUPS_ROLES_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'CONTACTS\'), 1, \'' . Uuid::uuid4() . '\', false, 6, true, \'contacts\', \''.FOLDER_MODULES.'/contacts/contacts.php\', \'person-vcard-fill\', \'SYS_CONTACTS\', \'SYS_CONTACTS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'DOCUMENTS-FILES\'), 1, \'' . Uuid::uuid4() . '\', false, 7, true, \'documents-files\', \''.FOLDER_MODULES.'/documents-files.php\', \'file-earmark-arrow-down-fill\', \'SYS_DOCUMENTS_FILES\', \'SYS_DOCUMENTS_FILES_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'INVENTORY\'), 1, \'' . Uuid::uuid4() . '\', false, 8, true, \'inventory\', \''.FOLDER_MODULES.'/inventory.php\', \'box-seam-fill\', \'SYS_INVENTORY\', \'SYS_INVENTORY_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'PHOTOS\'), 1, \'' . Uuid::uuid4() . '\', false, 9, true, \'photo\', \''.FOLDER_MODULES.'/photos/photos.php\', \'image-fill\', \'SYS_PHOTOS\', \'SYS_PHOTOS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'CATEGORY-REPORT\'), 1, \'' . Uuid::uuid4() . '\', false, 10, true, \'category-report\', \''.FOLDER_MODULES.'/category-report/category_report.php\', \'list-stars\', \'SYS_CATEGORY_REPORT\', \'SYS_CATEGORY_REPORT_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'LINKS\'), 1, \'' . Uuid::uuid4() . '\', false, 11, true, \'weblinks\', \''.FOLDER_MODULES.'/links/links.php\', \'link-45deg\', \'SYS_WEBLINKS\', \'SYS_WEBLINKS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'FORUM\'), 1, \'' . Uuid::uuid4() . '\', false, 12, true, \'forum\', \''.FOLDER_MODULES.'/forum.php\', \'chat-dots-fill\', \'SYS_FORUM\', \'SYS_FORUM_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'PREFERENCES\'), 2, \'' . Uuid::uuid4() . '\', false, 1, true, \'orgprop\', \''.FOLDER_MODULES.'/preferences.php\', \'gear-fill\', \'SYS_SETTINGS\', \'ORG_ORGANIZATION_PROPERTIES_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'REGISTRATION\'), 2, \'' . Uuid::uuid4() . '\', false, 2, true, \'registration\', \''.FOLDER_MODULES.'/registration.php\', \'card-checklist\', \'SYS_REGISTRATIONS\', \'SYS_MANAGE_NEW_REGISTRATIONS_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'MENU\'), 2, \'' . Uuid::uuid4() . '\', false, 3, true, \'menu\', \''.FOLDER_MODULES.'/menu.php\', \'menu-button-wide-fill\', \'SYS_MENU\', \'SYS_MENU_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'ORGANIZATIONS\'), 2, \'' . Uuid::uuid4() . '\', false, 4, true, \'organization\', \''.FOLDER_MODULES.'/organizations.php\', \'diagram-3-fill\', \'SYS_ORGANIZATION\', \'SYS_ORGANIZATION_DESC\')
+ , ((SELECT com_id FROM '.TBL_COMPONENTS.' WHERE com_name_intern = \'PLUGINS\'), 2, \'' . Uuid::uuid4() . '\', false, 5, true, \'plugins\', \''.FOLDER_MODULES.'/plugins.php\', \'puzzle\', \'SYS_PLUGIN_MANAGER\', \'SYS_PLUGIN_MANAGER_DESC\')';
$db->query($sql);
+// install all overview plugins
+$pluginManager = new PluginManager();
+$plugins = $pluginManager->getAvailablePlugins();
+
+foreach ($plugins as $plugin) {
+ // check, if the plugin has an interface, if not, scip it
+ if (!isset($plugin['interface']) || $plugin['interface'] == null) {
+ continue;
+ }
+ // check if the plugin is an overview plugin, if so, install it
+ $instance = $plugin['interface']::getInstance();
+ if ($instance->isAdmidioPlugin()) {
+ // Install the overview plugin
+ $instance->doInstall();
+ }
+}
+
// delete session data
session_unset();
session_destroy();
diff --git a/install/templates/sys-template-parts/parts/form.part.info.tpl b/install/templates/sys-template-parts/parts/form.part.info.tpl
new file mode 100644
index 0000000000..85de8ebc26
--- /dev/null
+++ b/install/templates/sys-template-parts/parts/form.part.info.tpl
@@ -0,0 +1,5 @@
+{if $data.alertInfo}
+
+ {$data.alertInfo}
+
+{/if}
diff --git a/install/templates/update.successful.tpl b/install/templates/update.successful.tpl
index dce4b54fdc..31552727ce 100644
--- a/install/templates/update.successful.tpl
+++ b/install/templates/update.successful.tpl
@@ -7,6 +7,20 @@
{$l10n->get('INS_UPDATING_WAS_SUCCESSFUL')}
+ {if $updateWarnings|@count > 0 || $updateInfos|@count > 0}
+ {$l10n->get('SYS_NOTE')}
+ {if $updateInfos|@count > 0}
+ {foreach from=$updateInfos item=data}
+ {include file="sys-template-parts/parts/form.part.info.tpl"}
+ {/foreach}
+ {/if}
+ {if $updateWarnings|@count > 0}
+ {foreach from=$updateWarnings item=data}
+ {include file="sys-template-parts/parts/form.part.warning.tpl"}
+ {/foreach}
+ {/if}
+ {/if}
+
{$l10n->get('INS_UPDATE_TO_VERSION_SUCCESSFUL', array(ADMIDIO_VERSION_TEXT))}
{$l10n->get('INS_SUPPORT_FURTHER_DEVELOPMENT')}
diff --git a/install/update.php b/install/update.php
index efc6b8627a..29288c3d90 100644
--- a/install/update.php
+++ b/install/update.php
@@ -305,9 +305,64 @@ function showErrorMessage(string $message, bool $reloadPage = false): void
));
exit();
} elseif ($getMode === 'result') {
+ // check if there are warnings about the plugins
+ $updateWarnings = array();
+ $updateInfos = array();
+ // the variables are stored in a cookie so that they survive the redirect after the update
+ $cookie = isset($_COOKIE['adm_update_plugins_warnings']) ? json_decode($_COOKIE['adm_update_plugins_warnings'], true) : [];
+ $gWarnOldPlugins = false;
+ $gWarn3rdPartyPlugins = false;
+ $gWarnOldPluginsFolder = false;
+ $gInfo3rdPartyPlugins = false;
+
+ if (!empty($cookie)) {
+ if (isset($cookie['warn_old_plugins'])) {
+ $gWarnOldPlugins = $cookie['warn_old_plugins'];
+ }
+ if (isset($cookie['warn_3rd_party_plugins'])) {
+ $gWarn3rdPartyPlugins = $cookie['warn_3rd_party_plugins'];
+ }
+ if (isset($cookie['warn_old_plugins_folder'])) {
+ $gWarnOldPluginsFolder = $cookie['warn_old_plugins_folder'];
+ }
+ if (isset($cookie['info_3rd_party_plugins'])) {
+ $gInfo3rdPartyPlugins = $cookie['info_3rd_party_plugins'];
+ }
+ }
+
+ // reset the cookie after reading the values so that the warnings are only shown once
+ setcookie('adm_update_plugins_warnings', '', time() - 3600, '/');
+
+ if ($gWarnOldPlugins) {
+ $updateWarnings[] = array(
+ 'id' => 'warn_old_plugins',
+ 'alertWarning' => $gL10n->get('INS_WARNING_OLD_ADM_PLUGINS_COULD_NOT_BE_DELETED', array('adm_plugins', 'adm_plugins'))
+ );
+ }
+ if ($gWarn3rdPartyPlugins) {
+ $updateWarnings[] = array(
+ 'id' => 'warn_3rd_party_plugins',
+ 'alertWarning' => $gL10n->get('INS_WARNING_3RD_PARRTY_PLUGINS_COULD_NOT_BE_MOVED', array('plugins', 'adm_plugins'))
+ );
+ }
+ if ($gWarnOldPluginsFolder) {
+ $updateWarnings[] = array(
+ 'id' => 'warn_old_plugins_folder',
+ 'alertWarning' => $gL10n->get('INS_WARNING_OLD_ADM_PLUGINS_FOLDER_COULD_NOT_BE_DELETED', array('adm_plugins', 'adm_plugins'))
+ );
+ }
+ if ($gInfo3rdPartyPlugins) {
+ $updateInfos[] = array(
+ 'id' => 'info_3rd_party_plugins',
+ 'alertInfo' => $gL10n->get('INS_INFO_3RD_PARRTY_PLUGINS_HAVE_BEEN_MOVED', array('plugins'))
+ );
+ }
+
// show notice that update was successful
$page = new InstallationPresenter('admidio-update-successful', $gL10n->get('INS_UPDATE'));
$page->addTemplateFile('update.successful.tpl');
+ $page->assignSmartyVariable('updateWarnings', $updateWarnings);
+ $page->assignSmartyVariable('updateInfos', $updateInfos);
$page->setUpdateModus();
$page->addJavascript('$("#buttonDonate").focus();', true);
$page->show();
diff --git a/languages/en.xml b/languages/en.xml
index 56b1b6513e..6a3163867f 100644
--- a/languages/en.xml
+++ b/languages/en.xml
@@ -35,6 +35,7 @@
File #VAR1_BOLD# could not be opened.
Groups
Hostname is not valid. Enter a valid host name or IP address.
+ Some 3rd party plugins have been moved to the new plugin folder #VAR1_BOLD#. Please check the functionality of these plugins after the update.
Install Admidio
Installation
The database already contains an Admidio installation. An additional installation is not possible.
@@ -60,6 +61,9 @@
Update to version #VAR1#
Update process successful
This is a beta version of Admidio.\n\nUsage can be risky due to possible instability and data loss. Please use as test environment only!
+ Some old admidio plugin folders could not be deleted from the old plugin folder #VAR1_BOLD#.\nPlease check the folder #VAR2_BOLD# within your Admidio installation and delete the old plugin folders manually and delete the old adm_plugins folder afterward.
+ The old plugin folder #VAR1_BOLD# could not be deleted.\nPlease check the folder #VAR2_BOLD# within your Admidio installation and delete the old adm_plugins folder manually.
+ Some 3rd party plugin folders could not be moved to the new plugin folder #VAR1_BOLD#.\nPlease check the folder #VAR2_BOLD# within your Admidio installation and move the 3rd party plugin folders manually and delete the old adm_plugins folder afterward.
This assistant helps you to set up the Admidio member management. All necessary data will be requested so that the system can be fully set up and you can log in as an administrator.\n\nIf you have questions or problems with the installation, please check #VAR1#our installation instructions#VAR2# or contact us at #VAR3#our support forum#VAR4#.
You have already updated the directories of Admidio with a new version. Now the database has to be updated from the current version #VAR2# to the version #VAR1#, so that Admidio can run with the new version.\n\nIf you have questions or problems with the update, please check #VAR3#our update instruction#VAR4# or contact #VAR5#our support forum#VAR6#.
Welcome to the installation of Admidio
@@ -90,6 +94,8 @@
Male superior
Wife
+ Plugin access
+ Plugin can be disabled or enabled for logged in users only. If plugin is only accessible to logged in users, it will be hidden for logged out users and web feed will be completely disabled for both user groups. (default: active)
Module access
Module can be disabled or enabled for logged in users only. If module is only accessible to logged in users, it will be hidden for logged out users and web feed will be completely disabled for both user groups. (default: active)
Additional variables
@@ -282,6 +288,7 @@
#VAR1# attachments
You signed up for the event #VAR1_BOLD# on #VAR2#.
You signed up tentative for the event #VAR1_BOLD# on #VAR2#.
+ Author
Auto-detect
Available
Available Beta
@@ -655,7 +662,9 @@
Export lists
Lists of groups and roles can be exported by users, provided that you have the right to view these lists. This setting can be used to restrict the export function. (Default: All)
Export vCard
+ Extension
Extensions
+ Available extensions
Facebook
Favicon File
The path to the favicon file for your installation. The path should be relative to your installation's root directory. If left empty, the default favicon of the theme will be used. (default: empty)
@@ -773,6 +782,7 @@
The Admidio database is now installed and completely set up. The configuration file with the access data to the database has been created and stored. You can now work with Admidio and log in with the administrator data.
The installation and setup of the database was successfully completed.
Installed
+ Installed version
Internal name
The internal name is used to uniquely access this data object. This is not relevant for program use. In development, however, this name must always be used to identify the field, e.g. to address with getValue (). The internal name is automatically filled when creating a data record.
Recipients need to be listed in the TO field instead of the BCC field
@@ -1109,6 +1119,7 @@
None
Not at registration
Not empty
+ Not installed
Not member of any organization
Not member of organization #VAR1#
Please use a valid number as condition for field #VAR1_BOLD#.
@@ -1155,6 +1166,8 @@
Name and contact details of the current organization with the option of creating and displaying sub-organizations.
Overhang
Overview
+ Overview extensions
+ The appearance of the overview extensions on the overview page can be changed here. The sequence of the extensions can be changed by drag & drop (big screens) or by clicking the move buttons (small screens).\n\n The new sequence only takes effect after saving the settings!
Next
Previous
#VAR1# and #VAR2#
@@ -1216,6 +1229,17 @@
Admidio requires #VAR1# or higher
Pipe (|)
Select option
+ This plugin is disabled.
+ Install plugin
+ Plugin Manager
+ Here you can manage the plugins of Admidio. Plugins are additional modules that can be installed to extend the functionality of Admidio. You can install, update or remove plugins.
+ No plugin name specified.
+ The plugin has no interface.
+ The plugin is not installed.
+ Plugin preferences
+ Uninstall plugin
+ Update plugin
+ Plugin version
Port
Portrait
Column names for auto detection: #VAR1#
@@ -1687,7 +1711,9 @@
Unknown
Unlock album
Unmanaged files
+ Up to date
Update successful
+ Update available
Upload files
Max. file upload size
Upload photos
diff --git a/modules/overview.php b/modules/overview.php
index e7e8ecc7b0..68deeb02f0 100644
--- a/modules/overview.php
+++ b/modules/overview.php
@@ -10,6 +10,7 @@
*/
use Admidio\UI\Presenter\PagePresenter;
+use Admidio\Infrastructure\Plugins\PluginManager;
try {
// if the config file doesn't exist, then show the installation dialog
@@ -28,6 +29,20 @@
// create html page object and load template file
$page = PagePresenter::withHtmlIDAndHeadline('admidio-overview', $headline);
$page->setContentFullWidth();
+
+ // get all overview plugins and add them to the template
+ $pluginManager = new PluginManager();
+ $plugins = $pluginManager->getOverviewPlugins();
+
+ $overviewPlugins = array();
+ foreach ($plugins as $plugin) {
+ $overviewPlugins[] = array(
+ 'id' => $plugin['id'],
+ 'name' => $plugin['name'],
+ 'file' => basename($plugin['file'])
+ );
+ }
+ $page->assignSmartyVariable('overviewPlugins', $overviewPlugins);
$page->addTemplateFile('system/overview.tpl');
$page->show();
diff --git a/modules/plugins.php b/modules/plugins.php
new file mode 100644
index 0000000000..6ef9de6e8d
--- /dev/null
+++ b/modules/plugins.php
@@ -0,0 +1,120 @@
+ 'list', 'validValues' => array('list', 'install', 'uninstall', 'update', 'sequence')));
+ $getPluginName = admFuncVariableIsValid($_GET, 'name', 'string', array('defaultValue' => ''));
+ $getPluginId = admFuncVariableIsValid($_GET, 'uuid', 'int');
+
+ // check rights to use this module
+ if (!$gCurrentUser->isAdministrator()) {
+ throw new Exception('SYS_NO_RIGHTS');
+ }
+
+ switch ($getMode) {
+ case 'list':
+ // create html page object
+ $page = new PluginsPresenter();
+ $page->createList();
+ $gNavigation->addStartUrl(CURRENT_URL, $page->getHeadline(), 'bi-puzzle-fill');
+ $page->show();
+ break;
+
+ case 'install':
+ // install plugin
+ if (!empty($getPluginName)) {
+ $pluginManager = new PluginManager();
+ $plugin = $pluginManager->getPluginByName($getPluginName);
+ if ($plugin) {
+ $interface = $plugin instanceof PluginAbstract ? $plugin::getInstance() : null;
+
+ if ($interface != null) {
+ if (!$interface->checkDependencies()) {
+ throw new RuntimeException('Missing dependencies for ' . $getPluginName . ' plugin');
+ }
+
+ $interface->doInstall();
+ }
+ }
+ $gNavigation->deleteLastUrl();
+ admRedirect(SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php'));
+ } else {
+ throw new Exception('SYS_PLUGIN_NAME_MISSING');
+ }
+ break;
+
+ case 'uninstall':
+ // uninstall plugin
+ if (!empty($getPluginName)) {
+ $pluginManager = new PluginManager();
+ $plugin = $pluginManager->getPluginByName($getPluginName);
+ if ($plugin) {
+ $interface = $plugin instanceof PluginAbstract ? $plugin::getInstance() : null;
+
+ if ($interface != null) {
+ $interface->doUninstall();
+ }
+ }
+ $gNavigation->deleteLastUrl();
+ admRedirect(SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php'));
+ } else {
+ throw new Exception('SYS_PLUGIN_NAME_MISSING');
+ }
+ break;
+
+ case 'update':
+ // update plugin
+ if (!empty($getPluginName)) {
+ $pluginManager = new PluginManager();
+ $plugin = $pluginManager->getPluginByName($getPluginName);
+ if ($plugin) {
+ $interface = $plugin instanceof PluginAbstract ? $plugin::getInstance() : null;
+
+ if ($interface != null) {
+ $interface->doUpdate();
+ }
+ }
+ $gNavigation->deleteLastUrl();
+ admRedirect(SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php'));
+ } else {
+ throw new Exception('SYS_PLUGIN_NAME_MISSING');
+ }
+ break;
+
+ default:
+ throw new Exception('SYS_UNKNOWN_MODE');
+ }
+} catch (Throwable $e) {
+ if (in_array($getMode, array('save', 'delete'))) {
+ echo json_encode(array('status' => 'error', 'message' => $e->getMessage()));
+ } else {
+ $gMessage->show($e->getMessage());
+ }
+}
diff --git a/plugins/announcement-list/announcement-list.json b/plugins/announcement-list/announcement-list.json
new file mode 100644
index 0000000000..6f78af9327
--- /dev/null
+++ b/plugins/announcement-list/announcement-list.json
@@ -0,0 +1,64 @@
+{
+ "name": "PLG_ANNOUNCEMENT_LIST_PLUGIN_NAME",
+ "description": "PLG_ANNOUNCEMENT_LIST_PLUGIN_DESCRIPTION",
+ "version": "1.0.0",
+ "author": "Admidio Team",
+ "url": "https://www.admidio.org",
+ "icon": "bi-newspaper",
+ "mainFile": "index.php",
+ "overviewPlugin": true,
+ "autoload": {
+ "psr-4": {
+ "AnnouncementList\\classes\\": "classes/"
+ }
+ },
+ "dependencies": [
+ "Admidio\\Announcements\\Entity\\Announcement",
+ "Admidio\\Infrastructure\\Database",
+ "Admidio\\Infrastructure\\Plugins\\Overview",
+ "Admidio\\Infrastructure\\Utils\\SecurityUtils",
+ "Admidio\\Infrastructure\\Plugins\\PluginAbstract"
+ ],
+ "defaultConfig": {
+ "announcement_list_plugin_enabled": {
+ "name": "ORG_ACCESS_TO_PLUGIN",
+ "description": "ORG_ACCESS_TO_PLUGIN_DESC",
+ "type": "integer",
+ "value" : 1
+ },
+ "announcement_list_overview_sequence": {
+ "type": "integer",
+ "value" : 6
+ },
+ "announcement_list_announcements_count": {
+ "name": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_ANNOUNCEMENTS_COUNT",
+ "description": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_ANNOUNCEMENTS_COUNT_DESC",
+ "type": "integer",
+ "value" : 2
+ },
+ "announcement_list_show_preview_chars": {
+ "name": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_SHOW_PREVIEW_CHARS",
+ "description": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_SHOW_PREVIEW_CHARS_DESC",
+ "type": "integer",
+ "value" : 70
+ },
+ "announcement_list_show_full_description": {
+ "name": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_SHOW_FULL_DESCRIPTION",
+ "description": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_SHOW_FULL_DESCRIPTION_DESC",
+ "type": "boolean",
+ "value" : false
+ },
+ "announcement_list_chars_before_linebreak": {
+ "name": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_CHARS_BEFORE_LINEBREAK",
+ "description": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_CHARS_BEFORE_LINEBREAK_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "announcement_list_displayed_categories": {
+ "name": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_DISPLAYED_CATEGORIES",
+ "description": "PLG_ANNOUNCEMENT_LIST_PREFERENCES_DISPLAYED_CATEGORIES_DESC",
+ "type": "array",
+ "value" : ["All"]
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/announcement-list/classes/AnnouncementList.php b/plugins/announcement-list/classes/AnnouncementList.php
new file mode 100644
index 0000000000..e61bc98507
--- /dev/null
+++ b/plugins/announcement-list/classes/AnnouncementList.php
@@ -0,0 +1,200 @@
+getAllVisibleCategories('ANN');
+ }
+ return $config;
+ }
+
+ /**
+ * Get the plugin configuration values
+ * @return array Returns the plugin configuration values
+ */
+ public static function getPluginConfigValues() : array
+ {
+ global $gCurrentUser;
+
+ // get the plugin config values from the parent class
+ $config = parent::getPluginConfigValues();
+
+ // if the key equals 'announcement_list_displayed_categories' and the value is still the default value, retrieve the categories from the database
+ if (array_key_exists('announcement_list_displayed_categories', $config) && $config['announcement_list_displayed_categories'] === self::$defaultConfig['announcement_list_displayed_categories']['value']) {
+ $config['announcement_list_displayed_categories'] = $gCurrentUser->getAllVisibleCategories('ANN');
+ }
+
+ return $config;
+ }
+
+ private static function getAnnouncementsData() : array
+ {
+ global $gSettingsManager, $gCurrentUser, $gDb, $gL10n;
+
+ $config = self::getPluginConfigValues();
+
+ if (!is_array($config['announcement_list_displayed_categories']) || empty($config['announcement_list_displayed_categories'])) {
+ $plgSqlCategories = '';
+ } else {
+ $plgSqlCategories = ' AND cat_id IN (' . Database::getQmForValues($config['announcement_list_displayed_categories']) . ') ';
+ }
+
+ // read announcements from database
+ $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('ANN'));
+
+ $sql = 'SELECT cat.*, ann.*
+ FROM ' . TBL_ANNOUNCEMENTS . ' AS ann
+ INNER JOIN ' . TBL_CATEGORIES . ' AS cat
+ ON cat_id = ann_cat_id
+ WHERE cat_id IN (' . Database::getQmForValues($catIdParams) . ')
+ ' . $plgSqlCategories . '
+ ORDER BY ann_timestamp_create DESC
+ LIMIT ' . $config['announcement_list_announcements_count'];
+
+ $pdoStatement = $gDb->queryPrepared($sql, array_merge($catIdParams, $config['announcement_list_displayed_categories']));
+ $plgAnnouncementsList = $pdoStatement->fetchAll();
+
+ $announcementArray = array();
+
+ if ($pdoStatement->rowCount() > 0) {
+ // get announcements data
+ $plgAnnouncement = new Announcement($gDb);
+
+ foreach ($plgAnnouncementsList as $plgRow) {
+ $plgAnnouncement->clear();
+ $plgAnnouncement->setArray($plgRow);
+
+ if ($config['announcement_list_chars_before_linebreak'] > 0) {
+ // Interrupt words of headline if they are too long
+ $plgNewHeadline = '';
+
+ $plgWords = explode(' ', $plgAnnouncement->getValue('ann_headline'));
+
+ foreach ($plgWords as $plgValue) {
+ if (strlen($plgValue) > $config['announcement_list_chars_before_linebreak']) {
+ $plgNewHeadline .= ' ' . substr($plgValue, 0, $config['announcement_list_chars_before_linebreak']) . '-
' .
+ substr($plgValue, $config['announcement_list_chars_before_linebreak']);
+ } else {
+ $plgNewHeadline .= ' ' . $plgValue;
+ }
+ }
+ } else {
+ $plgNewHeadline = $plgAnnouncement->getValue('ann_headline');
+ }
+
+ // show preview text
+ if ($config['announcement_list_show_full_description'] === true) {
+ $plgNewDescription = $plgAnnouncement->getValue('ann_description');
+ } elseif ($config['announcement_list_show_preview_chars'] > 0) {
+ // remove all html tags except some format tags
+ $plgNewDescription = strip_tags($plgAnnouncement->getValue('ann_description'));
+
+ // read first x chars of text and additional 15 chars. Then search for last space and cut the text there
+ $plgNewDescription = substr($plgNewDescription, 0, $config['announcement_list_show_preview_chars'] + 15);
+ $plgNewDescription = substr($plgNewDescription, 0, strrpos($plgNewDescription, ' ')) . '
+ »';
+ }
+
+ $announcementArray[] = array(
+ 'uuid' => $plgAnnouncement->getValue('ann_uuid'),
+ 'headline' => $plgNewHeadline,
+ 'description' => $plgNewDescription,
+ 'creationDate' => $plgAnnouncement->getValue('ann_timestamp_create', $gSettingsManager->getString('system_date'))
+ );
+ }
+ }
+ return $announcementArray;
+ }
+
+ /**
+ * @param PagePresenter $page
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @return bool
+ */
+ public static function doRender($page = null) : bool
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+
+ // show the announcement list
+ try {
+ $rootPath = dirname(__DIR__, 3);
+ $pluginFolder = basename(self::$pluginPath);
+
+ require_once($rootPath . '/system/common.php');
+
+ $announcementListPlugin = new Overview($pluginFolder);
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ throw new InvalidArgumentException($gL10n->get('SYS_PLUGIN_NOT_INSTALLED'));
+ }
+
+ if ($gSettingsManager->getInt('announcements_module_enabled') > 0) {
+ if (($gSettingsManager->getInt('announcements_module_enabled') === 1 || ($gSettingsManager->getInt('announcements_module_enabled') === 2 && $gValidLogin)) &&
+ ($gSettingsManager->getInt('announcement_list_plugin_enabled') === 1 || ($gSettingsManager->getInt('announcement_list_plugin_enabled') === 2 && $gValidLogin))) {
+ $announcementArray = self::getAnnouncementsData();
+ if (!empty($announcementArray)) {
+ $announcementListPlugin->assignTemplateVariable('announcements', $announcementArray);
+ } else {
+ $announcementListPlugin->assignTemplateVariable('message',$gL10n->get('SYS_NO_ENTRIES'));
+ }
+ } else {
+ $announcementListPlugin->assignTemplateVariable('message',$gL10n->get('PLG_ANNOUNCEMENT_LIST_NO_ENTRIES_VISITORS'));
+ }
+ } else {
+ $announcementListPlugin->assignTemplateVariable('message', $gL10n->get('SYS_MODULE_DISABLED'));
+ }
+
+ if (isset($page)) {
+ echo $announcementListPlugin->html('plugin.announcement-list.tpl');
+ } else {
+ $announcementListPlugin->showHtmlPage('plugin.announcement-list.tpl');
+ }
+ } catch (Throwable $e) {
+ echo $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/announcement-list/classes/Presenter/AnnouncementListPreferencesPresenter.php b/plugins/announcement-list/classes/Presenter/AnnouncementListPreferencesPresenter.php
new file mode 100644
index 0000000000..94eb0beabf
--- /dev/null
+++ b/plugins/announcement-list/classes/Presenter/AnnouncementListPreferencesPresenter.php
@@ -0,0 +1,107 @@
+ 'save', 'panel' => 'announcement_list')),
+ 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')
+ );
+ $formAnnouncementList->addSelectBox(
+ 'announcement_list_plugin_enabled',
+ Language::translateIfTranslationStrId($formValues['announcement_list_plugin_enabled']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['announcement_list_plugin_enabled']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['announcement_list_plugin_enabled']['description'])
+ );
+ $formAnnouncementList->addInput(
+ 'announcement_list_announcements_count',
+ Language::translateIfTranslationStrId($formValues['announcement_list_announcements_count']['name']),
+ $formValues['announcement_list_announcements_count']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'maxNumber' => 20, 'step' => 1, 'helpTextId' => $formValues['announcement_list_announcements_count']['description'])
+ );
+ $formAnnouncementList->addInput(
+ 'announcement_list_show_preview_chars',
+ Language::translateIfTranslationStrId($formValues['announcement_list_show_preview_chars']['name']),
+ $formValues['announcement_list_show_preview_chars']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['announcement_list_show_preview_chars']['description'])
+ );
+ $formAnnouncementList->addCheckbox(
+ 'announcement_list_show_full_description',
+ Language::translateIfTranslationStrId($formValues['announcement_list_show_full_description']['name']),
+ $formValues['announcement_list_show_full_description']['value'],
+ array('helpTextId' => $formValues['announcement_list_show_full_description']['description'])
+ );
+ $formAnnouncementList->addInput(
+ 'announcement_list_chars_before_linebreak',
+ Language::translateIfTranslationStrId($formValues['announcement_list_chars_before_linebreak']['name']),
+ $formValues['announcement_list_chars_before_linebreak']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['announcement_list_chars_before_linebreak']['description'])
+ );
+
+ $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('ANN'));
+ $sql = 'SELECT cat.cat_id, cat.cat_name
+ FROM ' . TBL_ANNOUNCEMENTS . ' AS ann
+ INNER JOIN ' . TBL_CATEGORIES . ' AS cat
+ WHERE cat_id IN (' . $gDb->getQmForValues($catIdParams) . ')
+ ORDER BY ann_timestamp_create DESC';
+ $sqlData = array(
+ 'query' => $sql,
+ 'params' => $catIdParams
+ );
+
+ $formAnnouncementList->addSelectBoxFromSql(
+ 'announcement_list_displayed_categories',
+ Language::translateIfTranslationStrId($formValues['announcement_list_displayed_categories']['name']),
+ $gDb,
+ $sqlData,
+ array('defaultValue' => $formValues['announcement_list_displayed_categories']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['announcement_list_displayed_categories']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($gCurrentUser->getAllVisibleCategories('ANN')))
+ );
+ $formAnnouncementList->addSubmitButton(
+ 'adm_button_save_announcement_list',
+ $gL10n->get('SYS_SAVE'),
+ array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
+ );
+
+ $formAnnouncementList->addToSmarty($smarty);
+ $gCurrentSession->addFormObject($formAnnouncementList);
+ return $smarty->fetch($pluginAnnouncementList::getPluginPath() . '/templates/preferences.plugin.announcement-list.tpl');
+ }
+}
\ No newline at end of file
diff --git a/plugins/announcement-list/classes/Service/UpdateStepsCode.php b/plugins/announcement-list/classes/Service/UpdateStepsCode.php
new file mode 100644
index 0000000000..b314d41c51
--- /dev/null
+++ b/plugins/announcement-list/classes/Service/UpdateStepsCode.php
@@ -0,0 +1,97 @@
+ $varValue) {
+ if ($varName === 'plg_announcements_count') {
+ if ($gSettingsManager->getInt('announcement_list_announcements_count') !== (int)$varValue) {
+ $gSettingsManager->set('announcement_list_announcements_count', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_preview') {
+ if ($gSettingsManager->getInt('announcement_list_show_preview_chars') !== (int)$varValue) {
+ $gSettingsManager->set('announcement_list_show_preview_chars', (int)$varValue);
+ }
+ } elseif ($varName === 'plgShowFullDescription') {
+ if ($gSettingsManager->getBool('announcement_list_show_full_description') !== (bool)$varValue) {
+ $gSettingsManager->set('announcement_list_show_full_description', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_max_char_per_word') {
+ if ($gSettingsManager->getInt('announcement_list_chars_before_linebreak') !== (int)$varValue) {
+ $gSettingsManager->set('announcement_list_chars_before_linebreak', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_categories') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ if ($categories !== array('All')) {
+ $categoryIds = array();
+ foreach ($categories as $category) {
+ // check if the category name exists and get the category id
+ $sql = 'SELECT cat_id
+ FROM ' . TBL_CATEGORIES . '
+ WHERE cat_name = ?
+ AND cat_type = \'ANN\'';
+ $pdoStatement = self::$db->queryPrepared($sql, array($category));
+ $row = $pdoStatement->fetch();
+ if ($row !== false) {
+ $categoryIds[] = (int)$row['cat_id'];
+ }
+ }
+ // if there are no valid category ids found, get all visible categories for the current user
+ if (empty($categoryIds)) {
+ $categoryIds = array('All');
+ }
+ } else {
+ $categoryIds = array('All');
+ }
+
+ $categoriesString = implode(',', $categoryIds);
+ if ($gSettingsManager->get('announcement_list_displayed_categories') !== $categoriesString) {
+ $gSettingsManager->set('announcement_list_displayed_categories', $categoriesString);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/announcement-list/db_scripts/update_1_0.xml b/plugins/announcement-list/db_scripts/update_1_0.xml
new file mode 100644
index 0000000000..c0cffa9222
--- /dev/null
+++ b/plugins/announcement-list/db_scripts/update_1_0.xml
@@ -0,0 +1,5 @@
+
+
+ UpdateStepsCode::updateStep10RetrievePreviousSettings
+ stop
+
diff --git a/plugins/announcement-list/index.php b/plugins/announcement-list/index.php
new file mode 100644
index 0000000000..99bac96ffe
--- /dev/null
+++ b/plugins/announcement-list/index.php
@@ -0,0 +1,24 @@
+doRender(isset($page) ? $page : null);
+
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
diff --git a/adm_plugins/announcement-list/languages/bg.xml b/plugins/announcement-list/languages/bg.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/bg.xml
rename to plugins/announcement-list/languages/bg.xml
diff --git a/adm_plugins/announcement-list/languages/da.xml b/plugins/announcement-list/languages/da.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/da.xml
rename to plugins/announcement-list/languages/da.xml
diff --git a/adm_plugins/announcement-list/languages/de-DE.xml b/plugins/announcement-list/languages/de-DE.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/de-DE.xml
rename to plugins/announcement-list/languages/de-DE.xml
diff --git a/adm_plugins/announcement-list/languages/de.xml b/plugins/announcement-list/languages/de.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/de.xml
rename to plugins/announcement-list/languages/de.xml
diff --git a/adm_plugins/announcement-list/languages/el.xml b/plugins/announcement-list/languages/el.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/el.xml
rename to plugins/announcement-list/languages/el.xml
diff --git a/plugins/announcement-list/languages/en.xml b/plugins/announcement-list/languages/en.xml
new file mode 100644
index 0000000000..1bf9ad93ca
--- /dev/null
+++ b/plugins/announcement-list/languages/en.xml
@@ -0,0 +1,19 @@
+
+
+ All entries
+ Announcements
+ There are no announcements available for visitors. Log in to see the announcements.
+ This plugin displays a preview list of the latest announcements on the Overview page.
+ Announcement List
+
+ Number of announcements
+ Number of announcements to be displayed. (default: 2)
+ Characters before line break
+ Maximum number of characters in a word before a line break should be performed. (0: deactivates line break, default: 0)
+ Displayed categories
+ If you only want to show announcements of a special category you can select the categories here. (default: all categories)
+ Show full description
+ If this setting is enabled the full content of the description will be shown. Otherwise only a text preview will be displayed. (default: false)
+ Preview text
+ Number of characters of the preview text. (0: no short preview, default: 70)
+
diff --git a/adm_plugins/announcement-list/languages/es.xml b/plugins/announcement-list/languages/es.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/es.xml
rename to plugins/announcement-list/languages/es.xml
diff --git a/adm_plugins/announcement-list/languages/et.xml b/plugins/announcement-list/languages/et.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/et.xml
rename to plugins/announcement-list/languages/et.xml
diff --git a/adm_plugins/announcement-list/languages/fi.xml b/plugins/announcement-list/languages/fi.xml
old mode 100755
new mode 100644
similarity index 100%
rename from adm_plugins/announcement-list/languages/fi.xml
rename to plugins/announcement-list/languages/fi.xml
diff --git a/adm_plugins/announcement-list/languages/fr.xml b/plugins/announcement-list/languages/fr.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/fr.xml
rename to plugins/announcement-list/languages/fr.xml
diff --git a/adm_plugins/announcement-list/languages/hu.xml b/plugins/announcement-list/languages/hu.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/hu.xml
rename to plugins/announcement-list/languages/hu.xml
diff --git a/adm_plugins/announcement-list/languages/nb.xml b/plugins/announcement-list/languages/nb.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/nb.xml
rename to plugins/announcement-list/languages/nb.xml
diff --git a/adm_plugins/announcement-list/languages/nl.xml b/plugins/announcement-list/languages/nl.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/nl.xml
rename to plugins/announcement-list/languages/nl.xml
diff --git a/adm_plugins/announcement-list/languages/pl.xml b/plugins/announcement-list/languages/pl.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/pl.xml
rename to plugins/announcement-list/languages/pl.xml
diff --git a/adm_plugins/announcement-list/languages/pt-BR.xml b/plugins/announcement-list/languages/pt-BR.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/pt-BR.xml
rename to plugins/announcement-list/languages/pt-BR.xml
diff --git a/adm_plugins/announcement-list/languages/pt.xml b/plugins/announcement-list/languages/pt.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/pt.xml
rename to plugins/announcement-list/languages/pt.xml
diff --git a/adm_plugins/announcement-list/languages/ru.xml b/plugins/announcement-list/languages/ru.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/ru.xml
rename to plugins/announcement-list/languages/ru.xml
diff --git a/adm_plugins/announcement-list/languages/sv.xml b/plugins/announcement-list/languages/sv.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/sv.xml
rename to plugins/announcement-list/languages/sv.xml
diff --git a/adm_plugins/announcement-list/languages/uk.xml b/plugins/announcement-list/languages/uk.xml
similarity index 100%
rename from adm_plugins/announcement-list/languages/uk.xml
rename to plugins/announcement-list/languages/uk.xml
diff --git a/adm_plugins/announcement-list/templates/plugin.announcement-list.tpl b/plugins/announcement-list/templates/plugin.announcement-list.tpl
similarity index 100%
rename from adm_plugins/announcement-list/templates/plugin.announcement-list.tpl
rename to plugins/announcement-list/templates/plugin.announcement-list.tpl
diff --git a/plugins/announcement-list/templates/preferences.plugin.announcement-list.tpl b/plugins/announcement-list/templates/preferences.plugin.announcement-list.tpl
new file mode 100644
index 0000000000..3688514ee4
--- /dev/null
+++ b/plugins/announcement-list/templates/preferences.plugin.announcement-list.tpl
@@ -0,0 +1,15 @@
+
+{$javascript}
\ No newline at end of file
diff --git a/plugins/birthday/birthday.json b/plugins/birthday/birthday.json
new file mode 100644
index 0000000000..c97f28443b
--- /dev/null
+++ b/plugins/birthday/birthday.json
@@ -0,0 +1,105 @@
+{
+ "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",
+ "overviewPlugin": true,
+ "autoload": {
+ "psr-4": {
+ "Birthday\\classes\\": "classes/"
+ }
+ },
+ "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_overview_sequence": {
+ "type": "integer",
+ "value" : 2
+ },
+ "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"
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/birthday/classes/Birthday.php b/plugins/birthday/classes/Birthday.php
new file mode 100644
index 0000000000..22953dc7de
--- /dev/null
+++ b/plugins/birthday/classes/Birthday.php
@@ -0,0 +1,430 @@
+findAll($roleType);
+
+ foreach ($data as $rowViewRoles) {
+ if ($onlyIds) {
+ // If only the IDs are requested, return an array with the role IDs
+ $allRolesSet[] = $rowViewRoles['rol_id'];
+ } else {
+ // Each role is now added to this array
+ $allRolesSet[] = array(
+ $rowViewRoles['rol_id'], // ID
+ $rowViewRoles['rol_name']
+ );
+ }
+ }
+ return $allRolesSet;
+ }
+
+ private static function getBirthdaysData() : array
+ {
+ global $gSettingsManager, $gCurrentUser, $gDb, $gL10n, $gProfileFields, $gValidLogin, $gDbType, $gCurrentOrgId;
+
+ $config = self::getPluginConfigValues();
+
+ // check if only members of configured roles could view birthday
+ if ($gValidLogin) {
+ if (isset($config['birthday_roles_view_plugin']) && count($config['birthday_roles_view_plugin']) > 0) {
+ // current user must be member of at least one listed role
+ if (count(array_intersect($config['birthday_roles_view_plugin'], $gCurrentUser->getRoleMemberships())) > 0) {
+ self::$birthdayShowNames = true;
+ }
+ }
+ } else {
+ if ($config['birthday_show_names_extern'] === 1) {
+ // every visitor is allowed to view birthdays
+ self::$birthdayShowNames = true;
+ }
+ }
+
+ // Check if the role condition has been set
+ if (!empty($config['birthday_roles_sql'])) {
+ $sqlRol = 'IN (' . implode(',', $config['birthday_roles_sql']) . ')';
+ } else {
+ $sqlRol = 'IS NOT NULL';
+ }
+
+ // Check if the sort condition has been set
+ if (!isset($config['birthday_sort_sql']) || $config['birthday_sort_sql'] === '') {
+ $sqlSort = 'DESC';
+ } else {
+ $sqlSort = $config['birthday_sort_sql'];
+ }
+
+ // if no birthdays should be shown than disable future and former birthday periods
+ if (!self::$birthdayShowNames) {
+ $config['birthday_show_past'] = 0;
+ $config['birthday_show_future'] = 0;
+ }
+
+ $fieldBirthday = $gProfileFields->getProperty('BIRTHDAY', 'usf_id');
+
+ if ($gDbType === 'pgsql') {
+ $sql = 'SELECT DISTINCT usr_id, usr_uuid, usr_login_name,
+ last_name.usd_value AS last_name, first_name.usd_value AS first_name,
+ birthday.bday AS birthday, birthday.bdate,
+ EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - birthday.bdate) * (-1) AS days_to_bdate, -- DATE_NOW
+ EXTRACT(YEAR FROM bdate) - EXTRACT(YEAR FROM TO_TIMESTAMP(bday, \'YYYY-MM-DD\')) AS age,
+ email.usd_value AS email, gender.usd_value AS gender
+ FROM ' . TBL_USERS . ' AS users
+ INNER JOIN ( (SELECT usd_usr_id, usd_value AS bday,
+ TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')) || TO_CHAR(TO_TIMESTAMP(bd1.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd1
+ WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')) || TO_CHAR(TO_TIMESTAMP(bd1.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ UNION
+ (SELECT usd_usr_id, usd_value AS bday,
+ TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\'))-1 || TO_CHAR(TO_TIMESTAMP(bd2.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd2
+ WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')- INTERVAL \'1 year\') || TO_CHAR(TO_TIMESTAMP(bd2.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ UNION
+ (SELECT usd_usr_id, usd_value AS bday,
+ TO_DATE(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\'))+1 || TO_CHAR(TO_TIMESTAMP(bd3.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\') AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd3
+ WHERE EXTRACT(DAY FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\') - TO_TIMESTAMP(EXTRACT(YEAR FROM TO_TIMESTAMP(?, \'YYYY-MM-DD\')+ INTERVAL \'1 year\') || TO_CHAR(TO_TIMESTAMP(bd3.usd_value, \'YYYY-MM-DD\'), \'-MM-DD\'), \'YYYY-MM-DD\')) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ ) AS birthday
+ ON birthday.usd_usr_id = usr_id
+ LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
+ ON last_name.usd_usr_id = usr_id
+ AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
+ ON first_name.usd_usr_id = usr_id
+ AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS email
+ ON email.usd_usr_id = usr_id
+ AND email.usd_usf_id = ? -- $gProfileFields->getProperty(\'EMAIL\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS gender
+ ON gender.usd_usr_id = usr_id
+ AND gender.usd_usf_id = ? -- $gProfileFields->getProperty(\'GENDER\', \'usf_id\')
+ LEFT JOIN ' . TBL_MEMBERS . '
+ ON mem_usr_id = usr_id
+ AND mem_begin <= ? -- DATE_NOW
+ AND mem_end > ? -- DATE_NOW
+ INNER JOIN ' . TBL_ROLES . '
+ ON mem_rol_id = rol_id
+ AND rol_valid = true
+ INNER JOIN ' . TBL_CATEGORIES . '
+ ON rol_cat_id = cat_id
+ AND cat_org_id = ? -- $gCurrentOrgId
+ WHERE usr_valid = true
+ AND mem_rol_id ' . $sqlRol . '
+ ORDER BY days_to_bdate ' . $sqlSort . ', last_name, first_name';
+ } else {
+ $sql = 'SELECT DISTINCT usr_id, usr_uuid, usr_login_name,
+ last_name.usd_value AS last_name, first_name.usd_value AS first_name,
+ birthday.bday AS birthday, birthday.bdate,
+ DATEDIFF(birthday.bdate, ?) AS days_to_bdate, -- DATE_NOW
+ YEAR(bdate) - YEAR(bday) AS age,
+ email.usd_value AS email, gender.usd_value AS gender
+ FROM ' . TBL_USERS . ' AS users
+ INNER JOIN ( (SELECT usd_usr_id, usd_value AS bday,
+ CONCAT(YEAR(?), DATE_FORMAT(bd1.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd1
+ WHERE DATEDIFF(CONCAT(YEAR(?), DATE_FORMAT(bd1.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ UNION
+ (SELECT usd_usr_id, usd_value AS bday,
+ CONCAT(YEAR(?)-1, DATE_FORMAT(bd2.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd2
+ WHERE DATEDIFF(CONCAT(YEAR(?)-1, DATE_FORMAT(bd2.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ UNION
+ (SELECT usd_usr_id, usd_value AS bday,
+ CONCAT(YEAR(?)+1, DATE_FORMAT(bd3.usd_value, \'-%m-%d\')) AS bdate -- DATE_NOW
+ FROM ' . TBL_USER_DATA . ' AS bd3
+ WHERE DATEDIFF(CONCAT(YEAR(?)+1, DATE_FORMAT(bd3.usd_value, \'-%m-%d\')), ?) -- DATE_NOW,DATE_NOW
+ BETWEEN ? AND ? -- -$config[\'birthday_show_past\'] AND $config[\'birthday_show_future\']
+ AND usd_usf_id = ?) -- $fieldBirthday
+ ) AS birthday
+ ON birthday.usd_usr_id = usr_id
+ LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
+ ON last_name.usd_usr_id = usr_id
+ AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
+ ON first_name.usd_usr_id = usr_id
+ AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS email
+ ON email.usd_usr_id = usr_id
+ AND email.usd_usf_id = ? -- $gProfileFields->getProperty(\'EMAIL\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS gender
+ ON gender.usd_usr_id = usr_id
+ AND gender.usd_usf_id = ? -- $gProfileFields->getProperty(\'GENDER\', \'usf_id\')
+ LEFT JOIN ' . TBL_MEMBERS . '
+ ON mem_usr_id = usr_id
+ AND mem_begin <= ? -- DATE_NOW
+ AND mem_end > ? -- DATE_NOW
+ INNER JOIN ' . TBL_ROLES . '
+ ON mem_rol_id = rol_id
+ AND rol_valid = true
+ INNER JOIN ' . TBL_CATEGORIES . '
+ ON rol_cat_id = cat_id
+ AND cat_org_id = ? -- $gCurrentOrgId
+ WHERE usr_valid = true
+ AND mem_rol_id ' . $sqlRol . '
+ ORDER BY days_to_bdate ' . $sqlSort . ', last_name, first_name';
+ }
+
+ $queryParams = array(
+ DATE_NOW,
+ DATE_NOW, DATE_NOW, DATE_NOW, -$config['birthday_show_past'], $config['birthday_show_future'], $fieldBirthday,
+ DATE_NOW, DATE_NOW, DATE_NOW, -$config['birthday_show_past'], $config['birthday_show_future'], $fieldBirthday,
+ DATE_NOW, DATE_NOW, DATE_NOW, -$config['birthday_show_past'], $config['birthday_show_future'], $fieldBirthday,
+ $gProfileFields->getProperty('LAST_NAME', 'usf_id'),
+ $gProfileFields->getProperty('FIRST_NAME', 'usf_id'),
+ $gProfileFields->getProperty('EMAIL', 'usf_id'),
+ $gProfileFields->getProperty('GENDER', 'usf_id'),
+ DATE_NOW,
+ DATE_NOW,
+ $gCurrentOrgId
+ // TODO add more params
+ );
+ $birthdayStatement = $gDb->queryPrepared($sql, $queryParams);
+
+ $numberBirthdays = $birthdayStatement->rowCount();
+
+ $birthdayArray = array();
+
+ if ($numberBirthdays > 0) {
+ if (self::$birthdayShowNames) {
+ // how many birthdays should be displayed (as a maximum)
+ $birthdayCount = null;
+
+ while (($row = $birthdayStatement->fetch()) && $birthdayCount < $config['birthday_show_display_limit']) {
+ // the display type of the name
+ switch ($config['birthday_show_names']) {
+ case 1: // first name, last name
+ $plgShowName = $row['first_name'] . ' ' . $row['last_name'];
+ break;
+ case 2: // last name, first name
+ $plgShowName = $row['last_name'] . ', ' . $row['first_name'];
+ break;
+ case 3: // first name
+ $plgShowName = $row['first_name'];
+ break;
+ case 4: // Loginname
+ $plgShowName = $row['usr_login_name'];
+ break;
+ default: // first name, last name
+ $plgShowName = $row['first_name'] . ' ' . $row['last_name'];
+ }
+
+ // from a specified age, only the last name with salutation is displayed for logged out visitors
+ if ($config['birthday_show_age_salutation'] > -1) {
+ if (!$gValidLogin && $config['birthday_show_age_salutation'] <= $row['age']) {
+ if ($row['gender'] > 1) {
+ $plgShowName = $gL10n->get('PLG_BIRTHDAY_WOMAN_VAR', array($row['last_name']));
+ } else {
+ $plgShowName = $gL10n->get('PLG_BIRTHDAY_MAN_VAR', array($row['last_name']));
+ }
+ }
+ }
+
+ // show name and e-mail link for registered users
+ if ($gValidLogin) {
+ $plgShowName = '' . $plgShowName . '';
+
+ // E-Mail-Adresse ist hinterlegt und soll auch bei eingeloggten Benutzern verlinkt werden
+ if ((string)$row['email'] !== '' && $config['birthday_show_email_extern'] < 2) {
+ $plgShowName .= '
+ ' .
+ '';
+ }
+ } elseif ($config['birthday_show_email_extern'] === 1 && strlen($row['email']) > 0) {
+ $plgShowName .= '
+ ' .
+ '';
+ }
+
+ // set css class and string for birthday today, in the future or in the past
+ $birthdayDate = DateTime::createFromFormat('Y-m-d', $row['birthday']);
+
+ if ($row['days_to_bdate'] < 0) {
+ $plgCssClass = 'plgBirthdayNameHighlightAgo';
+ if ($row['days_to_bdate'] == -1) {
+ $birthdayText = 'PLG_BIRTHDAY_YESTERDAY';
+ $plgDays = ' ';
+ } else {
+ $birthdayText = 'PLG_BIRTHDAY_PAST';
+ $plgDays = -$row['days_to_bdate'];
+ }
+ } elseif ($row['days_to_bdate'] > 0) {
+ $plgCssClass = 'plgBirthdayNameHighlightFuture';
+ if ($row['days_to_bdate'] == 1) {
+ $birthdayText = 'PLG_BIRTHDAY_TOMORROW';
+ $plgDays = ' ';
+ } else {
+ $birthdayText = 'PLG_BIRTHDAY_FUTURE';
+ $plgDays = $row['days_to_bdate'];
+ }
+ } else {
+ $plgCssClass = 'plgBirthdayNameHighlight';
+ $birthdayText = 'PLG_BIRTHDAY_TODAY';
+ $plgDays = $row['age'];
+ }
+
+ // don't show age of birthday person if preference is set
+ if ($config['birthday_show_age'] === 0 || !$gValidLogin) {
+ $birthdayText .= '_NO_AGE';
+ }
+
+ $birthdayArray[] = array(
+ 'userText' => $gL10n->get($birthdayText, array($plgShowName, $plgDays, $row['age'], $birthdayDate->format($gSettingsManager->getString('system_date'))))
+ );
+
+ // counting displayed birthdays
+ $birthdayCount++;
+ }
+ }
+ }
+
+ return $birthdayArray;
+ }
+
+ /**
+ * @param PagePresenter $page
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @return bool
+ */
+ public static function doRender($page = null) : bool
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+
+ // show the announcement list
+ try {
+ $rootPath = dirname(__DIR__, 3);
+ $pluginFolder = basename(self::$pluginPath);
+
+ require_once($rootPath . '/system/common.php');
+
+ $birthdayPlugin = new Overview($pluginFolder);
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ throw new InvalidArgumentException($gL10n->get('SYS_PLUGIN_NOT_INSTALLED'));
+ }
+
+ if ($gSettingsManager->getInt('events_module_enabled') > 0) {
+ if ($gSettingsManager->getInt('birthday_plugin_enabled') === 1 || ($gSettingsManager->getInt('birthday_plugin_enabled') === 2 && $gValidLogin)) {
+ $birthdaysArray = self::getBirthdaysData();
+
+ if (!empty($birthdaysArray)) {
+ if (self::$birthdayShowNames) {
+ $birthdayPlugin->assignTemplateVariable('birthdays', $birthdaysArray);
+ } else {
+ if (count($birthdaysArray) === 1) {
+ $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_ONE_MEMBER'));
+ } else {
+ $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_MORE_MEMBERS', array(count($birthdaysArray))));
+ }
+ }
+ } else {
+ // If the configuration is set accordingly, a message is output if no member has a birthday today
+ if ($gSettingsManager->getBool('birthday_show_notice_none')) {
+ $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_NO_MEMBERS'));
+ }
+ }
+ } else {
+ $birthdayPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_NO_ENTRIES_VISITORS'));
+ }
+ } else {
+ $birthdayPlugin->assignTemplateVariable('message', $gL10n->get('SYS_MODULE_DISABLED'));
+ }
+
+ if (isset($page)) {
+ echo $birthdayPlugin->html('plugin.birthday.tpl');
+ } else {
+ $birthdayPlugin->showHtmlPage('plugin.birthday.tpl');
+ }
+ } catch (Throwable $e) {
+ echo $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/birthday/classes/Presenter/BirthdayPreferencesPresenter.php b/plugins/birthday/classes/Presenter/BirthdayPreferencesPresenter.php
new file mode 100644
index 0000000000..bbe00e9319
--- /dev/null
+++ b/plugins/birthday/classes/Presenter/BirthdayPreferencesPresenter.php
@@ -0,0 +1,155 @@
+ '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'])
+ );
+ $selectBoxEntries = array(
+ '0' => $gL10n->get('SYS_FIRSTNAME') . ' ' . $gL10n->get('SYS_LASTNAME'),
+ '1' => $gL10n->get('SYS_LASTNAME') . ', ' . $gL10n->get('SYS_FIRSTNAME'),
+ '2' => $gL10n->get('SYS_FIRSTNAME'),
+ '3' => $gL10n->get('SYS_USERNAME')
+ );
+ $formBirthday->addSelectBox(
+ 'birthday_show_names',
+ Language::translateIfTranslationStrId($formValues['birthday_show_names']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['birthday_show_names']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_show_names']['description'])
+ );
+ $formBirthday->addCheckbox(
+ 'birthday_show_age',
+ Language::translateIfTranslationStrId($formValues['birthday_show_age']['name']),
+ $formValues['birthday_show_age']['value'],
+ array('helpTextId' => $formValues['birthday_show_age']['description'])
+ );
+ $formBirthday->addInput(
+ 'birthday_show_age_salutation',
+ Language::translateIfTranslationStrId($formValues['birthday_show_age_salutation']['name']),
+ $formValues['birthday_show_age_salutation']['value'],
+ array('type' => 'number', 'minNumber' => -1, 'step' => 1, 'helpTextId' => $formValues['birthday_show_age_salutation']['description'])
+ );
+ $formBirthday->addCheckbox(
+ 'birthday_show_notice_none',
+ Language::translateIfTranslationStrId($formValues['birthday_show_notice_none']['name']),
+ $formValues['birthday_show_notice_none']['value'],
+ array('helpTextId' => $formValues['birthday_show_notice_none']['description'])
+ );
+ $formBirthday->addInput(
+ 'birthday_show_past',
+ Language::translateIfTranslationStrId($formValues['birthday_show_past']['name']),
+ $formValues['birthday_show_past']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['birthday_show_past']['description'])
+ );
+ $formBirthday->addInput(
+ 'birthday_show_future',
+ Language::translateIfTranslationStrId($formValues['birthday_show_future']['name']),
+ $formValues['birthday_show_future']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['birthday_show_future']['description'])
+ );
+ $formBirthday->addInput(
+ 'birthday_show_display_limit',
+ Language::translateIfTranslationStrId($formValues['birthday_show_display_limit']['name']),
+ $formValues['birthday_show_display_limit']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['birthday_show_display_limit']['description'])
+ );
+ $selectBoxEntries = array(
+ '0' => $gL10n->get('SYS_NAME') . ' (' . $gL10n->get('SYS_VISITORS') . ')',
+ '1' => $gL10n->get('SYS_NAME') . ' + ' . $gL10n->get('SYS_EMAIL'),
+ '2' => $gL10n->get('SYS_NAME') . ' (' . $gL10n->get('SYS_VISITORS') . ' + ' . $gL10n->get('SYS_USERS') . ')'
+ );
+ $formBirthday->addSelectBox(
+ 'birthday_show_email_extern',
+ Language::translateIfTranslationStrId($formValues['birthday_show_email_extern']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['birthday_show_email_extern']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_show_email_extern']['description'])
+ );
+
+ $selectBoxEntries = $pluginBirthday instanceof Birthday ? $pluginBirthday::getAvailableRoles() : array();
+
+ $formBirthday->addSelectBox(
+ 'birthday_roles_view_plugin',
+ Language::translateIfTranslationStrId($formValues['birthday_roles_view_plugin']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['birthday_roles_view_plugin']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_roles_view_plugin']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($selectBoxEntries))
+ );
+ $formBirthday->addSelectBox(
+ 'birthday_roles_sql',
+ Language::translateIfTranslationStrId($formValues['birthday_roles_sql']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['birthday_roles_sql']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_roles_sql']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($selectBoxEntries))
+ );
+
+ $selectBoxEntries = array(
+ 'ASC' => 'ASC',
+ 'DESC' => 'DESC'
+ );
+ $formBirthday->addSelectBox(
+ 'birthday_sort_sql',
+ Language::translateIfTranslationStrId($formValues['birthday_sort_sql']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['birthday_sort_sql']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['birthday_sort_sql']['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');
+ }
+}
\ No newline at end of file
diff --git a/plugins/birthday/classes/Service/UpdateStepsCode.php b/plugins/birthday/classes/Service/UpdateStepsCode.php
new file mode 100644
index 0000000000..b236bb1c96
--- /dev/null
+++ b/plugins/birthday/classes/Service/UpdateStepsCode.php
@@ -0,0 +1,105 @@
+ $varValue) {
+ if ($varName === 'plg_show_names_extern') {
+ if ($gSettingsManager->getBool('birthday_show_names_extern') !== (bool)$varValue) {
+ $gSettingsManager->set('birthday_show_names_extern', (bool)$varValue);
+ }
+ } elseif ($varName === 'birthday_show_names') {
+ if ($gSettingsManager->getInt('birthday_show_names') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_names', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_age') {
+ if ($gSettingsManager->getBool('birthday_show_age') !== (bool)$varValue) {
+ $gSettingsManager->set('birthday_show_age', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_show_alter_anrede') {
+ if ($gSettingsManager->getInt('birthday_show_age_salutation') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_age_salutation', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_hinweis_keiner') {
+ if ($gSettingsManager->getBool('birthday_show_notice_none') !== (bool)$varValue) {
+ $gSettingsManager->set('birthday_show_notice_none', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_show_zeitraum') {
+ if ($gSettingsManager->getInt('birthday_show_past') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_past', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_future') {
+ if ($gSettingsManager->getInt('birthday_show_future') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_future', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_display_limit') {
+ if ($gSettingsManager->getInt('birthday_show_display_limit') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_display_limit', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_email_extern') {
+ if ($gSettingsManager->getInt('birthday_show_email_extern') !== (int)$varValue) {
+ $gSettingsManager->set('birthday_show_email_extern', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_birthday_roles_view_plugin') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ $categoriesString = implode(',', $categories);
+ if ($gSettingsManager->get('birthday_roles_view_plugin') !== $categoriesString) {
+ $gSettingsManager->set('birthday_roles_view_plugin', $categoriesString);
+ }
+ } elseif ($varName === 'plg_rolle_sql') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ $categoriesString = implode(',', $categories);
+ if ($gSettingsManager->get('birthday_roles_sql') !== $categoriesString) {
+ $gSettingsManager->set('birthday_roles_sql', $categoriesString);
+ }
+ } elseif ($varName === 'plg_sort_sql') {
+ if (strtolower($gSettingsManager->getString('birthday_sort_sql')) !== strtolower((string)$varValue)) {
+ $gSettingsManager->set('birthday_sort_sql', (string)$varValue);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/birthday/db_scripts/update_1_0.xml b/plugins/birthday/db_scripts/update_1_0.xml
new file mode 100644
index 0000000000..c0cffa9222
--- /dev/null
+++ b/plugins/birthday/db_scripts/update_1_0.xml
@@ -0,0 +1,5 @@
+
+
+ UpdateStepsCode::updateStep10RetrievePreviousSettings
+ stop
+
diff --git a/plugins/birthday/index.php b/plugins/birthday/index.php
new file mode 100644
index 0000000000..f36e59ce34
--- /dev/null
+++ b/plugins/birthday/index.php
@@ -0,0 +1,23 @@
+doRender(isset($page) ? $page : null);
+
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
diff --git a/adm_plugins/birthday/languages/bg.xml b/plugins/birthday/languages/bg.xml
similarity index 100%
rename from adm_plugins/birthday/languages/bg.xml
rename to plugins/birthday/languages/bg.xml
diff --git a/adm_plugins/birthday/languages/da.xml b/plugins/birthday/languages/da.xml
similarity index 100%
rename from adm_plugins/birthday/languages/da.xml
rename to plugins/birthday/languages/da.xml
diff --git a/adm_plugins/birthday/languages/de-DE.xml b/plugins/birthday/languages/de-DE.xml
similarity index 100%
rename from adm_plugins/birthday/languages/de-DE.xml
rename to plugins/birthday/languages/de-DE.xml
diff --git a/adm_plugins/birthday/languages/de.xml b/plugins/birthday/languages/de.xml
similarity index 100%
rename from adm_plugins/birthday/languages/de.xml
rename to plugins/birthday/languages/de.xml
diff --git a/adm_plugins/birthday/languages/el.xml b/plugins/birthday/languages/el.xml
similarity index 100%
rename from adm_plugins/birthday/languages/el.xml
rename to plugins/birthday/languages/el.xml
diff --git a/plugins/birthday/languages/en.xml b/plugins/birthday/languages/en.xml
new file mode 100644
index 0000000000..b5cfbbae4a
--- /dev/null
+++ b/plugins/birthday/languages/en.xml
@@ -0,0 +1,47 @@
+
+
+ #VAR1# will be #VAR3# years old in #VAR2# days, on the #VAR4#
+ #VAR1# has their birthday in #VAR2# days
+ Birthdays
+ Mr. #VAR1#
+ Today #VAR1# members have birthday.
+ There are no member's birthdays available for visitors. Log in to see the member's birthdays.
+ Today is not a member's birthday.
+ Today is a member's birthday.
+ #VAR1# became #VAR3# years old #VAR2# days before, on the #VAR4#
+ #VAR1# had their birthday #VAR2# days ago
+ This plugin displays a preview of the latest birthdays on the Overview page.
+ Birthday
+ #VAR1# will be today #VAR2#
+ #VAR1# has their birthday today
+ #VAR1# will be tomorrow #VAR3# years old, on the #VAR4#
+ #VAR1# has their birthday tomorrow
+ Mrs. #VAR1#
+ #VAR1# became #VAR3# years old yesterday on the #VAR4#
+ #VAR1# had their birthday yesterday
+
+ Show names for visitors
+ If this setting is enabled visitors will see all names (not the age) of birthday persons today, in the future and in the past. Otherwise visitors will only see the number of persons who have birthday today, nothing more. (default: false)
+ Display style for birthday person name
+ This setting allows you to choose how the names of birthday persons are displayed. (default: First name Surname)
+ Show age
+ If this setting is enabled logged in users will see the ages of birthday persons. (default: false)
+ Show name with salutation
+ Defines the age from which the first name of a birthday person will be replaced by the salutation for visitors. (disable: -1, default: 18)
+ Show no birthday persons notice
+ If this setting is enabled a notice will be shown if no member has birthday in the selected time span. (default: true)
+ Show past birthday days
+ With this setting you can define how many days in the past birthdays should be shown. (default: 1)
+ Show future birthday days
+ With this setting you can define how many days in the future birthdays should be shown. (default: 2)
+ Limit the number of birthday persons
+ With this setting you can define the maximum number of birthday persons to be displayed. (default: 200)
+ Show email address for visitors
+ This setting allows you to choose if the email address of birthday persons should be displayed. (default: only name (visitors))
+ Allowed roles for content
+ Here you can select the roles that are allowed to see the content of this plugin. If a user is not part of any selected role only the number of birthdays are shown. (default: all roles)
+ Allowed roles for birthdays
+ Here you can select the roles which users must have whose birthdays should be shown. (default: all roles)
+ Sort order of birthday persons
+ This setting allows you to choose the sort order of birthday persons. (default: DESC)
+
diff --git a/adm_plugins/birthday/languages/es.xml b/plugins/birthday/languages/es.xml
similarity index 100%
rename from adm_plugins/birthday/languages/es.xml
rename to plugins/birthday/languages/es.xml
diff --git a/adm_plugins/birthday/languages/et.xml b/plugins/birthday/languages/et.xml
similarity index 100%
rename from adm_plugins/birthday/languages/et.xml
rename to plugins/birthday/languages/et.xml
diff --git a/adm_plugins/birthday/languages/fi.xml b/plugins/birthday/languages/fi.xml
similarity index 100%
rename from adm_plugins/birthday/languages/fi.xml
rename to plugins/birthday/languages/fi.xml
diff --git a/adm_plugins/birthday/languages/fr.xml b/plugins/birthday/languages/fr.xml
similarity index 100%
rename from adm_plugins/birthday/languages/fr.xml
rename to plugins/birthday/languages/fr.xml
diff --git a/adm_plugins/birthday/languages/hu.xml b/plugins/birthday/languages/hu.xml
similarity index 100%
rename from adm_plugins/birthday/languages/hu.xml
rename to plugins/birthday/languages/hu.xml
diff --git a/adm_plugins/birthday/languages/nb.xml b/plugins/birthday/languages/nb.xml
similarity index 100%
rename from adm_plugins/birthday/languages/nb.xml
rename to plugins/birthday/languages/nb.xml
diff --git a/adm_plugins/birthday/languages/nl.xml b/plugins/birthday/languages/nl.xml
similarity index 100%
rename from adm_plugins/birthday/languages/nl.xml
rename to plugins/birthday/languages/nl.xml
diff --git a/adm_plugins/birthday/languages/pl.xml b/plugins/birthday/languages/pl.xml
similarity index 100%
rename from adm_plugins/birthday/languages/pl.xml
rename to plugins/birthday/languages/pl.xml
diff --git a/adm_plugins/birthday/languages/pt-BR.xml b/plugins/birthday/languages/pt-BR.xml
similarity index 100%
rename from adm_plugins/birthday/languages/pt-BR.xml
rename to plugins/birthday/languages/pt-BR.xml
diff --git a/adm_plugins/birthday/languages/pt.xml b/plugins/birthday/languages/pt.xml
similarity index 100%
rename from adm_plugins/birthday/languages/pt.xml
rename to plugins/birthday/languages/pt.xml
diff --git a/adm_plugins/birthday/languages/ru.xml b/plugins/birthday/languages/ru.xml
similarity index 100%
rename from adm_plugins/birthday/languages/ru.xml
rename to plugins/birthday/languages/ru.xml
diff --git a/adm_plugins/birthday/languages/sv.xml b/plugins/birthday/languages/sv.xml
similarity index 100%
rename from adm_plugins/birthday/languages/sv.xml
rename to plugins/birthday/languages/sv.xml
diff --git a/adm_plugins/birthday/languages/uk.xml b/plugins/birthday/languages/uk.xml
similarity index 100%
rename from adm_plugins/birthday/languages/uk.xml
rename to plugins/birthday/languages/uk.xml
diff --git a/adm_plugins/birthday/languages/zh.xml b/plugins/birthday/languages/zh.xml
similarity index 100%
rename from adm_plugins/birthday/languages/zh.xml
rename to plugins/birthday/languages/zh.xml
diff --git a/adm_plugins/birthday/templates/plugin.birthday.tpl b/plugins/birthday/templates/plugin.birthday.tpl
similarity index 100%
rename from adm_plugins/birthday/templates/plugin.birthday.tpl
rename to plugins/birthday/templates/plugin.birthday.tpl
diff --git a/plugins/birthday/templates/preferences.plugin.birthday.tpl b/plugins/birthday/templates/preferences.plugin.birthday.tpl
new file mode 100644
index 0000000000..74aec2db77
--- /dev/null
+++ b/plugins/birthday/templates/preferences.plugin.birthday.tpl
@@ -0,0 +1,23 @@
+
+{$javascript}
\ No newline at end of file
diff --git a/plugins/calendar/calendar.json b/plugins/calendar/calendar.json
new file mode 100644
index 0000000000..2531e5ab13
--- /dev/null
+++ b/plugins/calendar/calendar.json
@@ -0,0 +1,88 @@
+{
+ "name": "PLG_CALENDAR_PLUGIN_NAME",
+ "description": "PLG_CALENDAR_PLUGIN_DESCRIPTION",
+ "version": "1.0.0",
+ "author": "Admidio Team",
+ "url": "https://www.admidio.org",
+ "icon": "bi-calendar-week-fill",
+ "mainFile": "index.php",
+ "overviewPlugin": true,
+ "autoload": {
+ "psr-4": {
+ "Calendar\\classes\\": "classes/"
+ }
+ },
+ "dependencies": [
+ "Admidio\\Infrastructure\\Database",
+ "Admidio\\Infrastructure\\Plugins\\Overview",
+ "Admidio\\Infrastructure\\Plugins\\PluginAbstract",
+ "Admidio\\Infrastructure\\Utils\\SecurityUtils",
+ "Admidio\\Roles\\Service\\RolesService"
+ ],
+ "defaultConfig": {
+ "calendar_plugin_enabled": {
+ "name": "ORG_ACCESS_TO_PLUGIN",
+ "description": "ORG_ACCESS_TO_PLUGIN_DESC",
+ "type": "integer",
+ "value" : 1
+ },
+ "calendar_overview_sequence": {
+ "type": "integer",
+ "value" : 3
+ },
+ "calendar_show_events": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_EVENTS",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_EVENTS_DESC",
+ "type": "boolean",
+ "value" : true
+ },
+ "calendar_show_birthdays": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAYS",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAYS_DESC",
+ "type": "boolean",
+ "value" : true
+ },
+ "calendar_show_birthdays_to_guests": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAYS_TO_GUESTS",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAYS_TO_GUESTS_DESC",
+ "type": "boolean",
+ "value" : false
+ },
+ "calendar_show_birthday_icon": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAY_ICON",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAY_ICON_DESC",
+ "type": "boolean",
+ "value" : true
+ },
+ "calendar_show_birthday_names": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAY_NAMES",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_BIRTHDAY_NAMES_DESC",
+ "type": "integer",
+ "value" : 1
+ },
+ "calendar_show_categories": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_CATEGORIES",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_CATEGORIES_DESC",
+ "type": "array",
+ "value" : ["All"]
+ },
+ "calendar_show_categories_names": {
+ "name": "PLG_CALENDAR_PREFERENCES_SHOW_CATEGORIES_NAMES",
+ "description": "PLG_CALENDAR_PREFERENCES_SHOW_CATEGORIES_NAMES_DESC",
+ "type": "boolean",
+ "value" : false
+ },
+ "calendar_roles_view_plugin": {
+ "name": "PLG_CALENDAR_PREFERENCES_ROLES_VIEW_PLUGIN",
+ "description": "PLG_CALENDAR_PREFERENCES_ROLES_VIEW_PLUGIN_DESC",
+ "type": "array",
+ "value" : ["All"]
+ },
+ "calendar_roles_sql": {
+ "name": "PLG_CALENDAR_PREFERENCES_ROLES_SQL",
+ "description": "PLG_CALENDAR_PREFERENCES_ROLES_SQL_DESC",
+ "type": "array",
+ "value" : ["All"]
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/calendar/classes/Calendar.php b/plugins/calendar/classes/Calendar.php
new file mode 100644
index 0000000000..cfddd5a08a
--- /dev/null
+++ b/plugins/calendar/classes/Calendar.php
@@ -0,0 +1,592 @@
+getAllVisibleCategories('EVT');
+ }
+
+ // if the key equals 'calendar_roles_view_plugin' and the value is still the default value, retrieve the roles from the database
+ if (array_key_exists('calendar_roles_view_plugin', $config) && $config['calendar_roles_view_plugin']['value'] === self::$defaultConfig['calendar_roles_view_plugin']['value']) {
+ $config['calendar_roles_view_plugin']['value'] = self::getAvailableRoles(1, true);
+ }
+ // if the key equals 'calendar_roles_sql' and the value is still the default value, retrieve the roles from the database
+ if (array_key_exists('calendar_roles_sql', $config) && $config['calendar_roles_sql']['value'] === self::$defaultConfig['calendar_roles_sql']['value']) {
+ $config['calendar_roles_sql']['value'] = self::getAvailableRoles(1, true);
+ }
+ return $config;
+ }
+
+ /**
+ * Get the plugin configuration values
+ * @return array Returns the plugin configuration values
+ */
+ public static function getPluginConfigValues() : array
+ {
+ global $gCurrentUser;
+
+ // get the plugin config values from the parent class
+ $config = parent::getPluginConfigValues();
+
+ // if the key equals 'calendar_show_categories' and the value is still the default value, retrieve the roles from the database
+ if (array_key_exists('calendar_show_categories', $config) && $config['calendar_show_categories'] === self::$defaultConfig['calendar_show_categories']['value']) {
+ $config['calendar_show_categories'] = $gCurrentUser->getAllVisibleCategories('EVT');
+ }
+ // if the key equals 'calendar_roles_view_plugin' and the value is still the default value, retrieve the roles from the database
+ if (array_key_exists('calendar_roles_view_plugin', $config) && $config['calendar_roles_view_plugin'] === self::$defaultConfig['calendar_roles_view_plugin']['value']) {
+ $config['calendar_roles_view_plugin'] = self::getAvailableRoles(1, true);
+ }
+ // if the key equals 'calendar_roles_sql' and the value is still the default value, retrieve the roles from the database
+ if (array_key_exists('calendar_roles_sql', $config) && $config['calendar_roles_sql'] === self::$defaultConfig['calendar_roles_sql']['value']) {
+ $config['calendar_roles_sql'] = self::getAvailableRoles(1, true);
+ }
+
+ return $config;
+ }
+
+ /**
+ * Get the available roles for the calendar plugin
+ * @param int $roleType The type of roles to retrieve (0 for inactive, 1 for active, 2 for only event participation roles)
+ * @param bool $onlyIds If true, only the IDs of the roles are returned
+ * @return array Returns an array with the available roles
+ */
+ public static function getAvailableRoles($roleType = 1, bool $onlyIds = false): array {
+ global $gDb;
+
+ $allRolesSet = array();
+ $rolesService = new RolesService($gDb);
+ $data = $rolesService->findAll($roleType);
+
+ foreach ($data as $rowViewRoles) {
+ if ($onlyIds) {
+ // If only the IDs are requested, return an array with the role IDs
+ $allRolesSet[] = $rowViewRoles['rol_id'];
+ } else {
+ // Each role is now added to this array
+ $allRolesSet[] = array(
+ $rowViewRoles['rol_id'], // ID
+ $rowViewRoles['rol_name']
+ );
+ }
+ }
+ return $allRolesSet;
+ }
+
+ private static function createCalendar(array $eventsMonthDayArray, array $birthdaysMonthDayArray) : string
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+ // Kalender erstellen
+ $firstWeekdayOfMonth = (int)date('w', mktime(0, 0, 0, self::$currentMonth, 1, self::$currentYear));
+ self::$months = explode(',', $gL10n->get('PLG_CALENDAR_MONTH'));
+
+ if ($firstWeekdayOfMonth === 0) {
+ $firstWeekdayOfMonth = 7;
+ }
+
+ $tableContent = '';
+ $i = 1;
+ while ($i < $firstWeekdayOfMonth) {
+ $tableContent .= '| | ';
+ ++$i;
+ }
+
+ $currentDay = 1;
+ $boolNewStart = false;
+
+ while ($currentDay <= self::$lastDayCurrentMonth) {
+ $terLink = '';
+ $gebLink = '';
+ $htmlContent = '';
+ $textContent = '';
+ $hasEvents = false;
+ $hasBirthdays = false;
+ $countEvents = 0;
+
+ $dateObj = DateTime::createFromFormat('Y-m-j', self::$currentYear . '-' . self::$currentMonth . '-' . $currentDay);
+
+ // add events to the calendar
+ if (self::$pluginConfig['calendar_show_events']) {
+ // only show events in dependence of the events module view settings
+ if (array_key_exists($currentDay, $eventsMonthDayArray)
+ && ($gSettingsManager->getInt('events_module_enabled') === 1
+ || ($gSettingsManager->getInt('events_module_enabled') === 2 && $gValidLogin))) {
+ $hasEvents = true;
+
+ foreach ($eventsMonthDayArray[$currentDay] as $eventArray) {
+ if ($eventArray['location'] !== '') {
+ $eventArray['location'] = ', ' . $eventArray['location'];
+ }
+
+ if ($htmlContent !== '') {
+ $htmlContent .= '
';
+ }
+ if ($eventArray['all_day'] == 1) {
+ if ($eventArray['one_day']) {
+ $htmlContent .= '' . $gL10n->get('SYS_ALL_DAY') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ $textContent .= $gL10n->get('SYS_ALL_DAY') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ } else {
+ $htmlContent .= '' . $gL10n->get('PLG_CALENDAR_SEVERAL_DAYS') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ $textContent .= $gL10n->get('PLG_CALENDAR_SEVERAL_DAYS') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ }
+ } else {
+ $htmlContent .= '' . $eventArray['time'] . ' ' . $gL10n->get('SYS_CLOCK') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ $textContent .= $eventArray['time'] . ' ' . $gL10n->get('SYS_CLOCK') . ' ' . $eventArray['headline'] . $eventArray['location'];
+ }
+ ++$countEvents;
+ }
+
+ if ($countEvents > 0) {
+ $plgLink = SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/events/events.php', array('date_from' => $dateObj->format('Y-m-d'), 'date_to' => $dateObj->format('Y-m-d')));
+ }
+ }
+ }
+
+ // add users birthdays to the calendar
+ if (self::$pluginConfig['calendar_show_birthdays']) {
+ if (array_key_exists($currentDay, $birthdaysMonthDayArray) && self::$calendarShowNames) {
+ foreach ($birthdaysMonthDayArray[$currentDay] as $birthdayArray) {
+ $hasBirthdays = true;
+
+ if ($htmlContent !== '') {
+ $htmlContent .= '
';
+ $textContent .= ', ';
+ }
+
+ if (self::$pluginConfig['calendar_show_birthday_icon']) {
+ $icon = '';
+ } else {
+ $icon = '';
+ }
+
+ $htmlContent .= $icon . $birthdayArray['name'] . ' (' . $birthdayArray['age'] . ')';
+ $textContent .= $birthdayArray['name'] . ' (' . $birthdayArray['age'] . ')';
+ }
+ }
+ }
+
+ // First pre-assignment of the weekday classes
+ $plgLinkClassSaturday = 'plgCalendarSaturday';
+ $plgLinkClassSunday = 'plgCalendarSunday';
+ $plgLinkClassWeekday = 'plgCalendarDay';
+
+ if (!$hasEvents && $hasBirthdays) { // no events but birthdays
+ $plgLinkClass = 'geb';
+ $plgLinkClassSaturday .= ' plgCalendarBirthDay';
+ $plgLinkClassSunday .= ' plgCalendarBirthDay';
+ $plgLinkClassWeekday .= ' plgCalendarBirthDay';
+ }
+
+ if ($hasEvents && !$hasBirthdays) { // events but no birthdays
+ $plgLinkClass = 'date';
+ $plgLinkClassSaturday .= ' plgCalendarDateDay';
+ $plgLinkClassSunday .= ' plgCalendarDateDay';
+ $plgLinkClassWeekday .= ' plgCalendarDateDay';
+ }
+
+ if ($hasEvents && $hasBirthdays) { // events and birthdays
+ $plgLinkClass = 'merge';
+ $plgLinkClassSaturday .= ' plgCalendarMergeDay';
+ $plgLinkClassSunday .= ' plgCalendarMergeDay';
+ $plgLinkClassWeekday .= ' plgCalendarMergeDay';
+ }
+
+ if ($boolNewStart) {
+ $tableContent .= '
';
+ $boolNewStart = false;
+ }
+ $rest = ($currentDay + $firstWeekdayOfMonth - 1) % 7;
+ if ($currentDay === self::$today) {
+ $tableContent .= '| ';
+ } elseif ($rest === 6) {
+ $tableContent .= ' | ';
+ } elseif ($rest === 0) {
+ $tableContent .= ' | ';
+ } else {
+ $tableContent .= ' | ';
+ }
+
+ if ($currentDay === self::$today || $hasEvents || $hasBirthdays) {
+ if (!$hasEvents && $hasBirthdays) {
+ // Switch off link URL for birthday by #.
+ $plgLink = '#';
+ }
+
+ if ($hasEvents || $hasBirthdays) {
+ if ($terLink !== '' && $gebLink !== '') {
+ $gebLink = '&' . $gebLink;
+ }
+
+ // plg_link_class bestimmt das Erscheinungsbild des jeweiligen Links
+ $tableContent .= '' . $currentDay . '';
+ } elseif ($currentDay === self::$today) {
+ $tableContent .= '' . $currentDay . '';
+ }
+ } elseif ($rest === 6) {
+ $tableContent .= '' . $currentDay . '';
+ } elseif ($rest === 0) {
+ $tableContent .= '' . $currentDay . '';
+ } else {
+ $tableContent .= $currentDay;
+ }
+ $tableContent .= ' | ';
+ if ($rest === 0 || $currentDay === self::$lastDayCurrentMonth) {
+ $tableContent .= '
';
+ $boolNewStart = true;
+ }
+
+ ++$currentDay;
+ }
+
+ return $tableContent;
+ }
+
+ private static function getCalendarsData() : string
+ {
+ global $gSettingsManager, $gCurrentUser, $gDb, $gL10n, $gProfileFields, $gValidLogin, $gDbType, $gCurrentOrgId;
+
+ self::$pluginConfig = self::getPluginConfigValues();
+
+ // check if only members of configured roles could view birthday
+ if ($gValidLogin) {
+ if (isset(self::$pluginConfig['calendar_roles_view_plugin']) && count(self::$pluginConfig['calendar_roles_view_plugin']) > 0) {
+ // current user must be member of at least one listed role
+ if (count(array_intersect(self::$pluginConfig['calendar_roles_view_plugin'], $gCurrentUser->getRoleMemberships())) > 0) {
+ self::$calendarShowNames = true;
+ }
+ }
+ } else {
+ if (self::$pluginConfig['calendar_show_birthdays_to_guests']) {
+ // every visitor is allowed to view birthdays
+ self::$calendarShowNames = true;
+ }
+ }
+
+ // Check if the role condition has been set
+ if (!empty(self::$pluginConfig['calendar_roles_sql'])) {
+ $sqlRoleIds = 'IN (' . implode(',', self::$pluginConfig['calendar_roles_sql']) . ')';
+ } else {
+ $sqlRoleIds = 'IS NOT NULL';
+ }
+
+ $dateMonthStart = self::$currentYear . '-' . self::$currentMonth . '-01 00:00:01'; // add 1 second to ignore all day events that end at 00:00:00
+ $dateMonthEnd = self::$currentYear . '-' . self::$currentMonth . '-' . self::$lastDayCurrentMonth . ' 23:59:59';
+ $eventsMonthDayArray = array();
+ $birthdaysMonthDayArray = array();
+
+ // query of all events
+ if (self::$pluginConfig['calendar_show_events']) {
+ $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('EVT'));
+ $queryParams = array_merge($catIdParams, array($dateMonthEnd, $dateMonthStart));
+
+ // check if special calendars should be shown
+ $allCategories = $gCurrentUser->getAllVisibleCategories('EVT');
+ $selectedCategories = self::$pluginConfig['calendar_show_categories'];
+
+ sort($allCategories);
+ sort($selectedCategories);
+
+ if ($allCategories == $selectedCategories) {
+ // show all calendars
+ $sqlSyntax = '';
+ } else {
+ // show only calendars of the parameter calendar_show_categories
+ $sqlSyntax = ' AND cat_name IN (' . Database::getQmForValues(self::$pluginConfig['calendar_show_categories']) . ')';
+ $queryParams = array_merge($queryParams, self::$pluginConfig['calendar_show_categories']);
+ }
+
+ $sql = 'SELECT DISTINCT dat_id, dat_cat_id, cat_name, dat_begin, dat_end, dat_all_day, dat_location, dat_headline
+ FROM ' . TBL_EVENTS . '
+ INNER JOIN ' . TBL_CATEGORIES . '
+ ON cat_id = dat_cat_id
+ WHERE cat_id IN (' . Database::getQmForValues($catIdParams) . ')
+ AND dat_begin <= ? -- $dateMonthEnd
+ AND dat_end >= ? -- $dateMonthStart
+ ' . $sqlSyntax . '
+ ORDER BY dat_begin ASC';
+ $datesStatement = $gDb->queryPrepared($sql, $queryParams);
+
+ while ($row = $datesStatement->fetch()) {
+ $startDate = new DateTime($row['dat_begin']);
+ $endDate = new DateTime($row['dat_end']);
+
+ // set custom name of plugin for calendar or use default Admidio name
+ if (self::$pluginConfig['calendar_show_categories_names']) {
+ if ($row['cat_name'][3] === '_') {
+ $calendarName = $gL10n->get($row['cat_name']);
+ } else {
+ $calendarName = $row['cat_name'];
+ }
+ $row['dat_headline'] = $calendarName . ': ' . $row['dat_headline'];
+ }
+
+ if ($startDate->format('Y-m-d') === $endDate->format('Y-m-d')) {
+ // event only within one day
+ $eventsMonthDayArray[$startDate->format('j')][] = array(
+ 'dat_id' => $row['dat_id'],
+ 'time' => $startDate->format($gSettingsManager->getString('system_time')),
+ 'all_day' => $row['dat_all_day'],
+ 'location' => $row['dat_location'],
+ 'headline' => $row['dat_headline'],
+ 'one_day' => true
+ );
+ } else {
+ // event within several days
+
+ if ($startDate->format('m') !== self::$currentMonth) {
+ $firstDay = 1;
+ } else {
+ $firstDay = $startDate->format('j');
+ }
+
+ if ($endDate->format('m') !== self::$currentMonth) {
+ $lastDay = self::$lastDayCurrentMonth;
+ } else {
+ $lastDay = $endDate->format('j');
+ }
+
+ // now add event to every relevant day of month
+ for ($i = $firstDay; $i <= $lastDay; ++$i) {
+ $eventsMonthDayArray[$i][] = array(
+ 'dat_id' => $row['dat_id'],
+ 'time' => $startDate->format($gSettingsManager->getString('system_time')),
+ 'all_day' => $row['dat_all_day'],
+ 'location' => $row['dat_location'],
+ 'headline' => $row['dat_headline'],
+ 'one_day' => false
+ );
+ }
+ }
+ }
+ }
+
+ // query of all birthdays
+ if (self::$pluginConfig['calendar_show_birthdays']) {
+ if (DB_ENGINE === Database::PDO_ENGINE_PGSQL) {
+ $sqlYearOfBirthday = ' EXTRACT(YEAR FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
+ $sqlMonthOfBirthday = ' EXTRACT(MONTH FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
+ $sqlDayOfBirthday = ' EXTRACT(DAY FROM TO_TIMESTAMP(birthday.usd_value, \'YYYY-MM-DD\')) ';
+ } else {
+ $sqlYearOfBirthday = ' YEAR(birthday.usd_value) ';
+ $sqlMonthOfBirthday = ' MONTH(birthday.usd_value) ';
+ $sqlDayOfBirthday = ' DayOfMonth(birthday.usd_value) ';
+ }
+
+ switch (self::$pluginConfig['calendar_show_birthday_names']) {
+ case 1:
+ $sqlOrderName = 'first_name';
+ break;
+ case 2:
+ $sqlOrderName = 'last_name';
+ break;
+ case 0: // fallthrough
+ default:
+ $sqlOrderName = 'last_name, first_name';
+ }
+
+ // database query for all birthdays of this month
+ $sql = 'SELECT DISTINCT
+ usr_id, last_name.usd_value AS last_name, first_name.usd_value AS first_name, birthday.usd_value AS birthday,
+ ' . $sqlYearOfBirthday . ' AS birthday_year, ' . $sqlMonthOfBirthday . ' AS birthday_month,
+ ' . $sqlDayOfBirthday . ' AS birthday_day
+ FROM ' . TBL_MEMBERS . '
+ INNER JOIN ' . TBL_ROLES . '
+ ON rol_id = mem_rol_id
+ INNER JOIN ' . TBL_CATEGORIES . '
+ ON cat_id = rol_cat_id
+ INNER JOIN ' . TBL_USERS . '
+ ON usr_id = mem_usr_id
+ INNER JOIN ' . TBL_USER_DATA . ' AS birthday
+ ON birthday.usd_usr_id = usr_id
+ AND birthday.usd_usf_id = ? -- $gProfileFields->getProperty(\'BIRTHDAY\', \'usf_id\')
+ AND ' . $sqlMonthOfBirthday . ' = ? -- $currentMonth
+ LEFT JOIN ' . TBL_USER_DATA . ' AS last_name
+ ON last_name.usd_usr_id = usr_id
+ AND last_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'LAST_NAME\', \'usf_id\')
+ LEFT JOIN ' . TBL_USER_DATA . ' AS first_name
+ ON first_name.usd_usr_id = usr_id
+ AND first_name.usd_usf_id = ? -- $gProfileFields->getProperty(\'FIRST_NAME\', \'usf_id\')
+ WHERE usr_valid = true
+ AND cat_org_id = ? -- $gCurrentOrgId
+ AND rol_id ' . $sqlRoleIds . '
+ AND mem_begin <= ? -- DATE_NOW
+ AND mem_end > ? -- DATE_NOW
+ ORDER BY birthday_year DESC, birthday_month DESC, birthday_day DESC, ' . $sqlOrderName;
+
+ $queryParams = array(
+ $gProfileFields->getProperty('BIRTHDAY', 'usf_id'),
+ self::$currentMonth,
+ $gProfileFields->getProperty('LAST_NAME', 'usf_id'),
+ $gProfileFields->getProperty('FIRST_NAME', 'usf_id'),
+ $gCurrentOrgId,
+ DATE_NOW,
+ DATE_NOW
+ );
+ $birthdayStatement = $gDb->queryPrepared($sql, $queryParams);
+
+ while ($row = $birthdayStatement->fetch()) {
+ $birthdayDate = new DateTime($row['birthday']);
+
+ switch (self::$pluginConfig['calendar_show_birthday_names']) {
+ case 1:
+ $name = $row['first_name'];
+ break;
+ case 2:
+ $name = $row['last_name'];
+ break;
+ case 0: // fallthrough
+ default:
+ $name = $row['last_name'] . ($row['last_name'] ? ', ' : '') . $row['first_name'];
+ }
+
+ $birthdaysMonthDayArray[$birthdayDate->format('j')][] = array(
+ 'year' => $birthdayDate->format('Y'),
+ 'age' => self::$currentYear - $birthdayDate->format('Y'),
+ 'name' => $name
+ );
+ }
+ }
+
+ return self::createCalendar($eventsMonthDayArray, $birthdaysMonthDayArray);
+ }
+
+ public static function initParams(array $params = array()) : bool
+ {
+ // check if params is an array
+ if (!is_array($params))
+ {
+ throw new InvalidArgumentException('Config must be an "array".');
+ }
+
+ // reset get date id flag before init
+ self::$getDatId = false;
+
+ // init parameters
+ if (isset($params['date_id']) && $params['date_id'] !== '') {
+ self::$getDatId = true;
+ // Read Date ID or generate current month and year
+ self::$currentMonth = substr($params['date_id'], 0, 2);
+ self::$currentYear = substr($params['date_id'], 2, 4);
+ $_SESSION['plugin_calendar_last_month'] = self::$currentMonth . self::$currentYear;
+ } elseif (isset($_SESSION['plugin_calendar_last_month'])) {
+ // Show last selected month
+ self::$currentMonth = substr($_SESSION['plugin_calendar_last_month'], 0, 2);
+ self::$currentYear = substr($_SESSION['plugin_calendar_last_month'], 2, 4);
+ } else {
+ // show current month
+ self::$currentMonth = date('m');
+ self::$currentYear = date('Y');
+ }
+
+ if (self::$currentMonth === date('m') && self::$currentYear === date('Y')) {
+ self::$today = (int)date('d');
+ }
+
+ self::$lastDayCurrentMonth = (int)date('t', mktime(0, 0, 0, self::$currentMonth, 1, self::$currentYear));
+
+ return true;
+ }
+
+ /**
+ * @param PagePresenter $page
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @return bool
+ */
+ public static function doRender($page = null) : bool
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+
+ // show the announcement list
+ try {
+ $rootPath = dirname(__DIR__, 3);
+ $pluginFolder = basename(self::$pluginPath);
+
+ require_once($rootPath . '/system/common.php');
+
+ $calendarPlugin = new Overview($pluginFolder);
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ throw new InvalidArgumentException($gL10n->get('SYS_PLUGIN_NOT_INSTALLED'));
+ }
+ if ($gSettingsManager->getInt('events_module_enabled') > 0 && $gSettingsManager->getInt('announcements_module_enabled') > 0) {
+ if ($gSettingsManager->getInt('calendar_plugin_enabled') === 1 || ($gSettingsManager->getInt('calendar_plugin_enabled') === 2 && $gValidLogin)) {
+
+ $tableContent = self::getCalendarsData();
+
+ header('Content-Type: text/html; charset=utf-8');
+
+ $calendarPlugin->assignTemplateVariable('pluginFolder', $pluginFolder);
+ $calendarPlugin->assignTemplateVariable('monthYearHeadline', self::$months[(int) self::$currentMonth - 1] . ' ' . self::$currentYear);
+ $calendarPlugin->assignTemplateVariable('monthYear', self::$currentMonth . self::$currentYear);
+ $calendarPlugin->assignTemplateVariable('currentMonthYear', date('mY'));
+ $calendarPlugin->assignTemplateVariable('dateIdLastMonth', date('mY', mktime(0, 0, 0, self::$currentMonth - 1, 1, self::$currentYear)));
+ $calendarPlugin->assignTemplateVariable('dateIdNextMonth', date('mY', mktime(0, 0, 0, self::$currentMonth + 1, 1, self::$currentYear)));
+ $calendarPlugin->assignTemplateVariable('tableContent', $tableContent);
+ } else {
+ $calendarPlugin->assignTemplateVariable('message',$gL10n->get('PLG_BIRTHDAY_NO_ENTRIES_VISITORS'));
+ }
+ } else {
+ $calendarPlugin->assignTemplateVariable('message', $gL10n->get('SYS_MODULE_DISABLED'));
+ }
+
+ if (isset($page) || self::$getDatId) {
+ echo $calendarPlugin->html('plugin.calendar.tpl');
+ } else {
+ $calendarPlugin->showHtmlPage('plugin.calendar.tpl');
+ }
+ } catch (Throwable $e) {
+ echo $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/calendar/classes/Presenter/CalendarPreferencesPresenter.php b/plugins/calendar/classes/Presenter/CalendarPreferencesPresenter.php
new file mode 100644
index 0000000000..40535574ca
--- /dev/null
+++ b/plugins/calendar/classes/Presenter/CalendarPreferencesPresenter.php
@@ -0,0 +1,141 @@
+ 'save', 'panel' => 'calendar')),
+ 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')
+ );
+ $formCalendar->addSelectBox(
+ 'calendar_plugin_enabled',
+ Language::translateIfTranslationStrId($formValues['calendar_plugin_enabled']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['calendar_plugin_enabled']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['calendar_plugin_enabled']['description'])
+ );
+ $formCalendar->addCheckbox(
+ 'calendar_show_events',
+ Language::translateIfTranslationStrId($formValues['calendar_show_events']['name']),
+ $formValues['calendar_show_events']['value'],
+ array('helpTextId' => $formValues['calendar_show_events']['description'])
+ );
+ $formCalendar->addCheckbox(
+ 'calendar_show_birthdays',
+ Language::translateIfTranslationStrId($formValues['calendar_show_birthdays']['name']),
+ $formValues['calendar_show_birthdays']['value'],
+ array('helpTextId' => $formValues['calendar_show_birthdays']['description'])
+ );
+ $formCalendar->addCheckbox(
+ 'calendar_show_birthdays_to_guests',
+ Language::translateIfTranslationStrId($formValues['calendar_show_birthdays_to_guests']['name']),
+ $formValues['calendar_show_birthdays_to_guests']['value'],
+ array('helpTextId' => $formValues['calendar_show_birthdays_to_guests']['description'])
+ );
+ $formCalendar->addCheckbox(
+ 'calendar_show_birthday_icon',
+ Language::translateIfTranslationStrId($formValues['calendar_show_birthday_icon']['name']),
+ $formValues['calendar_show_birthday_icon']['value'],
+ array('helpTextId' => $formValues['calendar_show_birthday_icon']['description'])
+ );
+ $selectBoxEntries = array(
+ '0' => $gL10n->get('SYS_LASTNAME') . ', ' . $gL10n->get('SYS_FIRSTNAME'),
+ '1' => $gL10n->get('SYS_FIRSTNAME'),
+ '2' => $gL10n->get('SYS_LASTNAME')
+ );
+ $formCalendar->addSelectBox(
+ 'calendar_show_birthday_names',
+ Language::translateIfTranslationStrId($formValues['calendar_show_birthday_names']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['calendar_show_birthday_names']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['calendar_show_birthday_names']['description'])
+ );
+
+ $catIdParams = array_merge(array(0), $gCurrentUser->getAllVisibleCategories('EVT'));
+ $sql = 'SELECT cat.cat_id, cat.cat_name
+ FROM ' . TBL_EVENTS . ' AS evt
+ INNER JOIN ' . TBL_CATEGORIES . ' AS cat
+ WHERE cat_id IN (' . $gDb->getQmForValues($catIdParams) . ')
+ ORDER BY evt.dat_timestamp_create DESC';
+ $sqlData = array(
+ 'query' => $sql,
+ 'params' => $catIdParams
+ );
+
+ $formCalendar->addSelectBoxFromSql(
+ 'calendar_show_categories',
+ Language::translateIfTranslationStrId($formValues['calendar_show_categories']['name']),
+ $gDb,
+ $sqlData,
+ array('defaultValue' => $formValues['calendar_show_categories']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['calendar_show_categories']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($gCurrentUser->getAllVisibleCategories('EVT')))
+ );
+
+ $formCalendar->addCheckbox(
+ 'calendar_show_categories_names',
+ Language::translateIfTranslationStrId($formValues['calendar_show_categories_names']['name']),
+ $formValues['calendar_show_categories_names']['value'],
+ array('helpTextId' => $formValues['calendar_show_categories_names']['description'])
+ );
+
+ $selectBoxEntries = $pluginCalendar instanceof Calendar ? $pluginCalendar::getAvailableRoles() : array();
+
+ $formCalendar->addSelectBox(
+ 'calendar_roles_view_plugin',
+ Language::translateIfTranslationStrId($formValues['calendar_roles_view_plugin']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['calendar_roles_view_plugin']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['calendar_roles_view_plugin']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($selectBoxEntries))
+ );
+ $formCalendar->addSelectBox(
+ 'calendar_roles_sql',
+ Language::translateIfTranslationStrId($formValues['calendar_roles_sql']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['calendar_roles_sql']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['calendar_roles_sql']['description'], 'multiselect' => true, 'maximumSelectionNumber' => count($selectBoxEntries))
+ );
+
+ $formCalendar->addSubmitButton(
+ 'adm_button_save_calendar',
+ $gL10n->get('SYS_SAVE'),
+ array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
+ );
+
+ $formCalendar->addToSmarty($smarty);
+ $gCurrentSession->addFormObject($formCalendar);
+ return $smarty->fetch($pluginCalendar::getPluginPath() . '/templates/preferences.plugin.calendar.tpl');
+ }
+}
\ No newline at end of file
diff --git a/plugins/calendar/classes/Service/UpdateStepsCode.php b/plugins/calendar/classes/Service/UpdateStepsCode.php
new file mode 100644
index 0000000000..ff74d8196b
--- /dev/null
+++ b/plugins/calendar/classes/Service/UpdateStepsCode.php
@@ -0,0 +1,120 @@
+ $varValue) {
+ if ($varName === 'plg_ter_aktiv') {
+ if ($gSettingsManager->getBool('calendar_show_events') !== (bool)$varValue) {
+ $gSettingsManager->set('calendar_show_events', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_geb_aktiv') {
+ if ($gSettingsManager->getBool('calendar_show_birthdays') !== (bool)$varValue) {
+ $gSettingsManager->set('calendar_show_birthdays', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_geb_login') {
+ if ($gSettingsManager->getBool('calendar_show_birthdays_to_guests') !== (bool)$varValue) {
+ $gSettingsManager->set('calendar_show_birthdays_to_guests', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_geb_icon') {
+ if ($gSettingsManager->getBool('calendar_show_birthday_icon') !== (bool)$varValue) {
+ $gSettingsManager->set('calendar_show_birthday_icon', (bool)$varValue);
+ }
+ }elseif ($varName === 'plg_geb_displayNames') {
+ if ($gSettingsManager->getInt('calendar_show_birthday_names') !== (int)$varValue) {
+ $gSettingsManager->set('calendar_show_birthday_names', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_kal_cat_show') {
+ if ($gSettingsManager->getBool('calendar_show_categories_names') !== (bool)$varValue) {
+ $gSettingsManager->set('calendar_show_categories_names', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_kal_cat') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ if ($categories !== array('All')) {
+ $categoryIds = array();
+ foreach ($categories as $category) {
+ // check if the category name exists and get the category id
+ $sql = 'SELECT cat_id
+ FROM ' . TBL_CATEGORIES . '
+ WHERE cat_name = ?
+ AND cat_type = \'EVT\'';
+ $pdoStatement = self::$db->queryPrepared($sql, array($category));
+ $row = $pdoStatement->fetch();
+ if ($row !== false) {
+ $categoryIds[] = (int)$row['cat_id'];
+ }
+ }
+ // if there are no valid category ids found, get all visible categories for the current user
+ if (empty($categoryIds)) {
+ $categoryIds = array('All');
+ }
+ } else {
+ $categoryIds = array('All');
+ }
+
+ $categoriesString = implode(',', $categoryIds);
+ // make sure to use 'All' instead of 'all' for consistency
+ $categoriesString = str_ireplace('all', 'All', $categoriesString);
+
+ if ($gSettingsManager->get('calendar_show_categories') !== $categoriesString) {
+ $gSettingsManager->set('calendar_show_categories', $categoriesString);
+ }
+ } elseif ($varName === 'plg_calendar_roles_view_plugin') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ $categoriesString = implode(',', $categories);
+ if ($gSettingsManager->get('calendar_roles_view_plugin') !== $categoriesString) {
+ $gSettingsManager->set('calendar_roles_view_plugin', $categoriesString);
+ }
+ } elseif ($varName === 'plg_rolle_sql') {
+ $categories = (is_array($varValue) && !empty($varValue)) ? $varValue : array('All');
+ $categoriesString = implode(',', $categories);
+ if ($gSettingsManager->get('calendar_roles_sql') !== $categoriesString) {
+ $gSettingsManager->set('calendar_roles_sql', $categoriesString);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/calendar/db_scripts/update_1_0.xml b/plugins/calendar/db_scripts/update_1_0.xml
new file mode 100644
index 0000000000..c0cffa9222
--- /dev/null
+++ b/plugins/calendar/db_scripts/update_1_0.xml
@@ -0,0 +1,5 @@
+
+
+ UpdateStepsCode::updateStep10RetrievePreviousSettings
+ stop
+
diff --git a/plugins/calendar/index.php b/plugins/calendar/index.php
new file mode 100644
index 0000000000..ed8acf0702
--- /dev/null
+++ b/plugins/calendar/index.php
@@ -0,0 +1,27 @@
+initParams(array('date_id' => $getDateId));
+ $pluginCalendar->doRender(isset($page) ? $page : null);
+
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
diff --git a/adm_plugins/calendar/languages/bg.xml b/plugins/calendar/languages/bg.xml
similarity index 100%
rename from adm_plugins/calendar/languages/bg.xml
rename to plugins/calendar/languages/bg.xml
diff --git a/adm_plugins/calendar/languages/da.xml b/plugins/calendar/languages/da.xml
similarity index 100%
rename from adm_plugins/calendar/languages/da.xml
rename to plugins/calendar/languages/da.xml
diff --git a/adm_plugins/calendar/languages/de-DE.xml b/plugins/calendar/languages/de-DE.xml
similarity index 100%
rename from adm_plugins/calendar/languages/de-DE.xml
rename to plugins/calendar/languages/de-DE.xml
diff --git a/adm_plugins/calendar/languages/de.xml b/plugins/calendar/languages/de.xml
similarity index 100%
rename from adm_plugins/calendar/languages/de.xml
rename to plugins/calendar/languages/de.xml
diff --git a/adm_plugins/calendar/languages/el.xml b/plugins/calendar/languages/el.xml
similarity index 100%
rename from adm_plugins/calendar/languages/el.xml
rename to plugins/calendar/languages/el.xml
diff --git a/plugins/calendar/languages/en.xml b/plugins/calendar/languages/en.xml
new file mode 100644
index 0000000000..1bd3671e4b
--- /dev/null
+++ b/plugins/calendar/languages/en.xml
@@ -0,0 +1,34 @@
+
+
+ Current month
+ January,February,March,April,May,June,July,August,September,October,November,December
+ Mo
+ Tu
+ We
+ Th
+ Fr
+ Sa
+ Su
+ Several days
+ This plugin displays a calendar with all events of the current month on the Overview page.
+ Calendar
+
+ Show events
+ If this setting is enabled, events will be displayed in the calendar. (default: true)
+ Show birthdays
+ If this setting is enabled, the birthdays of members will be displayed in the calendar. (default: true)
+ Show birthdays for visitors
+ If this setting is enabled, the birthdays of members will be displayed for visitors. (default: false)
+ Show birthday icon
+ If this setting is enabled, a birthday icon will be displayed in the calendar. (default: true)
+ Display style for birthday person name
+ This setting allows you to choose how the names of birthday persons are displayed. (default: First name)
+ Considered categories
+ This setting allows you to choose which categories should be considered for events in the calendar. (default: all categories)
+ Display calendar names
+ If this setting is enabled, the names of the calendars will be displayed for each event. (default: false)
+ Allowed roles for content
+ Here you can select the roles that are allowed to see the content of this plugin. If a user is not part of any selected role only the number of events and birthdays are shown. (default: all roles)
+ Allowed roles for birhdays
+ Here you can select the roles which users must have whose birthdays should be shown. (default: all roles)
+
diff --git a/adm_plugins/calendar/languages/es.xml b/plugins/calendar/languages/es.xml
similarity index 100%
rename from adm_plugins/calendar/languages/es.xml
rename to plugins/calendar/languages/es.xml
diff --git a/adm_plugins/calendar/languages/et.xml b/plugins/calendar/languages/et.xml
similarity index 100%
rename from adm_plugins/calendar/languages/et.xml
rename to plugins/calendar/languages/et.xml
diff --git a/adm_plugins/calendar/languages/fi.xml b/plugins/calendar/languages/fi.xml
old mode 100755
new mode 100644
similarity index 100%
rename from adm_plugins/calendar/languages/fi.xml
rename to plugins/calendar/languages/fi.xml
diff --git a/adm_plugins/calendar/languages/fr.xml b/plugins/calendar/languages/fr.xml
similarity index 100%
rename from adm_plugins/calendar/languages/fr.xml
rename to plugins/calendar/languages/fr.xml
diff --git a/adm_plugins/calendar/languages/hu.xml b/plugins/calendar/languages/hu.xml
similarity index 100%
rename from adm_plugins/calendar/languages/hu.xml
rename to plugins/calendar/languages/hu.xml
diff --git a/adm_plugins/calendar/languages/nb.xml b/plugins/calendar/languages/nb.xml
similarity index 100%
rename from adm_plugins/calendar/languages/nb.xml
rename to plugins/calendar/languages/nb.xml
diff --git a/adm_plugins/calendar/languages/nl.xml b/plugins/calendar/languages/nl.xml
similarity index 100%
rename from adm_plugins/calendar/languages/nl.xml
rename to plugins/calendar/languages/nl.xml
diff --git a/adm_plugins/calendar/languages/pl.xml b/plugins/calendar/languages/pl.xml
similarity index 100%
rename from adm_plugins/calendar/languages/pl.xml
rename to plugins/calendar/languages/pl.xml
diff --git a/adm_plugins/calendar/languages/pt-BR.xml b/plugins/calendar/languages/pt-BR.xml
similarity index 100%
rename from adm_plugins/calendar/languages/pt-BR.xml
rename to plugins/calendar/languages/pt-BR.xml
diff --git a/adm_plugins/calendar/languages/pt.xml b/plugins/calendar/languages/pt.xml
similarity index 100%
rename from adm_plugins/calendar/languages/pt.xml
rename to plugins/calendar/languages/pt.xml
diff --git a/adm_plugins/calendar/languages/ru.xml b/plugins/calendar/languages/ru.xml
similarity index 100%
rename from adm_plugins/calendar/languages/ru.xml
rename to plugins/calendar/languages/ru.xml
diff --git a/adm_plugins/calendar/languages/sv.xml b/plugins/calendar/languages/sv.xml
similarity index 100%
rename from adm_plugins/calendar/languages/sv.xml
rename to plugins/calendar/languages/sv.xml
diff --git a/adm_plugins/calendar/languages/uk.xml b/plugins/calendar/languages/uk.xml
similarity index 100%
rename from adm_plugins/calendar/languages/uk.xml
rename to plugins/calendar/languages/uk.xml
diff --git a/adm_plugins/calendar/languages/zh.xml b/plugins/calendar/languages/zh.xml
similarity index 100%
rename from adm_plugins/calendar/languages/zh.xml
rename to plugins/calendar/languages/zh.xml
diff --git a/adm_plugins/calendar/templates/plugin.calendar.tpl b/plugins/calendar/templates/plugin.calendar.tpl
similarity index 92%
rename from adm_plugins/calendar/templates/plugin.calendar.tpl
rename to plugins/calendar/templates/plugin.calendar.tpl
index 45178d0020..52cefa739b 100644
--- a/adm_plugins/calendar/templates/plugin.calendar.tpl
+++ b/plugins/calendar/templates/plugin.calendar.tpl
@@ -32,7 +32,7 @@
+
+{$javascript}
\ No newline at end of file
diff --git a/plugins/random-photo/classes/Presenter/RandomPhotoPreferencesPresenter.php b/plugins/random-photo/classes/Presenter/RandomPhotoPreferencesPresenter.php
new file mode 100644
index 0000000000..62409d22c1
--- /dev/null
+++ b/plugins/random-photo/classes/Presenter/RandomPhotoPreferencesPresenter.php
@@ -0,0 +1,100 @@
+ 'save', 'panel' => 'random_photo')),
+ 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')
+ );
+ $formRandomPhoto->addSelectBox(
+ 'random_photo_plugin_enabled',
+ Language::translateIfTranslationStrId($formValues['random_photo_plugin_enabled']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['random_photo_plugin_enabled']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['random_photo_plugin_enabled']['description'])
+ );
+ $formRandomPhoto->addInput(
+ 'random_photo_max_char_per_word',
+ Language::translateIfTranslationStrId($formValues['random_photo_max_char_per_word']['name']),
+ $formValues['random_photo_max_char_per_word']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['random_photo_max_char_per_word']['description'])
+ );
+ $formRandomPhoto->addInput(
+ 'random_photo_max_width',
+ Language::translateIfTranslationStrId($formValues['random_photo_max_width']['name']),
+ $formValues['random_photo_max_width']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['random_photo_max_width']['description'])
+ );
+ $formRandomPhoto->addInput(
+ 'random_photo_max_height',
+ Language::translateIfTranslationStrId($formValues['random_photo_max_height']['name']),
+ $formValues['random_photo_max_height']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['random_photo_max_height']['description'])
+ );
+ $formRandomPhoto->addInput(
+ 'random_photo_albums',
+ Language::translateIfTranslationStrId($formValues['random_photo_albums']['name']),
+ $formValues['random_photo_albums']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['random_photo_albums']['description'])
+ );
+ $formRandomPhoto->addInput(
+ 'random_photo_album_photo_number',
+ Language::translateIfTranslationStrId($formValues['random_photo_album_photo_number']['name']),
+ $formValues['random_photo_album_photo_number']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['random_photo_album_photo_number']['description'])
+ );
+ $formRandomPhoto->addCheckbox(
+ 'random_photo_show_album_link',
+ Language::translateIfTranslationStrId($formValues['random_photo_show_album_link']['name']),
+ $formValues['random_photo_show_album_link']['value'],
+ array('helpTextId' => $formValues['random_photo_show_album_link']['description'])
+ );
+ $formRandomPhoto->addSubmitButton(
+ 'adm_button_save_random_photo',
+ $gL10n->get('SYS_SAVE'),
+ array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
+ );
+
+ $formRandomPhoto->addToSmarty($smarty);
+ $gCurrentSession->addFormObject($formRandomPhoto);
+ return $smarty->fetch($pluginRandomPhoto::getPluginPath() . '/templates/preferences.plugin.random-photo.tpl');
+ }
+}
\ No newline at end of file
diff --git a/plugins/random-photo/classes/RandomPhoto.php b/plugins/random-photo/classes/RandomPhoto.php
new file mode 100644
index 0000000000..e6856a568f
--- /dev/null
+++ b/plugins/random-photo/classes/RandomPhoto.php
@@ -0,0 +1,150 @@
+ 0
+ ORDER BY pho_begin DESC';
+
+ // optional set a limit which albums should be scanned
+ if (self::$pluginConfig['random_photo_albums'] > 0) {
+ $sql .= ' LIMIT ' . self::$pluginConfig['random_photo_albums'];
+ }
+
+ $albumStatement = $gDb->queryPrepared($sql, array($gCurrentOrgId));
+ $albumList = $albumStatement->fetchAll();
+
+ $i = 0;
+ $photoNr = 0;
+ $photoServerPath = '';
+ $linkText = '';
+ $album = new Album($gDb);
+
+ // loop, if an image is not found directly, but limit to 20 passes
+ while (!is_file($photoServerPath) && $i < 20 && $albumStatement->rowCount() > 0) {
+ $album->setArray($albumList[mt_rand(0, $albumStatement->rowCount() - 1)]);
+
+ // optionally select an image randomly
+ if (self::$pluginConfig['random_photo_album_photo_number'] === 0) {
+ $photoNr = mt_rand(1, (int)$album->getValue('pho_quantity'));
+ } else {
+ $photoNr = self::$pluginConfig['random_photo_album_photo_number'];
+ }
+
+ // Compose image path
+ $photoServerPath = ADMIDIO_PATH . FOLDER_DATA . '/photos/' . $album->getValue('pho_begin', 'Y-m-d') . '_' . (int)$album->getValue('pho_id') . '/' . $photoNr . '.jpg';
+ ++$i;
+ }
+
+ if (self::$pluginConfig['random_photo_show_album_link'] && self::$pluginConfig['random_photo_max_char_per_word'] > 0) {
+ // Wrap link text if necessary
+ $words = explode(' ', $album->getValue('pho_name'));
+
+ foreach ($words as $word) {
+ if (strlen($word) > self::$pluginConfig['random_photo_max_char_per_word']) {
+ $linkText .= substr($word, 0, self::$pluginConfig['random_photo_max_char_per_word']) . '-
' .
+ substr($word, self::$pluginConfig['random_photo_max_char_per_word']) . ' ';
+ } else {
+ $linkText .= $word . ' ';
+ }
+ }
+ } else {
+ $linkText = $album->getValue('pho_name');
+ }
+ $photoData['photoNr'] = $photoNr;
+ $photoData['uuid'] = $album->getValue('pho_uuid');
+ $photoData['linkText'] = $linkText;
+ return $photoData;
+ }
+
+ /**
+ * @param PagePresenter $page
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @return bool
+ */
+ public static function doRender($page = null) : bool
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+
+ // show random photo
+ try {
+ $rootPath = dirname(__DIR__, 3);
+ $pluginFolder = basename(self::$pluginPath);
+
+ require_once($rootPath . '/system/common.php');
+
+ $randomPhotoPlugin = new Overview($pluginFolder);
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ throw new InvalidArgumentException($gL10n->get('SYS_PLUGIN_NOT_INSTALLED'));
+ }
+
+ if ($gSettingsManager->getInt('photo_module_enabled') > 0) {
+ if (($gSettingsManager->getInt('photo_module_enabled') === 1 || ($gSettingsManager->getInt('photo_module_enabled') === 2 && $gValidLogin)) &&
+ ($gSettingsManager->getInt('random_photo_plugin_enabled') === 1 || ($gSettingsManager->getInt('random_photo_plugin_enabled') === 2 && $gValidLogin))) {
+ $photoData = self::getPhotoData();
+ $randomPhotoPlugin->assignTemplateVariable('photoUUID', $photoData['uuid']);
+ $randomPhotoPlugin->assignTemplateVariable('photoNr', $photoData['photoNr']);
+ $randomPhotoPlugin->assignTemplateVariable('photoTitle', $photoData['linkText']);
+ $randomPhotoPlugin->assignTemplateVariable('photoMaxWidth', self::$pluginConfig['random_photo_max_width']);
+ $randomPhotoPlugin->assignTemplateVariable('photoMaxHeight', self::$pluginConfig['random_photo_max_height']);
+ $randomPhotoPlugin->assignTemplateVariable('photoShowLink', self::$pluginConfig['random_photo_show_album_link']);
+ } else {
+ $randomPhotoPlugin->assignTemplateVariable('message',$gL10n->get('PLG_RANDOM_PHOTO_NO_ENTRIES_VISITORS'));
+ }
+ } else {
+ $randomPhotoPlugin->assignTemplateVariable('message', $gL10n->get('SYS_MODULE_DISABLED'));
+ }
+
+ if (isset($page)) {
+ echo $randomPhotoPlugin->html('plugin.random-photo.tpl');
+ } else {
+ $randomPhotoPlugin->showHtmlPage('plugin.random-photo.tpl');
+ }
+ } catch (Throwable $e) {
+ echo $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/random-photo/classes/Service/UpdateStepsCode.php b/plugins/random-photo/classes/Service/UpdateStepsCode.php
new file mode 100644
index 0000000000..08f1b899a8
--- /dev/null
+++ b/plugins/random-photo/classes/Service/UpdateStepsCode.php
@@ -0,0 +1,78 @@
+ $varValue) {
+ if ($varName === 'plg_max_char_per_word') {
+ if ($gSettingsManager->getInt('random_photo_max_char_per_word') !== (int)$varValue) {
+ $gSettingsManager->set('random_photo_max_char_per_word', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_photos_max_width') {
+ if ($gSettingsManager->getInt('random_photo_max_width') !== (int)$varValue) {
+ $gSettingsManager->set('random_photo_max_width', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_photos_max_height') {
+ if ($gSettingsManager->getInt('random_photo_max_height') !== (int)$varValue) {
+ $gSettingsManager->set('random_photo_max_height', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_photos_albums') {
+ if ($gSettingsManager->getInt('random_photo_albums') !== (int)$varValue) {
+ $gSettingsManager->set('random_photo_albums', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_photos_picnr') {
+ if ($gSettingsManager->getInt('random_photo_album_photo_number') !== (int)$varValue) {
+ $gSettingsManager->set('random_photo_album_photo_number', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_photos_show_link') {
+ if ($gSettingsManager->getBool('random_photo_show_album_link') !== (bool)$varValue) {
+ $gSettingsManager->set('random_photo_show_album_link', (bool)$varValue);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/random-photo/db_scripts/update_1_0.xml b/plugins/random-photo/db_scripts/update_1_0.xml
new file mode 100644
index 0000000000..c0cffa9222
--- /dev/null
+++ b/plugins/random-photo/db_scripts/update_1_0.xml
@@ -0,0 +1,5 @@
+
+
+ UpdateStepsCode::updateStep10RetrievePreviousSettings
+ stop
+
diff --git a/plugins/random-photo/index.php b/plugins/random-photo/index.php
new file mode 100644
index 0000000000..d5a1300c53
--- /dev/null
+++ b/plugins/random-photo/index.php
@@ -0,0 +1,24 @@
+doRender(isset($page) ? $page : null);
+
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
diff --git a/adm_plugins/random_photo/languages/bg.xml b/plugins/random-photo/languages/bg.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/bg.xml
rename to plugins/random-photo/languages/bg.xml
diff --git a/adm_plugins/random_photo/languages/da.xml b/plugins/random-photo/languages/da.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/da.xml
rename to plugins/random-photo/languages/da.xml
diff --git a/adm_plugins/random_photo/languages/de-DE.xml b/plugins/random-photo/languages/de-DE.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/de-DE.xml
rename to plugins/random-photo/languages/de-DE.xml
diff --git a/adm_plugins/random_photo/languages/de.xml b/plugins/random-photo/languages/de.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/de.xml
rename to plugins/random-photo/languages/de.xml
diff --git a/adm_plugins/random_photo/languages/el.xml b/plugins/random-photo/languages/el.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/el.xml
rename to plugins/random-photo/languages/el.xml
diff --git a/plugins/random-photo/languages/en.xml b/plugins/random-photo/languages/en.xml
new file mode 100644
index 0000000000..2de8c45de2
--- /dev/null
+++ b/plugins/random-photo/languages/en.xml
@@ -0,0 +1,19 @@
+
+
+ There are no photos available for visitors. Log in to see the photos.
+ This plugin displays a randomly selected photo from the photo module.
+ Random Photo
+
+ Number of characters per word
+ Maximum number of characters in a word before a line break should be performed. If it's set to 0 then no line break will be performed. (default: 0)
+ Maximum photo width
+ Maximum width of the photo in pixels. If it's set to 0 then the image will be dynamically scaled by the html container in which this plugin is placed. (default: 0)
+ Maximum photo height
+ Maximum height of the photo in pixels. If it's set to 0 then the image will be dynamically scaled by the html container in which this plugin is placed. (default: 0)
+ Considered albums
+ Number of albums the photo may come from. Counts from the most recent album in descending order. If it's set to 0 then all albums will be considered. (default: 0)
+ Photo number
+ Number of the photo from the album to be displayed. If it's set to 0 then a random photo from the album will be displayed, if it's set to a specific number then that photo will be displayed. (default: 0)
+ Show album link
+ If this setting is enabled a link to the album with the album name below the photo will be displayed. (default: true)
+
diff --git a/adm_plugins/random_photo/languages/et.xml b/plugins/random-photo/languages/et.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/et.xml
rename to plugins/random-photo/languages/et.xml
diff --git a/adm_plugins/random_photo/languages/fi.xml b/plugins/random-photo/languages/fi.xml
old mode 100755
new mode 100644
similarity index 100%
rename from adm_plugins/random_photo/languages/fi.xml
rename to plugins/random-photo/languages/fi.xml
diff --git a/adm_plugins/random_photo/languages/fr.xml b/plugins/random-photo/languages/fr.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/fr.xml
rename to plugins/random-photo/languages/fr.xml
diff --git a/adm_plugins/random_photo/languages/hu.xml b/plugins/random-photo/languages/hu.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/hu.xml
rename to plugins/random-photo/languages/hu.xml
diff --git a/adm_plugins/random_photo/languages/nb.xml b/plugins/random-photo/languages/nb.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/nb.xml
rename to plugins/random-photo/languages/nb.xml
diff --git a/adm_plugins/random_photo/languages/nl.xml b/plugins/random-photo/languages/nl.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/nl.xml
rename to plugins/random-photo/languages/nl.xml
diff --git a/adm_plugins/random_photo/languages/pl.xml b/plugins/random-photo/languages/pl.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/pl.xml
rename to plugins/random-photo/languages/pl.xml
diff --git a/adm_plugins/random_photo/languages/pt.xml b/plugins/random-photo/languages/pt.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/pt.xml
rename to plugins/random-photo/languages/pt.xml
diff --git a/adm_plugins/random_photo/languages/ru.xml b/plugins/random-photo/languages/ru.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/ru.xml
rename to plugins/random-photo/languages/ru.xml
diff --git a/adm_plugins/random_photo/languages/sv.xml b/plugins/random-photo/languages/sv.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/sv.xml
rename to plugins/random-photo/languages/sv.xml
diff --git a/adm_plugins/random_photo/languages/uk.xml b/plugins/random-photo/languages/uk.xml
similarity index 100%
rename from adm_plugins/random_photo/languages/uk.xml
rename to plugins/random-photo/languages/uk.xml
diff --git a/plugins/random-photo/random-photo.json b/plugins/random-photo/random-photo.json
new file mode 100644
index 0000000000..a77cffa1a6
--- /dev/null
+++ b/plugins/random-photo/random-photo.json
@@ -0,0 +1,68 @@
+{
+ "name": "PLG_RANDOM_PHOTO_PLUGIN_NAME",
+ "description": "PLG_RANDOM_PHOTO_PLUGIN_DESCRIPTION",
+ "version": "1.0.0",
+ "author": "Admidio Team",
+ "url": "https://www.admidio.org",
+ "icon": "bi-image-fill",
+ "mainFile": "index.php",
+ "overviewPlugin": true,
+ "autoload": {
+ "psr-4": {
+ "RandomPhoto\\classes\\": "classes/"
+ }
+ },
+ "dependencies": [
+ "Admidio\\Infrastructure\\Plugins\\Overview",
+ "Admidio\\Infrastructure\\Plugins\\PluginAbstract",
+ "Admidio\\Photos\\Entity\\Album"
+ ],
+ "defaultConfig": {
+ "random_photo_plugin_enabled": {
+ "name": "ORG_ACCESS_TO_PLUGIN",
+ "description": "ORG_ACCESS_TO_PLUGIN_DESC",
+ "type": "integer",
+ "value" : 1
+ },
+ "random_photo_overview_sequence": {
+ "type": "integer",
+ "value" : 4
+ },
+ "random_photo_max_char_per_word": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_CHAR_PER_WORD",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_CHAR_PER_WORD_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "random_photo_max_width": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_WIDTH",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_WIDTH_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "random_photo_max_height": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_HEIGHT",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_MAX_HEIGHT_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "random_photo_albums": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_ALBUMS",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_ALBUMS_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "random_photo_album_photo_number": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_ALBUM_PHOTO_NUMBER",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_ALBUM_PHOTO_NUMBER_DESC",
+ "type": "integer",
+ "value" : 0
+ },
+ "random_photo_show_album_link": {
+ "name": "PLG_RANDOM_PHOTO_PREFERENCES_SHOW_ALBUM_LINK",
+ "description": "PLG_RANDOM_PHOTO_PREFERENCES_SHOW_ALBUM_LINK_DESC",
+ "type": "boolean",
+ "value" : true
+ }
+ }
+}
\ No newline at end of file
diff --git a/adm_plugins/random_photo/templates/plugin.random-photo.tpl b/plugins/random-photo/templates/plugin.random-photo.tpl
similarity index 100%
rename from adm_plugins/random_photo/templates/plugin.random-photo.tpl
rename to plugins/random-photo/templates/plugin.random-photo.tpl
diff --git a/plugins/random-photo/templates/preferences.plugin.random-photo.tpl b/plugins/random-photo/templates/preferences.plugin.random-photo.tpl
new file mode 100644
index 0000000000..b2ef185107
--- /dev/null
+++ b/plugins/random-photo/templates/preferences.plugin.random-photo.tpl
@@ -0,0 +1,17 @@
+
+{$javascript}
\ No newline at end of file
diff --git a/plugins/who-is-online/classes/Presenter/WhoIsOnlinePreferencesPresenter.php b/plugins/who-is-online/classes/Presenter/WhoIsOnlinePreferencesPresenter.php
new file mode 100644
index 0000000000..be962de30f
--- /dev/null
+++ b/plugins/who-is-online/classes/Presenter/WhoIsOnlinePreferencesPresenter.php
@@ -0,0 +1,99 @@
+ 'save', 'panel' => 'who_is_online')),
+ 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')
+ );
+ $formWhoIsOnline->addSelectBox(
+ 'who_is_online_plugin_enabled',
+ Language::translateIfTranslationStrId($formValues['who_is_online_plugin_enabled']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['who_is_online_plugin_enabled']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['who_is_online_plugin_enabled']['description'])
+ );
+ $formWhoIsOnline->addInput(
+ 'who_is_online_time_still_active',
+ Language::translateIfTranslationStrId($formValues['who_is_online_time_still_active']['name']),
+ $formValues['who_is_online_time_still_active']['value'],
+ array('type' => 'number', 'minNumber' => 0, 'step' => 1, 'helpTextId' => $formValues['who_is_online_time_still_active']['description'])
+ );
+ $formWhoIsOnline->addCheckbox(
+ 'who_is_online_show_visitors',
+ Language::translateIfTranslationStrId($formValues['who_is_online_show_visitors']['name']),
+ $formValues['who_is_online_show_visitors']['value'],
+ array('helpTextId' => $formValues['who_is_online_show_visitors']['description'])
+ );
+ $selectBoxEntries = array(
+ '0' => $gL10n->get('PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_MEMBERS_TO_VISITORS_SELECTION_1'),
+ '1' => $gL10n->get('PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_MEMBERS_TO_VISITORS_SELECTION_2'),
+ '2' => $gL10n->get('PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_MEMBERS_TO_VISITORS_SELECTION_3')
+ );
+ $formWhoIsOnline->addSelectBox(
+ 'who_is_online_show_members_to_visitors',
+ Language::translateIfTranslationStrId($formValues['who_is_online_show_members_to_visitors']['name']),
+ $selectBoxEntries,
+ array('defaultValue' => $formValues['who_is_online_show_members_to_visitors']['value'], 'showContextDependentFirstEntry' => false, 'helpTextId' => $formValues['who_is_online_show_members_to_visitors']['description'])
+ );
+ $formWhoIsOnline->addCheckbox(
+ 'who_is_online_show_self',
+ Language::translateIfTranslationStrId($formValues['who_is_online_show_self']['name']),
+ $formValues['who_is_online_show_self']['value'],
+ array('helpTextId' => $formValues['who_is_online_show_self']['description'])
+ );
+ $formWhoIsOnline->addCheckbox(
+ 'who_is_online_show_users_side_by_side',
+ Language::translateIfTranslationStrId($formValues['who_is_online_show_users_side_by_side']['name']),
+ $formValues['who_is_online_show_users_side_by_side']['value'],
+ array('helpTextId' => $formValues['who_is_online_show_users_side_by_side']['description'])
+ );
+ $formWhoIsOnline->addSubmitButton(
+ 'adm_button_save_who_is_online',
+ $gL10n->get('SYS_SAVE'),
+ array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
+ );
+
+ $formWhoIsOnline->addToSmarty($smarty);
+ $gCurrentSession->addFormObject($formWhoIsOnline);
+ return $smarty->fetch($pluginWhoIsOnline::getPluginPath() . '/templates/preferences.plugin.who-is-online.tpl');
+ }
+}
\ No newline at end of file
diff --git a/plugins/who-is-online/classes/Service/UpdateStepsCode.php b/plugins/who-is-online/classes/Service/UpdateStepsCode.php
new file mode 100644
index 0000000000..6885a98456
--- /dev/null
+++ b/plugins/who-is-online/classes/Service/UpdateStepsCode.php
@@ -0,0 +1,74 @@
+ $varValue) {
+ if ($varName === 'plg_time_online') {
+ if ($gSettingsManager->getInt('who_is_online_time_still_active') !== (int)$varValue) {
+ $gSettingsManager->set('who_is_online_time_still_active', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_visitors') {
+ if ($gSettingsManager->getBool('who_is_online_show_visitors') !== (bool)$varValue) {
+ $gSettingsManager->set('who_is_online_show_visitors', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_show_members') {
+ if ($gSettingsManager->getInt('who_is_online_show_members_to_visitors') !== (int)$varValue) {
+ $gSettingsManager->set('who_is_online_show_members_to_visitors', (int)$varValue);
+ }
+ } elseif ($varName === 'plg_show_self') {
+ if ($gSettingsManager->getBool('who_is_online_show_self') !== (bool)$varValue) {
+ $gSettingsManager->set('who_is_online_show_self', (bool)$varValue);
+ }
+ } elseif ($varName === 'plg_show_users_side_by_side') {
+ if ($gSettingsManager->getBool('who_is_online_show_users_side_by_side') !== (bool)$varValue) {
+ $gSettingsManager->set('who_is_online_show_users_side_by_side', (bool)$varValue);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/who-is-online/classes/WhoIsOnline.php b/plugins/who-is-online/classes/WhoIsOnline.php
new file mode 100644
index 0000000000..dbaa2da26e
--- /dev/null
+++ b/plugins/who-is-online/classes/WhoIsOnline.php
@@ -0,0 +1,158 @@
+sub($minutesOffset)->format('Y-m-d H:i:s');
+
+ // Find user IDs of all sessions that are in the specified current and reference time
+ $sql = 'SELECT ses_usr_id, usr_uuid, usr_login_name
+ FROM ' . TBL_SESSIONS . '
+ LEFT JOIN ' . TBL_USERS . '
+ ON usr_id = ses_usr_id
+ WHERE ses_timestamp BETWEEN ? AND ? -- $refDate AND DATETIME_NOW
+ AND ses_org_id = ? -- $gCurrentOrgId';
+ $queryParams = array($refDate, DATETIME_NOW, $gCurrentOrgId);
+ if (!$config['who_is_online_show_visitors']) {
+ $sql .= '
+ AND ses_usr_id IS NOT NULL';
+ }
+ if (!$config['who_is_online_show_self'] && $gValidLogin) {
+ $sql .= '
+ AND ses_usr_id <> ? -- $gCurrentUserId';
+ $queryParams[] = $gCurrentUserId;
+ }
+ $sql .= '
+ ORDER BY ses_usr_id';
+ $onlineUsersStatement = $gDb->queryPrepared($sql, $queryParams);
+
+ if ($onlineUsersStatement->rowCount() > 0) {
+ $usrIdMerker = 0;
+ $countMembers = 0;
+ $countVisitors = 0;
+ $allVisibleOnlineUsers = array();
+ $textOnlineVisitors = '';
+
+ while ($row = $onlineUsersStatement->fetch()) {
+ if ($row['ses_usr_id'] > 0) {
+ if (((int)$row['ses_usr_id'] !== $usrIdMerker)
+ && ($config['who_is_online_show_members_to_visitors'] == 1 || $gValidLogin)) {
+ $allVisibleOnlineUsers[] = '' . $row['usr_login_name'] . '';
+ $usrIdMerker = (int)$row['ses_usr_id'];
+ }
+ ++$countMembers;
+ } else {
+ ++$countVisitors;
+ }
+ }
+
+ if (!$gValidLogin && $config['who_is_online_show_members_to_visitors'] == 2 && $countMembers > 0) {
+ if ($countMembers > 1) {
+ $allVisibleOnlineUsers[] = $gL10n->get('PLG_WHO_IS_ONLINE_VAR_NUM_MEMBERS', array($countMembers));
+ } else {
+ $allVisibleOnlineUsers[] = $gL10n->get('PLG_WHO_IS_ONLINE_VAR_NUM_MEMBER', array($countMembers));
+ }
+ }
+
+ if ($config['who_is_online_show_visitors'] && $countVisitors > 0) {
+ $allVisibleOnlineUsers[] = $gL10n->get('PLG_WHO_IS_ONLINE_VAR_NUM_VISITORS', array($countVisitors));
+ }
+
+ if ($config['who_is_online_show_users_side_by_side']) {
+ $textOnlineVisitors = implode(', ', $allVisibleOnlineUsers);
+ } else {
+ $textOnlineVisitors = '
' . implode('
', $allVisibleOnlineUsers);
+ }
+
+ if ($onlineUsersStatement->rowCount() === 1) {
+ $text = $gL10n->get('PLG_WHO_IS_ONLINE_VAR_ONLINE_IS', array($textOnlineVisitors));
+ } else {
+ $text = $gL10n->get('PLG_WHO_IS_ONLINE_VAR_ONLINE_ARE', array($textOnlineVisitors));
+ }
+ } else {
+ $text = $gL10n->get('PLG_WHO_IS_ONLINE_NO_VISITORS_ON_WEBSITE');
+ }
+
+ return $text;
+ }
+
+ /**
+ * @param PagePresenter $page
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @return bool
+ */
+ public static function doRender($page = null) : bool
+ {
+ global $gSettingsManager, $gL10n, $gValidLogin;
+
+ // show random photo
+ try {
+ $rootPath = dirname(__DIR__, 3);
+ $pluginFolder = basename(self::$pluginPath);
+
+ require_once($rootPath . '/system/common.php');
+
+ $whoIsOnlinePlugin = new Overview($pluginFolder);
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ throw new InvalidArgumentException($gL10n->get('SYS_PLUGIN_NOT_INSTALLED'));
+ }
+
+ if ($gSettingsManager->getInt('who_is_online_plugin_enabled') === 1 || ($gSettingsManager->getInt('who_is_online_plugin_enabled') === 2 && $gValidLogin)) {
+ $text = self::getWhoIsOnlineText();
+ $whoIsOnlinePlugin->assignTemplateVariable('message', $text);
+ } else {
+ $whoIsOnlinePlugin->assignTemplateVariable('message', $gL10n->get('PLG_WHO_IS_ONLINE_NO_DATA_VISITORS'));
+ }
+
+ if (isset($page)) {
+ echo $whoIsOnlinePlugin->html('plugin.who-is-online.tpl');
+ } else {
+ $whoIsOnlinePlugin->showHtmlPage('plugin.who-is-online.tpl');
+ }
+ } catch (Throwable $e) {
+ echo $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/who-is-online/db_scripts/update_1_0.xml b/plugins/who-is-online/db_scripts/update_1_0.xml
new file mode 100644
index 0000000000..c0cffa9222
--- /dev/null
+++ b/plugins/who-is-online/db_scripts/update_1_0.xml
@@ -0,0 +1,5 @@
+
+
+ UpdateStepsCode::updateStep10RetrievePreviousSettings
+ stop
+
diff --git a/plugins/who-is-online/index.php b/plugins/who-is-online/index.php
new file mode 100644
index 0000000000..a88df7fc27
--- /dev/null
+++ b/plugins/who-is-online/index.php
@@ -0,0 +1,23 @@
+doRender(isset($page) ? $page : null);
+
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
diff --git a/plugins/who-is-online/languages/bg.xml b/plugins/who-is-online/languages/bg.xml
new file mode 100644
index 0000000000..6a0225b431
--- /dev/null
+++ b/plugins/who-is-online/languages/bg.xml
@@ -0,0 +1,10 @@
+
+
+ Кой е онлайн?
+ Няма онлайн потребители..
+ #VAR1# посетители
+ #VAR1# члет
+ #VAR1# членове
+ Онлайн има #VAR1#
+ Онлайн е #VAR1#
+
diff --git a/plugins/who-is-online/languages/da.xml b/plugins/who-is-online/languages/da.xml
new file mode 100644
index 0000000000..c40b6654e7
--- /dev/null
+++ b/plugins/who-is-online/languages/da.xml
@@ -0,0 +1,10 @@
+
+
+ On-line
+ Lige nu er der ikke nogen besøgende på hjemmesiden
+ #VAR1# Gæster
+ #VAR1# Medlem
+ #VAR1# Medlemmer
+ Online er #VAR1#
+ Online er #VAR1#
+
diff --git a/plugins/who-is-online/languages/de-DE.xml b/plugins/who-is-online/languages/de-DE.xml
new file mode 100644
index 0000000000..e08477a459
--- /dev/null
+++ b/plugins/who-is-online/languages/de-DE.xml
@@ -0,0 +1,10 @@
+
+
+ Wer ist Online?
+ Zur Zeit sind keine Besucher auf der Webseite.
+ #VAR1# Besucher
+ #VAR1# Mitglied
+ #VAR1# Mitglieder
+ Online sind #VAR1#
+ Online ist #VAR1#
+
diff --git a/plugins/who-is-online/languages/de.xml b/plugins/who-is-online/languages/de.xml
new file mode 100644
index 0000000000..de924a77b3
--- /dev/null
+++ b/plugins/who-is-online/languages/de.xml
@@ -0,0 +1,10 @@
+
+
+ Wer ist Online?
+ Zur Zeit sind keine Besucher auf der Webseite.
+ #VAR1# Besucher
+ #VAR1# Mitglied
+ #VAR1# Mitglieder
+ Online sind #VAR1#
+ Online ist #VAR1#
+
diff --git a/plugins/who-is-online/languages/el.xml b/plugins/who-is-online/languages/el.xml
new file mode 100644
index 0000000000..189a1aa009
--- /dev/null
+++ b/plugins/who-is-online/languages/el.xml
@@ -0,0 +1,10 @@
+
+
+ Ποιος είναι online;
+ Δεν υπάρχουν επισκέπτες αυτή τη στιγμή.
+ #VAR1# Επισκέπτες
+ #VAR1# μέλος
+ #VAR1# μέλη
+ Online είναι #VAR1#
+ Online είναι #VAR1#
+
diff --git a/plugins/who-is-online/languages/en.xml b/plugins/who-is-online/languages/en.xml
new file mode 100644
index 0000000000..5d61db2a8a
--- /dev/null
+++ b/plugins/who-is-online/languages/en.xml
@@ -0,0 +1,27 @@
+
+
+ Who is online?
+ There is no data available for visitors. Log in to see who is online.
+ No visitors are on the website.
+ #VAR1# Visitors
+ #VAR1# member
+ #VAR1# members
+ Online are #VAR1#
+ Online is #VAR1#
+ This plugin displays visitors and registered members on the Overview page.
+ Who is Online
+
+ Time still active
+ The time (in minutes) a user is considered still online after their last action. (default: 10)
+ Show visitors
+ If this setting is enabled the number of visitors will be displayed. Otherwise only logged-in users will be shown. (default: true)
+ Show members to visitors
+ Here you can select how members are displayed to visitors. (default: number of logged in members)
+ Don\'t show logged in members
+ Show logged in members
+ Number of logged in members
+ Show self
+ If this setting is enabled the own status (logged in as well as logged out) will be displayed. (default: true)
+ Show users side by side
+ If this setting is enabled the users will be displayed side by side. Otherwise they will be displayed one below the other. (default: false)
+
diff --git a/plugins/who-is-online/languages/es.xml b/plugins/who-is-online/languages/es.xml
new file mode 100644
index 0000000000..6a686a4f83
--- /dev/null
+++ b/plugins/who-is-online/languages/es.xml
@@ -0,0 +1,8 @@
+
+
+ En línea
+ En este momento no hay visitantes en el sitio web.
+ #VAR1# visitantes
+ #VAR1# Membro
+ #VAR1# Membros
+
diff --git a/plugins/who-is-online/languages/et.xml b/plugins/who-is-online/languages/et.xml
new file mode 100644
index 0000000000..9da927b44a
--- /dev/null
+++ b/plugins/who-is-online/languages/et.xml
@@ -0,0 +1,10 @@
+
+
+ Kes on Internetis?
+ Veebisaidil pole külastajaid..
+ #VAR1# Külastajad
+ #VAR1# liige
+ #VAR1# liikmed
+ Internetis on #VAR1#
+ Internetis on #VAR1#
+
diff --git a/plugins/who-is-online/languages/fi.xml b/plugins/who-is-online/languages/fi.xml
new file mode 100644
index 0000000000..10faccb4d1
--- /dev/null
+++ b/plugins/who-is-online/languages/fi.xml
@@ -0,0 +1,10 @@
+
+
+ Kuka on kirjautuneena?
+ Ei vieraita sivustolla..
+ #VAR1# Vieraita
+ #VAR1# jäsen
+ #VAR1# jäsentä
+ #VAR1# linjoilla
+ #VAR1# on linjoilla
+
diff --git a/plugins/who-is-online/languages/fr.xml b/plugins/who-is-online/languages/fr.xml
new file mode 100644
index 0000000000..ca2148850e
--- /dev/null
+++ b/plugins/who-is-online/languages/fr.xml
@@ -0,0 +1,10 @@
+
+
+ Qui est en ligne ?
+ Il n\'y a actuellement aucun visiteur sur le site Web.
+ #VAR1# visiteurs
+ #VAR1# Membre
+ #VAR1# Membres
+ En ligne sont #VAR1#
+ En ligne est #VAR1#
+
diff --git a/plugins/who-is-online/languages/hu.xml b/plugins/who-is-online/languages/hu.xml
new file mode 100644
index 0000000000..41934ae6c0
--- /dev/null
+++ b/plugins/who-is-online/languages/hu.xml
@@ -0,0 +1,10 @@
+
+
+ Kik vannak online?
+ A honlapon nincsenek látogatók..
+ #VAR1# Látogató
+ #VAR1# tag
+ #VAR1# tag
+ Online van #VAR1#
+ Online van #VAR1#
+
diff --git a/plugins/who-is-online/languages/nb.xml b/plugins/who-is-online/languages/nb.xml
new file mode 100644
index 0000000000..32168364c8
--- /dev/null
+++ b/plugins/who-is-online/languages/nb.xml
@@ -0,0 +1,10 @@
+
+
+ Online
+ Foreløpig er det ingen besøkende til nettstedet.
+ #VAR1# Besøkende
+ #VAR1# Medlem
+ #VAR1# Medlemmer
+ #VAR1# er online
+ #VAR1# er online
+
diff --git a/plugins/who-is-online/languages/nl.xml b/plugins/who-is-online/languages/nl.xml
new file mode 100644
index 0000000000..0949df4e02
--- /dev/null
+++ b/plugins/who-is-online/languages/nl.xml
@@ -0,0 +1,10 @@
+
+
+ Wie is online?
+ Op dit moment zijn geen bezoekers op de website.
+ #VAR1# bezoekers
+ #VAR1# lid
+ #VAR1# leden
+ Online zijn #VAR1#
+ Online is #VAR1#
+
diff --git a/plugins/who-is-online/languages/pl.xml b/plugins/who-is-online/languages/pl.xml
new file mode 100644
index 0000000000..32b43a8f82
--- /dev/null
+++ b/plugins/who-is-online/languages/pl.xml
@@ -0,0 +1,10 @@
+
+
+ Kto jest on-line?
+ Nie ma odwiedzających na stronie.
+ #VAR1# Odwiedzających
+ #VAR1# Członek
+ #VAR1# Członków
+ #VAR1# jest On-line
+ #VAR1# są On-line
+
diff --git a/plugins/who-is-online/languages/pt-BR.xml b/plugins/who-is-online/languages/pt-BR.xml
new file mode 100644
index 0000000000..cb8a47390e
--- /dev/null
+++ b/plugins/who-is-online/languages/pt-BR.xml
@@ -0,0 +1,10 @@
+
+
+ Quem está on-line?
+ Atualmente, não há nenhum visitante no site da Web.
+ #VAR1# Visitantes
+ #VAR1# Membro
+ #VAR1# Membros
+ Estão #VAR1# on-line
+ Está #VAR1# on-line
+
diff --git a/plugins/who-is-online/languages/pt.xml b/plugins/who-is-online/languages/pt.xml
new file mode 100644
index 0000000000..53993e1335
--- /dev/null
+++ b/plugins/who-is-online/languages/pt.xml
@@ -0,0 +1,10 @@
+
+
+ Quem está on-line?
+ Sem visitantes no site da Web.
+ #VAR1# Visitantes
+ #VAR1# membro
+ #VAR1# membros
+ Estão #VAR1# on-line
+ Está #VAR1# on-line
+
diff --git a/plugins/who-is-online/languages/ru.xml b/plugins/who-is-online/languages/ru.xml
new file mode 100644
index 0000000000..5b948e9d35
--- /dev/null
+++ b/plugins/who-is-online/languages/ru.xml
@@ -0,0 +1,10 @@
+
+
+ Кто онлайн?
+ На сайте нет посетителей.
+ #VAR1# посетителей
+ #VAR1# участник
+ #VAR1# участников
+ Онлайн #VAR1#
+ Онлайн #VAR1#
+
diff --git a/plugins/who-is-online/languages/sv.xml b/plugins/who-is-online/languages/sv.xml
new file mode 100644
index 0000000000..be694fdd1a
--- /dev/null
+++ b/plugins/who-is-online/languages/sv.xml
@@ -0,0 +1,10 @@
+
+
+ Vem är online?
+ Inga besökare på webbplatsen ..
+ #VAR1# besökare
+ #VAR1# medlem
+ #VAR1# medlemmar
+ Online är #VAR1#
+ Online är #VAR1#
+
diff --git a/plugins/who-is-online/languages/uk.xml b/plugins/who-is-online/languages/uk.xml
new file mode 100644
index 0000000000..c0c09c6df5
--- /dev/null
+++ b/plugins/who-is-online/languages/uk.xml
@@ -0,0 +1,10 @@
+
+
+ Хто онлайн?
+ На сайті немає відвідувачів.
+ #VAR1# Відвідувачів
+ #VAR1# учасник
+ #VAR1# учасників
+ Онлайн #VAR1#
+ Онлайн #VAR1#
+
diff --git a/plugins/who-is-online/languages/zh.xml b/plugins/who-is-online/languages/zh.xml
new file mode 100644
index 0000000000..68190e7183
--- /dev/null
+++ b/plugins/who-is-online/languages/zh.xml
@@ -0,0 +1,8 @@
+
+
+ 当前谁在线?
+ 还没有访客。
+ #VAR1# 访客
+ #VAR1# 会员
+ #VAR1# 会员
+
diff --git a/adm_plugins/who-is-online/templates/plugin.who-is-online.tpl b/plugins/who-is-online/templates/plugin.who-is-online.tpl
similarity index 58%
rename from adm_plugins/who-is-online/templates/plugin.who-is-online.tpl
rename to plugins/who-is-online/templates/plugin.who-is-online.tpl
index 743cf12580..868b05766a 100644
--- a/adm_plugins/who-is-online/templates/plugin.who-is-online.tpl
+++ b/plugins/who-is-online/templates/plugin.who-is-online.tpl
@@ -1,5 +1,5 @@
-
{$l10n->get('PLG_ONLINE_HEADLINE')}
+ {$l10n->get('PLG_WHO_IS_ONLINE_HEADLINE')}
{$message}
diff --git a/plugins/who-is-online/templates/preferences.plugin.who-is-online.tpl b/plugins/who-is-online/templates/preferences.plugin.who-is-online.tpl
new file mode 100644
index 0000000000..f14e70a5f6
--- /dev/null
+++ b/plugins/who-is-online/templates/preferences.plugin.who-is-online.tpl
@@ -0,0 +1,16 @@
+
+{$javascript}
\ No newline at end of file
diff --git a/plugins/who-is-online/who-is-online.json b/plugins/who-is-online/who-is-online.json
new file mode 100644
index 0000000000..773a5cd77f
--- /dev/null
+++ b/plugins/who-is-online/who-is-online.json
@@ -0,0 +1,62 @@
+{
+ "name": "PLG_WHO_IS_ONLINE_PLUGIN_NAME",
+ "description": "PLG_WHO_IS_ONLINE_PLUGIN_DESCRIPTION",
+ "version": "1.0.0",
+ "author": "Admidio Team",
+ "url": "https://www.admidio.org",
+ "icon": "bi-building-fill-check",
+ "mainFile": "index.php",
+ "overviewPlugin": true,
+ "autoload": {
+ "psr-4": {
+ "WhoIsOnline\\classes\\": "classes/"
+ }
+ },
+ "dependencies": [
+ "Admidio\\Infrastructure\\Plugins\\Overview",
+ "Admidio\\Infrastructure\\Plugins\\PluginAbstract",
+ "Admidio\\Infrastructure\\Utils\\SecurityUtils"
+ ],
+ "defaultConfig": {
+ "who_is_online_plugin_enabled": {
+ "name": "ORG_ACCESS_TO_PLUGIN",
+ "description": "ORG_ACCESS_TO_PLUGIN_DESC",
+ "type": "integer",
+ "value" : 1
+ },
+ "who_is_online_overview_sequence": {
+ "type": "integer",
+ "value" : 8
+ },
+ "who_is_online_time_still_active": {
+ "name": "PLG_WHO_IS_ONLINE_PREFERENCES_TIME_STILL_ACTIVE",
+ "description": "PLG_WHO_IS_ONLINE_PREFERENCES_TIME_STILL_ACTIVE_DESC",
+ "type": "integer",
+ "value" : 10
+ },
+ "who_is_online_show_visitors": {
+ "name": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_VISITORS",
+ "description": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_VISITORS_DESC",
+ "type": "boolean",
+ "value" : true
+ },
+ "who_is_online_show_members_to_visitors": {
+ "name": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_MEMBERS_TO_VISITORS",
+ "description": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_MEMBERS_TO_VISITORS_DESC",
+ "type": "integer",
+ "value" : 2
+ },
+ "who_is_online_show_self": {
+ "name": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_SELF",
+ "description": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_SELF_DESC",
+ "type": "boolean",
+ "value" : true
+ },
+ "who_is_online_show_users_side_by_side": {
+ "name": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_USERS_SIDE_BY_SIDE",
+ "description": "PLG_WHO_IS_ONLINE_PREFERENCES_SHOW_USERS_SIDE_BY_SIDE_DESC",
+ "type": "boolean",
+ "value" : false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Components/Entity/Component.php b/src/Components/Entity/Component.php
index e827d5bb2c..21848de804 100644
--- a/src/Components/Entity/Component.php
+++ b/src/Components/Entity/Component.php
@@ -5,6 +5,8 @@
use Admidio\Infrastructure\Exception;
use Admidio\Infrastructure\Database;
use Admidio\Infrastructure\Entity\Entity;
+use Admidio\Infrastructure\Plugins\PluginAbstract;
+use Admidio\Infrastructure\Plugins\PluginManager;
use Admidio\UI\Presenter\InventoryPresenter;
/**
@@ -197,6 +199,7 @@ public static function isAdministrable(string $componentName): bool
case 'MESSAGES': // fallthrough
case 'ORGANIZATIONS': // fallthrough
case 'PREFERENCES': // fallthrough
+ case 'PLUGINS': // fallthrough
case 'ROOMS':
if ($gCurrentUser->isAdministrator()) {
return true;
@@ -321,11 +324,21 @@ public static function isVisible(string $componentName): bool
case 'MENU': // fallthrough
case 'ORGANIZATIONS': // fallthrough
case 'PREFERENCES': // fallthrough
+ case 'PLUGINS': // fallthrough
case 'ROOMS':
if ($gCurrentUser->isAdministrator()) {
return true;
}
break;
+
+ default:
+ // check if the component is a plugin and it is visible
+ $pluginManager = new PluginManager();
+ $plugin = $pluginManager->getPluginByName($componentName);
+ if ($plugin) {
+ return ($plugin instanceof PluginAbstract) ? $plugin::getInstance()->isVisible() : false;
+ }
+ break;
}
return false;
diff --git a/src/Components/Entity/ComponentUpdate.php b/src/Components/Entity/ComponentUpdate.php
index 00c1d8f715..c51d4e2070 100644
--- a/src/Components/Entity/ComponentUpdate.php
+++ b/src/Components/Entity/ComponentUpdate.php
@@ -33,7 +33,7 @@
class ComponentUpdate extends Component
{
public const UPDATE_STEP_STOP = 'stop';
-
+ private static $database = null;
/**
* Constructor that will create an object for component updating.
* @param Database $database Object of the class Database. This should be the default global object **$gDb**.
@@ -42,8 +42,8 @@ class ComponentUpdate extends Component
public function __construct(Database $database)
{
parent::__construct($database);
-
- UpdateStepsCode::setDatabase($database);
+
+ self::$database = $database;
}
/**
@@ -83,10 +83,25 @@ private function getXmlObject(int $mainVersion, int $minorVersion): SimpleXMLEle
$message = 'XML-Update file not found!';
$gLogger->warning($message, array('filePath' => $updateFile));
+ throw new UnexpectedValueException($message);
+ } elseif ($this->getValue('com_type') === 'PLUGIN') {
+ $updateFile = ADMIDIO_PATH . FOLDER_PLUGINS . '/' . $this->getValue('com_name_intern') . '/db_scripts/update_'.$mainVersion.'_'.$minorVersion.'.xml';
+
+ if (is_file($updateFile)) {
+ try {
+ return new SimpleXMLElement($updateFile, 0, true);
+ } catch (\Exception $e) {
+ throw new Exception($e->getMessage());
+ }
+ }
+
+ $message = 'XML-Update file not found!';
+ $gLogger->warning($message, array('filePath' => $updateFile));
+
throw new UnexpectedValueException($message);
}
- throw new UnexpectedValueException('No System update!');
+ throw new UnexpectedValueException('No System or Plugin update!');
}
/**
@@ -127,12 +142,22 @@ public function getMaxUpdateStep(): int
* Get method name and execute this method
* @param string $updateStepContent
*/
- private static function executeUpdateMethod(string $updateStepContent)
+ private static function executeUpdateMethod(string $updateStepContent, string $namespace)
{
// get the method name (remove "UpdateStepsCode::")
+ $className = substr($updateStepContent, 0, strpos($updateStepContent, '::'));
$methodName = substr($updateStepContent, strrpos($updateStepContent, '::') + 2);
+
+ // add namespace to class name
+ $className = $namespace . $className;
+
+ // check if the class exists and the method is defined in this class
+ if (!class_exists($className) || !method_exists($className, $methodName)) {
+ throw new Exception('Update class ' . $className . ' not found!');
+ }
// now call the method
- UpdateStepsCode::{$methodName}();
+ $className::setDatabase(self::$database);
+ $className::{$methodName}();
}
/**
@@ -160,7 +185,7 @@ private function executeUpdateSql(string $sql, bool $showError): bool
* @param string $version A version string of the version corresponding to the $xmlNode
* @throws Exception
*/
- private function executeStep(SimpleXMLElement $xmlNode, string $version = '')
+ private function executeStep(SimpleXMLElement $xmlNode, string $version = '', $namespace = 'Admidio\\InstallationUpdate\\Service\\')
{
global $gLogger;
@@ -180,7 +205,7 @@ private function executeStep(SimpleXMLElement $xmlNode, string $version = '')
// then call this function and don't execute a SQL statement
if (str_starts_with($updateStepContent, 'UpdateStepsCode::')) {
try {
- self::executeUpdateMethod($updateStepContent);
+ self::executeUpdateMethod($updateStepContent, $namespace);
} catch (Throwable $e) {
throw new Exception('
@@ -299,4 +324,93 @@ public function update(string $targetVersion)
WHERE com_type IN (\'SYSTEM\', \'MODULE\')';
$this->db->queryPrepared($sql, array(ADMIDIO_VERSION, ADMIDIO_VERSION_BETA));
}
+
+ /**
+ * Do a loop through all versions start with the last installed version and end with the current version of the
+ * file system (**$targetVersion**). Within every subversion the method will search for an update xml file and
+ * execute all steps in this file until the end of file is reached. If an error occurred then the update will
+ * be stopped and the system will be marked with update not completed so that it's possible to continue the
+ * update later if the problem was fixed.
+ * @param string $targetVersion The target version to update.
+ * @throws Exception
+ */
+ public function updatePlugin(string $targetVersion, string $namespace)
+ {
+ global $gLogger;
+
+ if (empty($this->getValue('com_version'))) {
+ $currentVersionArray = array(0, 0, 0);
+ } else {
+ $currentVersionArray = self::getVersionArrayFromVersion($this->getValue('com_version'));
+ }
+ $targetVersionArray = self::getVersionArrayFromVersion($targetVersion);
+ $initialMinorVersion = $currentVersionArray[1];
+
+ // if the update is from a version lower than 4.2.0 than the field com_update_complete doesn't exist
+ // otherwise set the status to incomplete update
+ if(version_compare($this->getValue('com_update_version'), '4.2.0', '>')) {
+ $this->setValue('com_update_completed', false);
+ $this->save();
+ }
+
+ for ($mainVersion = $currentVersionArray[0]; $mainVersion <= $targetVersionArray[0]; ++$mainVersion) {
+ // Set max subversion for iteration. If we are in the loop of the target main version
+ // then set target minor-version to the max version
+ $maxMinorVersion = 20;
+ if ($mainVersion === $targetVersionArray[0]) {
+ $maxMinorVersion = $targetVersionArray[1];
+ }
+
+ for ($minorVersion = $initialMinorVersion; $minorVersion <= $maxMinorVersion; ++$minorVersion) {
+ // if version is not equal to current version then start update step with 0
+ if ($mainVersion !== $currentVersionArray[0] || $minorVersion !== $currentVersionArray[1]) {
+ $this->setValue('com_update_step', 0);
+ $this->save();
+ }
+
+ // save current version to system component
+ $this->setValue('com_version', $mainVersion . '.' . $minorVersion . '.0');
+ $this->save();
+
+ // output of the version number for better debugging
+ $gLogger->notice('UPDATE: Start executing update steps to version ' . $mainVersion . '.' . $minorVersion);
+
+ // open xml file for this version
+ try {
+ $xmlObject = $this->getXmlObject($mainVersion, $minorVersion);
+
+ // go step by step through the SQL statements and execute them
+ foreach ($xmlObject->children() as $updateStep) {
+ if ((string)$updateStep === self::UPDATE_STEP_STOP) {
+ break;
+ }
+ if ((int)$updateStep['id'] > (int)$this->getValue('com_update_step')) {
+ if ($namespace === '') {
+ $namespace = 'Admidio\\InstallationUpdate\\Service\\';
+ }
+ $this->executeStep($updateStep, $mainVersion . '.' . $minorVersion . '.0', $namespace);
+ } else {
+ $gLogger->info('UPDATE: Skip update step Nr: ' . (int)$updateStep['id']);
+ }
+ }
+ } catch (Exception $exception) {
+ throw new Exception($exception->getMessage());
+ } catch (UnexpectedValueException|\Exception $exception) {
+ // TODO
+ }
+
+ $gLogger->notice('UPDATE: Finish executing update steps to version '.$mainVersion.'.'.$minorVersion);
+ }
+
+ // reset subversion because we want to start update for next main version with subversion 0
+ $initialMinorVersion = 0;
+ }
+
+ // save current version of plugin
+ $sql = 'UPDATE '.TBL_COMPONENTS.'
+ SET com_version = ? -- $targetVersion
+ WHERE com_id = ? -- :com_id';
+ $this->db->queryPrepared($sql, array($targetVersion, $this->getValue('com_id')));
+
+ }
}
diff --git a/src/Infrastructure/Language.php b/src/Infrastructure/Language.php
index 8cf94bd231..b22966ff91 100644
--- a/src/Infrastructure/Language.php
+++ b/src/Infrastructure/Language.php
@@ -123,7 +123,7 @@ public function addLanguageFolderPath(string $languageFolderPath): bool
}
/**
- * Read language folder of each plugin in adm_plugins and add this folder to the language folder
+ * Read language folder of each plugin in plugins and add this folder to the language folder
* array of this class.
*/
public function addPluginLanguageFolderPaths(): void
diff --git a/src/Infrastructure/Plugins/PluginAbstract.php b/src/Infrastructure/Plugins/PluginAbstract.php
new file mode 100644
index 0000000000..4b0ac8088f
--- /dev/null
+++ b/src/Infrastructure/Plugins/PluginAbstract.php
@@ -0,0 +1,973 @@
+setValue('men_men_id_parent', 3); // extensions node has the id of 3 by default
+ $pluginMenuEntry->setValue('men_name', self::$name);
+ $pluginMenuEntry->setValue('men_com_id', self::$pluginComId);
+ $pluginMenuEntry->setValue('men_description', self::$metadata['description']);
+ $pluginMenuEntry->setValue('men_url', FOLDER_PLUGINS . '/' . $className . '/' . (self::$metadata['mainFile'] ?? $className . '.php'));
+ $pluginMenuEntry->setValue('men_icon', self::$metadata['icon']);
+ $pluginMenuEntry->save();
+
+ // rename the menu entry internal name to the class name
+ $pluginMenuEntry->readDataById($pluginMenuEntry->getValue('men_id'));
+ $pluginMenuEntry->setValue('men_name_intern', $className);
+ $pluginMenuEntry->save();
+ }
+
+ /**
+ * Remove the plugin menu entry from the database.
+ * This method is called during the uninstallation of the plugin.
+ * @throws Exception
+ */
+ public static function removeMenuEntry(): void
+ {
+ global $gDb;
+
+ $className = basename(self::$pluginPath);
+
+ // delete the plugin menu entry
+ $pluginMenuEntry = new MenuEntry($gDb);
+ if ($pluginMenuEntry->readDataByColumns(array('men_name_intern' => $className))) {
+ $pluginMenuEntry->delete();
+ }
+ }
+
+ /**
+ * Initialize the preferences panel for this plugin.
+ * This method will be called automatically when the plugin is activated.
+ * It will register the preferences panel for this plugin in the PreferencesService.
+ */
+ public static function initPreferencePanelCallback(): void
+ {
+ // get the calling plugin name in lowercase and without underscores, hyphens, spaces or other special characters
+ $callingPlugin = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', self::getComponentName()));
+
+ // get psr4 autoload mappings and preferences file from metadata
+ $psr4 = self::$metadata['autoload']['psr-4'] ?? null;
+ $preferencesFile = self::$metadata['preferencesFile'] ?? null;
+
+ // if a preferences file is defined in the metadata, the plugin handles the preferences itself
+ if (isset($preferencesFile)) {
+ return;
+ }
+
+ // check if psr4 autoload mappings are defined, otherwise we cannot find the presenter class
+ if (!is_array($psr4)) {
+ return;
+ }
+
+ // look for presenter class path in the psr4 autoload mappings
+ $preferencesClass = null;
+
+ foreach ($psr4 as $prefix => $relativePath) {
+ if (str_contains(strtolower($prefix), $callingPlugin)) {
+ $presenterBasePath = self::getPluginPath() . DIRECTORY_SEPARATOR . $relativePath;
+ // now loop over all files in the presenter path to find a preferences presenter
+ $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($presenterBasePath));
+ foreach ($it as $file) {
+ if (!$file->isFile() || !str_contains($file->getFilename(), 'PreferencesPresenter.php')) {
+ continue;
+ }
+ $preferencesFile = $file->getPathname();
+ $preferencesClass = self::getClassNameFromFile($preferencesFile);
+ break;
+ }
+ }
+ }
+
+ if (isset($preferencesClass) && class_exists($preferencesClass)) {
+
+ // get the function name for the preferences panel form
+ $className = str_replace('PreferencesPresenter', '', (new ReflectionClass($preferencesClass))->getShortName());
+ $functionName = 'create' . $className . 'Form';
+ if (!method_exists($preferencesClass, $functionName)) {
+ throw new Exception('The preferences class ' . $preferencesClass . ' does not have a method ' . $functionName . '().');
+ }
+ if (self::isOverviewPlugin()) {
+ // register the overview preferences presenter for this plugin
+ PreferencesService::addOverviewPluginPreferencesPresenter(self::getComponentId(), [$preferencesClass, $functionName]);
+ } else {
+ // register the preferences presenter for this plugin
+ PreferencesService::addPluginPreferencesPresenter(self::getComponentId(), [$preferencesClass, $functionName]);
+ }
+ }
+ }
+
+ /**
+ * Parse a PHP file and return the first class name found.
+ */
+ private static function getClassNameFromFile(string $file): ?string
+ {
+ $src = file_get_contents($file);
+ $tokens = token_get_all($src);
+ $namespace = '';
+ $class = null;
+ for ($i = 0, $count = count($tokens); $i < $count; $i++) {
+ if ($tokens[$i][0] === T_NAMESPACE) {
+ $i++;
+ while ($tokens[$i][0] === T_WHITESPACE) $i++;
+ while (in_array($tokens[$i][0], [T_STRING, T_NAME_QUALIFIED, T_NS_SEPARATOR])) {
+ $namespace .= $tokens[$i++][1];
+ }
+ }
+ if ($tokens[$i][0] === T_CLASS) {
+ // skip whitespace
+ $i++;
+ while ($tokens[$i][0] === T_WHITESPACE) $i++;
+ $class = $tokens[$i][1];
+ break;
+ }
+ }
+ if ($class) {
+ return $namespace ? "$namespace\\$class" : $class;
+ }
+ return null;
+ }
+
+ /**
+ * Reads the plugin metadata from the plugin file.
+ *
+ * @throws Exception
+ */
+ private function readPluginMetadata(): void
+ {
+ // get the plugin name, version and metadata from the plugin file
+ $configFiles = self::getStaticFiles('json');
+ if (!isset($configFiles) || count($configFiles) === 0) {
+ //throw new Exception('Plugin configuration file not found.');
+ return;
+ } else {
+ $configFile = $configFiles[0];
+ }
+ $configData = json_decode(file_get_contents($configFile), true);
+ if ($configData === null) {
+ throw new Exception('Plugin configuration file ' . $configFile . ' is not valid JSON.');
+ } else {
+ self::$name = $configData['name'] ?? '';
+ self::$dependencies = $configData['dependencies'] ?? array();
+ self::$defaultConfig = $configData['defaultConfig'] ?? array();
+ self::$metadata = $configData;
+ }
+ }
+
+ /**
+ * @return PluginAbstract
+ * @throws Exception
+ * @throws ReflectionException
+ */
+ public static function getInstance(): PluginAbstract
+ {
+ // reset global variables
+ self::$pluginComId = 0;
+ self::$pluginPath = '';
+ self::$name = '';
+ self::$version = '0.0.0';
+ self::$dependencies = array();
+ self::$metadata = array();
+ self::$defaultConfig = array();
+
+ // get the class name of the called class
+ $class = get_called_class();
+ if (!array_key_exists($class, self::$instances)) {
+ self::$instances[$class] = new $class();
+ }
+
+ // set the plugin path to the folder of this class
+ $reflection = new ReflectionClass(self::$instances[$class]);
+ self::$pluginPath = dirname($reflection->getFileName(), 2);
+
+ // read the plugin metadata
+ self::$instances[$class]->readPluginMetadata();
+
+ // after we have the plugin path and metadata, we can do the class autoload
+ self::$instances[$class]->doClassAutoload($class);
+
+ // check if the plugin is installed
+ if (self::$instances[$class]->isInstalled()) {
+ global $gDb;
+ // get the component id of the plugin
+ $sql = 'SELECT com_id FROM ' . TBL_COMPONENTS . ' WHERE com_name = ? AND com_type = ?';
+ $statement = $gDb->queryPrepared($sql, array(self::getName(), 'PLUGIN'));
+ self::$pluginComId = (int)$statement->fetchColumn();
+
+ // get the installed version of the plugin
+ $sql = 'SELECT com_version FROM ' . TBL_COMPONENTS . ' WHERE com_name = ? AND com_type = ?';
+ $statement = $gDb->queryPrepared($sql, array(self::getName(), 'PLUGIN'));
+ self::$version = (string)$statement->fetchColumn();
+ }
+
+ return self::$instances[$class];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName(): string
+ {
+ return self::$name;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getIcon(): string
+ {
+ return self::$metadata['icon'] ?? '';
+ }
+
+ /**
+ * @return string
+ */
+ public static function getVersion(): string
+ {
+ return self::$version;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getMetadata(): array
+ {
+
+ return self::$metadata;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getDependencies(): array
+ {
+ return self::$dependencies;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getSupportedLanguages(): array
+ {
+ $dir = __DIR__ . DIRECTORY_SEPARATOR . 'languages';
+
+ $langFiles = array();
+ foreach (scandir($dir) as $entry) {
+ $entryPath = $dir . DIRECTORY_SEPARATOR . $entry;
+ $entryInfo = pathinfo($entryPath);
+ if (is_file($entryPath) && $entryInfo['extension'] === 'xml') {
+ $langFiles[] = $entryInfo['filename'];
+ }
+ }
+
+ return $langFiles;
+ }
+
+ /**
+ * @param string|null $type
+ * @param string $path
+ * @return array
+ * @throws Exception
+ */
+ public static function getStaticFiles(?string $type = null, string $path = '' /* self::$pluginPath */): array
+ {
+ if ($path === '') {
+ $path = self::$pluginPath;
+ }
+
+ if ($type !== null && !is_string($type)) {
+ throw new InvalidArgumentException('Type must be "null" or a "string".');
+ }
+
+ if (!is_dir($path)) {
+ throw new Exception('Plugin path does not exist: ' . $path);
+ }
+
+ $files = array();
+ foreach (scandir($path) as $entry) {
+ $entryPath = $path . DIRECTORY_SEPARATOR . $entry;
+ if (is_file($entryPath)) {
+ $entryInfo = pathinfo($entryPath);
+
+ if (!isset($entryInfo['extension'])) {
+ $entryInfo['extension'] = '';
+ }
+ if (!array_key_exists($entryInfo['extension'], $files)) {
+ $files[$entryInfo['extension']] = array();
+ }
+
+ $files[$entryInfo['extension']][] = $entryPath;
+ }
+ }
+
+ if ($type === null) {
+ return $files;
+ } else {
+ return (array_key_exists($type, $files)) ? $files[$type] : array();
+ }
+ }
+
+ public static function getPluginConfigValues(): array
+ {
+ global $gSettingsManager;
+ $config = array();
+
+ // loop over all default config keys and get their values from the database if the key exists
+ foreach (self::$defaultConfig as $key => $value) {
+ if ($gSettingsManager->has($key)) {
+ switch ($value['type']) {
+ case 'integer':
+ $config[$key] = $gSettingsManager->getInt($key);
+ break;
+ case 'boolean':
+ $config[$key] = $gSettingsManager->getBool($key);
+ break;
+ case 'array':
+ $valueString = $gSettingsManager->get($key);
+ if ($gSettingsManager->has($key . '_keys')) {
+ // if the keys are stored separately, use them to create the array
+ $keyString = $gSettingsManager->get($key . '_keys');
+ $config[$key] = $valueString === "" ? array() : array_combine(explode(',', $keyString), explode(',', $valueString));
+ } else {
+ // if no keys are stored, use the value string as the value
+ $config[$key] = $valueString === "" ? array() : explode(',', $valueString);
+ }
+ break;
+ case 'string':
+ default:
+ $config[$key] = $gSettingsManager->get($key);
+ break;
+ }
+ } else {
+ $config[$key] = $value['value'];
+ }
+ }
+
+ return $config;
+ }
+
+ /**
+ * Get the plugin configuration
+ * @return array Returns the plugin configuration
+ */
+ public static function getPluginConfig(): array
+ {
+ $config = self::$defaultConfig;
+ // get the plugin config values from the database
+ $values = self::getPluginConfigValues();
+ // loop over all default config keys and set their current values
+ foreach ($config as $key => $value) {
+ $config[$key]['value'] = $values[$key];
+ }
+ return $config;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getPluginPath(): string
+ {
+ return self::$pluginPath;
+ }
+
+ /**
+ * @return int
+ */
+ public static function getComponentId(): int
+ {
+ return self::$pluginComId;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getComponentName(): string
+ {
+ return basename(self::$pluginPath);
+ }
+
+ /**
+ * Get the sequence of the plugin in the components table.
+ * @return int Returns the sequence of the plugin.
+ * @throws Exception
+ */
+ public static function getPluginSequence(): int
+ {
+ $pluginConfig = self::getPluginConfig();
+ $sequenceSuffix = '_overview_sequence';
+ // find the sequence key in the plugin config
+ $sequenceKeys = array_filter(array_keys($pluginConfig), function($k) use ($sequenceSuffix) {
+ return substr($k, -strlen($sequenceSuffix)) === $sequenceSuffix;
+ });
+
+ if (!empty($sequenceKeys)) {
+ // get the first matching key
+ $sequenceKey = array_values($sequenceKeys)[0];
+
+ // return the value from the config if available
+ return $pluginConfig[$sequenceKey]['value'] ?? 0;
+ }
+ return 0;
+ }
+
+ /**
+ * Check if the plugin has all dependencies installed.
+ * @return bool Returns true if all dependencies are installed, false otherwise.
+ * @throws Exception
+ */
+ public static function checkDependencies(): bool
+ {
+ // check if the plugin has dependencies
+ if (empty(self::$dependencies)) {
+ return true;
+ }
+
+ $missing = array();
+
+ // loop over all dependencies and check if they are available
+ foreach (self::$dependencies as $dependency) {
+ // dependencies should be a class name of the admidio core or the final namespace of the class
+
+ // check if the dependency is a fully qualified class name or only a short name
+ if (class_exists($dependency, true)) {
+ // if the class exists, continue to the next dependency
+ continue;
+ }
+
+ // if the class does not exist, try to find it in the Admidio namespace
+ if (self::findAdmidioClass($dependency) === null) {
+ $missing[] = $dependency;
+ }
+ }
+
+ if (!empty($missing)) {
+ // not all dependencies are met
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Scan src/ under the Admidio\ namespace for a class named $shortName.
+ * Returns the fully qualified class name if found, or null otherwise.
+ *
+ * @param string $shortName
+ * @return string|null
+ */
+ private static function findAdmidioClass(string $shortName): ?string
+ {
+ // define the path to the src directory
+ $srcDir = ADMIDIO_PATH . '/src';
+ $prefix = 'Admidio\\';
+
+ $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($srcDir));
+
+ // iterate through the directory structure to find the class file
+ foreach ($it as $file) {
+ if (!$file->isFile() || $file->getFilename() !== $shortName . '.php') {
+ continue;
+ }
+
+ // if the file is found, construct the full class name
+ $relPath = substr($file->getPathname(), strlen($srcDir) + 1, -4);
+ $subNamespaces = str_replace(DIRECTORY_SEPARATOR, '\\', $relPath);
+ $fullName = $prefix . ($subNamespaces !== '' ? $subNamespaces : $shortName);
+
+
+ if (class_exists($fullName, true)) {
+ return $fullName;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isInstalled(): bool
+ {
+ global $gDb;
+ // check if the plugin exists in components database table
+ $sql = 'SELECT COUNT(*) AS count FROM ' . TBL_COMPONENTS . ' WHERE com_name = ? AND com_type = ?';
+ $statement = $gDb->queryPrepared($sql, array(self::getName(), 'PLUGIN'));
+ $columns = (int)$statement->fetchColumn();
+
+ return $columns > 0;
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isActivated(): bool
+ {
+ return self::isInstalled() && (self::getComponentId() > 0);
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isVisible(): bool
+ {
+ global $gValidLogin;
+
+ // check if the plugin is activated
+ if (!self::isActivated()) {
+ return false;
+ }
+
+ // check if the plugin has setting ending with '_enabled'
+ $pluginConfig = self::getPluginConfig();
+ foreach ($pluginConfig as $key => $value) {
+ if (str_ends_with($key, '_enabled') && isset($value['value'])) {
+ if (($value['value'] === 1 || ($value['value'] === 2 && $gValidLogin))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isOverviewPlugin(): bool
+ {
+ global $gDb;
+ // check if the plugin exists in components database table and is of type 'PLUGIN' and has overview flag
+ $sql = 'SELECT COUNT(*) AS count FROM ' . TBL_COMPONENTS . ' WHERE com_name = ? AND com_type = ? AND com_overview_plugin = ?';
+ $statement = $gDb->queryPrepared($sql, array(self::getName(), 'PLUGIN', true));
+ $columns = (int)$statement->fetchColumn();
+
+ return $columns > 0;
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isAdmidioPlugin(): bool
+ {
+ $admidioOverviewPlugins = array('announcement-list', 'birthday', 'calendar', 'event-list', 'latest-documents-files', 'login-form', 'random-photo', 'who-is-online');
+
+ if (in_array(strtolower(self::getComponentName()), $admidioOverviewPlugins)) {
+ return true;
+ }
+ return false;
+ }
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function isUpdateAvailable(): bool
+ {
+ return version_compare(self::$version, self::$metadata['version'], '<');
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function doClassAutoload(string $class): bool
+ {
+ // Register plugin PSR-4 autoload mappings from plugin metadata (no DB required).
+ // Each plugin can define in its JSON:
+ // {
+ // "autoload": { "psr-4": { "Vendor\\Plugin\\": "classes/" } }
+ // }
+ try {
+ if (class_exists(ClassLoader::class)) {
+ $loaders = ClassLoader::getRegisteredLoaders();
+ $composerLoader = reset($loaders);
+
+ if ($composerLoader) {
+ $existingPrefixes = $composerLoader->getPrefixesPsr4();
+
+ $instance = self::$instances[$class];
+
+ // check if the instance is valid
+ if (!$instance instanceof PluginAbstract) {
+ return false;
+ }
+
+ // read the plugin metadata if not already done
+ if (empty($instance::$metadata)) {
+ $instance->readPluginMetadata();
+ }
+
+ // check if psr4 autoload mappings are defined
+ $psr4 = $instance::$metadata['autoload']['psr-4'] ?? null;
+ if (!is_array($psr4)) {
+ return false;
+ }
+
+ // register each PSR-4 mapping for this plugin
+ foreach ($psr4 as $prefix => $relativePath) {
+ if (!is_string($prefix) || !is_string($relativePath)) {
+ continue;
+ }
+
+ $prefix = rtrim($prefix, '\\') . '\\';
+ $absolutePath = $instance::$pluginPath . DIRECTORY_SEPARATOR . trim($relativePath, "/\\");
+
+ if (!is_dir($absolutePath)) {
+ continue;
+ }
+
+ // already registered?
+ if (isset($existingPrefixes[$prefix]) && in_array($absolutePath, $existingPrefixes[$prefix], true)) {
+ continue;
+ }
+
+ // register the PSR-4 mapping for this plugin
+ $composerLoader->addPsr4($prefix, $absolutePath, true);
+ }
+ }
+ }
+ } catch (\Throwable $e) {
+ throw new Exception('Error during plugin class autoload registration: ' . $e->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * @param bool $enable
+ * @throws Exception
+ */
+ private static function toggleForeignKeyChecks(bool $enable): void
+ {
+ global $gDb;
+
+ if (DB_ENGINE === Database::PDO_ENGINE_MYSQL) {
+ // disable foreign key checks for mysql, so tables can easily be deleted
+ $sql = 'SET foreign_key_checks = ' . (int)$enable;
+ $gDb->queryPrepared($sql);
+ }
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function doInstall(bool $addMenuEntry = true): bool
+ {
+ global $gDb, $gSettingsManager;
+
+ // check if the plugin is already installed
+ if (self::isInstalled()) {
+ return false;
+ }
+
+ // insert default plugin config values into the database
+ $configValues = self::getPluginConfigValues();
+ foreach ($configValues as $key => $value) {
+ if (is_array($value)) {
+ $gSettingsManager->set($key, implode(',', $value));
+ // check if the value contains keys
+ if (array_keys($value) !== range(0, count($value) - 1)) {
+ // if the value is an associative array, store the keys separately
+ $gSettingsManager->set($key . '_keys', implode(',', array_keys($value)));
+ }
+ } elseif (is_bool($value)) {
+ // if the value is a boolean, store it as an integer
+ $gSettingsManager->set($key, (int)$value);
+ } else {
+ $gSettingsManager->set($key, $value);
+ }
+ }
+
+ // check if the db_scripts folder exists
+ if (is_dir(self::$pluginPath . DIRECTORY_SEPARATOR . 'db_scripts')) {
+ // check if the plugin has a .sql file to create the database tables
+ $sqlFiles = self::getStaticFiles('sql', self::$pluginPath . DIRECTORY_SEPARATOR . 'db_scripts');
+ if (isset($sqlFiles) && count($sqlFiles) > 0) {
+ $sqlFile = null;
+ if (count($sqlFiles) === 1) {
+ // if there is only one sql file, take it
+ $sqlFile = $sqlFiles[0];
+ } else {
+ // if there are multiple sql files, we need to find the installation file
+ // the installation file needs to be named *install.sql
+ foreach ($sqlFiles as $file) {
+ if (str_contains($file, 'install')) {
+ $sqlFile = $file;
+ break;
+ }
+ }
+ }
+ if ($sqlFile !== null) {
+ // read data from sql install script and execute all statements to the current database
+ if (!is_file($sqlFile)) {
+ throw new Exception('INS_DATABASE_FILE_NOT_FOUND', array(basename($sqlFile), dirname($sqlFile)));
+ }
+
+ try {
+ $sqlStatements = Database::getSqlStatementsFromSqlFile($sqlFile);
+ } catch (RuntimeException) {
+ throw new Exception('INS_ERROR_OPEN_FILE', array($sqlFile));
+ }
+
+ self::toggleForeignKeyChecks(false);
+ foreach ($sqlStatements as $sqlStatement) {
+ $gDb->queryPrepared($sqlStatement);
+ }
+ self::toggleForeignKeyChecks(true);
+ }
+ }
+ }
+
+ // install the plugin
+ $componentUpdateHandle = new ComponentUpdate($gDb);
+ $componentUpdateHandle->readDataByColumns(
+ array(
+ 'com_type' => 'PLUGIN',
+ 'com_name' => self::getName(),
+ 'com_name_intern' => strtoupper(basename(self::$pluginPath)),
+ 'com_overview_plugin' => self::$metadata['overviewPlugin']
+ )
+ );
+
+ // after we have the plugin path and metadata, we can do the class autoload
+ self::doClassAutoload(get_called_class());
+ // check if psr4 autoload mappings are defined
+ $psr4 = self::$metadata['autoload']['psr-4'] ?? null;
+ if (is_array($psr4) && count($psr4) > 0) {
+ // define the update class namespace for the plugin
+ foreach ($psr4 as $prefix => $relativePath) {
+ if (!is_string($prefix) || !is_string($relativePath)) {
+ continue;
+ }
+
+ // if the class does not exist, it will be ignored when performing updatePlugin()
+ $updateStepCodeNamespace = $prefix . 'Service\\';
+ $componentUpdateHandle->updatePlugin(self::$metadata['version'], $updateStepCodeNamespace);
+ }
+ }
+
+ // set the new component id of the plugin
+ self::$pluginComId = $componentUpdateHandle->getValue('com_id');
+
+ // set the installed version of the plugin
+ self::$version = self::$metadata['version'];
+
+ // add the plugin menu entry to the database
+ if (!self::isOverviewPlugin() && $addMenuEntry) {
+ self::addMenuEntry();
+ }
+
+ // perform additional installation tasks
+ // TODO: implement function to perform updateSteps for the plugin
+ // e.g.: $componentUpdateHandle->doUpdateSteps();
+
+ return true;
+ }
+
+ /**
+ * @param array $options
+ * @return bool
+ * @throws Exception
+ * @throws InvalidArgumentException
+ */
+ public static function doUninstall(bool $removeMenuEntry = true, array $options = array()): bool
+ {
+ if (!is_array($options)) {
+ throw new InvalidArgumentException('Options must be an "array".');
+ }
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ return false;
+ }
+
+ global $gDb, $gSettingsManager;
+
+ // check if the db_scripts folder exists
+ if (is_dir(self::$pluginPath . DIRECTORY_SEPARATOR . 'db_scripts')) {
+ // check if the plugin has a .sql file to delete the database tables
+ $sqlFiles = self::getStaticFiles('sql', self::$pluginPath . DIRECTORY_SEPARATOR . 'db_scripts');
+ if (isset($sqlFiles) && count($sqlFiles) > 0) {
+ $sqlFile = null;
+ // if there is only a db.sql file, no uninstall script is needed
+ if (count($sqlFiles) > 1) {
+ // if there are multiple sql files, we need to find the uninstallation file
+ // the file needs to be named *uninstall.sql
+ foreach ($sqlFiles as $file) {
+ if (str_contains($file, 'uninstall')) {
+ $sqlFile = $file;
+ break;
+ }
+ }
+ }
+ if ($sqlFile !== null) {
+ // read data from sql install script and execute all statements to the current database
+ if (!is_file($sqlFile)) {
+ throw new Exception('INS_DATABASE_FILE_NOT_FOUND', array(basename($sqlFile), dirname($sqlFile)));
+ }
+
+ try {
+ $sqlStatements = Database::getSqlStatementsFromSqlFile($sqlFile);
+ } catch (RuntimeException) {
+ throw new Exception('INS_ERROR_OPEN_FILE', array($sqlFile));
+ }
+
+ self::toggleForeignKeyChecks(false);
+ foreach ($sqlStatements as $sqlStatement) {
+ $gDb->queryPrepared($sqlStatement);
+ }
+ self::toggleForeignKeyChecks(true);
+ }
+ }
+ }
+
+ // delete the plugin config values from the database
+ foreach (self::getPluginConfigValues() as $key => $value) {
+ if ($gSettingsManager->has($key)) {
+ $gSettingsManager->del($key);
+ }
+ }
+
+ // update $gSettingsManager to remove the plugin config values
+ $gSettingsManager->resetAll();
+
+ // remove the plugin menu entry
+ if ($removeMenuEntry) {
+ self::removeMenuEntry();
+ }
+
+ // delete the plugin from the components table
+ $plugin = new Component($gDb, self::$pluginComId);
+ $plugin->delete();
+
+ // reset the plugin component id
+ self::$pluginComId = 0;
+
+ // reset the installed version of the plugin
+ self::$version = '0.0.0';
+
+ // perform additional uninstallation tasks
+ // TODO: implement function to perform additional uninstallation tasks for the plugin
+ return true;
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public static function doUpdate(): bool
+ {
+ global $gDb, $gSettingsManager;
+
+ // check if the plugin is installed
+ if (!self::isInstalled()) {
+ return false;
+ }
+
+ // add new plugin config values to the database
+ // insert default plugin config values into the database
+ $configValues = self::getPluginConfigValues();
+ foreach ($configValues as $key => $value) {
+ if (is_array($value)) {
+ $gSettingsManager->set($key, implode(',', $value), false);
+ $gSettingsManager->set($key . '_keys', implode(',', array_keys($value)), false);
+ } else {
+ $gSettingsManager->set($key, $value, false);
+ }
+ }
+
+ // update the plugin
+ $componentUpdateHandle = new ComponentUpdate($gDb);
+ $componentUpdateHandle->readDataByColumns(array('com_name' => self::getName(), 'com_name_intern' => basename(self::$pluginPath)));
+ // define the update class namespace for the plugin
+ // if the update class does not exist, it will be ignored when performing updatePlugin()
+ $updateStepCodeNamespace = 'Plugins\\' . basename(self::$pluginPath) . '\\classes\\Service\\';
+ $componentUpdateHandle->updatePlugin(self::$metadata['version'], $updateStepCodeNamespace);
+
+ // set the installed version of the plugin
+ self::$version = self::$metadata['version'];
+
+ // perform additional update tasks
+ // TODO: implement function to perform updateSteps for the plugin
+ // e.g.: $componentUpdateHandle->doUpdateSteps();
+ return true;
+ }
+
+ public static function initParams(array $params = array()): bool
+ {
+ if (!is_array($params)) {
+ throw new InvalidArgumentException('Params must be an "array".');
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Plugins/PluginInterface.php b/src/Infrastructure/Plugins/PluginInterface.php
new file mode 100644
index 0000000000..a4ce2fc33a
--- /dev/null
+++ b/src/Infrastructure/Plugins/PluginInterface.php
@@ -0,0 +1,163 @@
+pluginsPath = realpath(ADMIDIO_PATH . FOLDER_PLUGINS);
+ }
+
+ /**
+ *
+ */
+ public function getAvailablePlugins() : array|object
+ {
+ $plugins = array();
+ foreach (scandir($this->pluginsPath) as $entry) {
+ // skip dot and dotdot entries
+ if ($entry === '.' || $entry === '..') {
+ continue;
+ }
+
+ $pluginFolder = $this->pluginsPath . DIRECTORY_SEPARATOR . $entry;
+ $className = null;
+ $pluginClassFile = null;
+
+ if (is_dir($pluginFolder)) {
+ // loop over all class files to find the main plugin class file in the classes folder
+ foreach (scandir($pluginFolder . DIRECTORY_SEPARATOR . 'classes') as $classFileEntry) {
+ if ($classFileEntry === '.' || $classFileEntry === '..' || !is_file($pluginFolder . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $classFileEntry)) {
+ continue;
+ }
+
+ $pluginClassFile = $pluginFolder . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $classFileEntry;
+ $className = is_file($pluginClassFile) ? $this->getClassNameFromFile($pluginClassFile) : null;
+
+ // The classname only contains the namespace of the plugin itself, so we need a way to find the class to get an instance of it.
+ // The problem is, that if the plugin isn't installed yet, we cannot use autoloading to find the class.
+ // Therefore, we check if the class exists first. If not, we include the file manually.
+ if ($className !== null && !class_exists($className)) {
+ include_once $pluginClassFile;
+
+ if (is_subclass_of($className, PluginAbstract::class)) {
+ break;
+ }
+ }
+ }
+
+ $instance = $className != null ? $className::getInstance() : null;
+
+ // find the main plugin file
+ $this->getMainPluginFile($pluginFolder, $entry, $instance);
+ $plugins[$entry] = array(
+ 'fullPath' => $this->pluginMainFile,
+ 'relativePath' => str_replace(realpath(ADMIDIO_PATH), '', $this->pluginMainFile),
+ 'interface' => $instance
+ );
+ }
+ }
+
+ return $plugins;
+ }
+
+ public function getPluginById(int $pluginId) : ?PluginAbstract
+ {
+ $plugins = $this->getAvailablePlugins();
+ foreach ($plugins as $plugin) {
+ $plugin['interface'] = $plugin['interface'] !== null ? $plugin['interface']::getInstance() : null;
+ if ($plugin['interface'] instanceof PluginAbstract && $plugin['interface']->getComponentId() === $pluginId) {
+ return $plugin['interface'];
+ }
+ }
+ return null;
+ }
+
+ public function getPluginByName(string $pluginName) : ?PluginAbstract
+ {
+ $plugins = $this->getAvailablePlugins();
+ if (isset($plugins[$pluginName])) {
+ $plugin = $plugins[$pluginName]['interface'] !== null ? $plugins[$pluginName]['interface']::getInstance() : null;
+ if ($plugin instanceof PluginAbstract) {
+ return $plugin;
+ }
+ }
+ return null;
+ }
+
+ public function getMetadataByComponentId(int $componentId) : ?array
+ {
+ $plugin = $this->getPluginById($componentId);
+ return $plugin ? $plugin->getMetadata() : null;
+ }
+
+ private function getMainPluginFile(string $pluginFolder, string $pluginName, ?PluginAbstract $instance) : void
+ {
+ $pluginFileName = $instance != null ? $instance->getMetadata()['mainFile'] : 'index.php';
+ $pluginFile = $pluginFolder . DIRECTORY_SEPARATOR . $pluginFileName;
+ if (is_file($pluginFile)) {
+ $this->pluginMainFile = $pluginFile;
+ } else {
+ $pluginFileName = $pluginName . '.php';
+ $pluginFile = $pluginFolder . DIRECTORY_SEPARATOR . $pluginFileName;
+ if (is_file($pluginFile)) {
+ $this->pluginMainFile = $pluginFile;
+ }
+ }
+ }
+
+ /**
+ * Parse a PHP file and return the first class name found.
+ */
+ private function getClassNameFromFile(string $file) : ?string
+ {
+ $src = file_get_contents($file);
+ $tokens = token_get_all($src);
+ $namespace = '';
+ $class = null;
+ for ($i = 0, $count = count($tokens); $i < $count; $i++) {
+ if ($tokens[$i][0] === T_NAMESPACE) {
+ $i++;
+ while ($tokens[$i][0] === T_WHITESPACE) $i++;
+ while (in_array($tokens[$i][0], [T_STRING, T_NAME_QUALIFIED, T_NS_SEPARATOR])) {
+ $namespace .= $tokens[$i++][1];
+ }
+ }
+ if ($tokens[$i][0] === T_CLASS) {
+ // skip whitespace
+ $i++;
+ while ($tokens[$i][0] === T_WHITESPACE) $i++;
+ $class = $tokens[$i][1];
+ break;
+ }
+ }
+ if ($class) {
+ return $namespace ? "$namespace\\$class" : $class;
+ }
+ return null;
+ }
+
+ /**
+ *
+ */
+ public function getInstalledPlugins() : array
+ {
+ $availablePlugins = $this->getAvailablePlugins();
+ $installedPlugins = array();
+ foreach ($availablePlugins as $plugin) {
+ $plugin['interface'] = $plugin['interface'] !== null ? $plugin['interface']::getInstance() : null;
+ if ($plugin['interface'] instanceof PluginAbstract && $plugin['interface']->isInstalled()) {
+ $installedPlugins[] = $plugin['interface'];
+ }
+ }
+ return $installedPlugins;
+ }
+
+ /**
+ * Get all plugins that are used on the overview page.
+ * @return array
+ */
+ public function getOverviewPlugins() : array
+ {
+ global $gValidLogin;
+
+ $availablePlugins = $this->getAvailablePlugins();
+ $overviewPlugins = array();
+ foreach ($availablePlugins as $plugin) {
+ $plugin['interface'] = $plugin['interface'] !== null ? $plugin['interface']::getInstance() : null;
+ if ($plugin['interface'] instanceof PluginAbstract && $plugin['interface']->isInstalled() && $plugin['interface']->isOverviewPlugin()) {
+ $configValues = $plugin['interface']->getPluginConfigValues();
+ $enabled = false;
+ foreach ($configValues as $key => $value) {
+ if (str_ends_with($key, '_plugin_enabled') && ($value === 1 || ($value === 2 && $gValidLogin))) {
+ $enabled = true;
+ break;
+ }
+ }
+ if (!$enabled) {
+ continue;
+ }
+
+ $templateRow = array(
+ 'id' => $plugin['interface']->getComponentId(),
+ 'name' => $plugin['interface']->getComponentName(),
+ 'file' => basename($plugin['relativePath']),
+ 'interface' => $plugin['interface']
+ );
+
+ // Get the sequence of the plugin
+ $sequence = $plugin['interface']->getPluginSequence();
+ $desiredSequence = $sequence;
+ if (isset($overviewPlugins[$desiredSequence])) {
+ $desiredSequence++;
+ while (isset($overviewPlugins[$desiredSequence])) {
+ $desiredSequence++;
+ }
+ }
+ $overviewPlugins[$desiredSequence] = $templateRow;
+ ksort($overviewPlugins);
+ }
+ }
+ return $overviewPlugins;
+ }
+
+ /**
+ * Get all active plugins.
+ * @return array
+ */
+ public function getActivePlugins() : array
+ {
+ $availablePlugins = $this->getAvailablePlugins();
+ $activePlugins = array();
+ // TODO: Check if the plugin is activated
+ // For now, we assume all installed plugins are active.
+ foreach ($availablePlugins as $plugin) {
+ $plugin['interface'] = $plugin['interface'] !== null ? $plugin['interface']::getInstance() : null;
+ if ($plugin['interface'] instanceof PluginAbstract && $plugin['interface']->isActivated()) {
+ $activePlugins[] = $plugin['interface'];
+ }
+ }
+ return $activePlugins;
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Plugins/Smarty.php b/src/Infrastructure/Plugins/Smarty.php
index 8e9b40092b..2455019051 100644
--- a/src/Infrastructure/Plugins/Smarty.php
+++ b/src/Infrastructure/Plugins/Smarty.php
@@ -200,4 +200,32 @@ public static function smarty_tag_getThemedFile(array $params, \Smarty\Template
return THEME_URL . $filepath;
}
+ /**
+ * Function for the Smarty template engine to compare two version strings.
+ * @param array $params Array with all the variables that are set within the template file.
+ * @param Template $template The Smarty template object that could be used within the function.
+ * @return bool Returns **true** if the first version is greater than or equal to the second version, otherwise **false**.
+ *
+ * **Code example**
+ * ```
+ * // example of this function within a template file
+ * {if {version_compare firstVersion='1.0.0' secondVersion='1.0.1'}}
+ * ...
+ * {else}
+ * ...
+ * {/if}
+ * ```
+ */
+ public static function versionCompare(array $params, Template $template): bool
+ {
+ if (empty($params['firstVersion']) || empty($params['secondVersion'])) {
+ throw new \UnexpectedValueException('Smarty function version_compare: missing "firstVersion" or "secondVersion" parameter');
+ }
+
+ if (empty($params['operator'])) {
+ $params['operator'] = '==';
+ }
+
+ return version_compare($params['firstVersion'], $params['secondVersion'], $params['operator']);
+ }
}
\ No newline at end of file
diff --git a/src/InstallationUpdate/Service/UpdateStepsCode.php b/src/InstallationUpdate/Service/UpdateStepsCode.php
index 96a3e9db9b..a8b12f93dc 100644
--- a/src/InstallationUpdate/Service/UpdateStepsCode.php
+++ b/src/InstallationUpdate/Service/UpdateStepsCode.php
@@ -2,6 +2,7 @@
namespace Admidio\InstallationUpdate\Service;
+use Admidio\Infrastructure\Plugins\PluginManager;
use Admidio\Categories\Entity\Category;
use Admidio\Documents\Entity\Folder;
use Admidio\Infrastructure\Utils\FileSystemUtils;
@@ -15,9 +16,11 @@
use Admidio\Infrastructure\Database;
use Admidio\Infrastructure\Entity\Text;
use DateTime;
+use PDOException;
use Ramsey\Uuid\Uuid;
use Admidio\Infrastructure\Exception;
use RuntimeException;
+use UnexpectedValueException;
// this must be declared for backwards compatibility. Can be removed if update scripts don't use it anymore
const TBL_DATES = TABLE_PREFIX . '_dates';
@@ -43,7 +46,110 @@ public static function setDatabase(Database $database)
self::$db = $database;
}
+ public static function updateStep51CheckFor3rdPartyPlugins(): void
+ {
+ global $gLogger, $gL10n;
+
+ $arrayOldOverviewPlugins = array('announcement-list', 'birthday', 'calendar', 'event-list', 'latest-documents-files', 'login_form', 'random_photo', 'who-is-online');
+ $oldFolderPluginPath = ADMIDIO_PATH . '/adm_plugins';
+ $gWarnOldPlugins = false;
+ $gWarn3rdPartyPlugins = false;
+ $gInfo3rdPartyPlugins = false;
+ $gWarnOldPluginsFolder = false;
+
+ // check if the old adm_plugins folder exists
+ if (!is_dir($oldFolderPluginPath)) {
+ return;
+ }
+
+ // get all folders inside the adm_plugins folder
+ $pluginFolders = FileSystemUtils::getDirectoryContent($oldFolderPluginPath, false, true, array(FileSystemUtils::CONTENT_TYPE_DIRECTORY));
+ foreach ($pluginFolders as $oldPluginPath => $type) {
+ $folderName = basename($oldPluginPath);
+ if (in_array($folderName, $arrayOldOverviewPlugins)) {
+ // the old plugin is no longer supported, so we remove it
+ try {
+ FileSystemUtils::deleteDirectoryIfExists($oldPluginPath, true);
+ } catch (Exception|RuntimeException|UnexpectedValueException $exception) {
+ // no rights to delete the old folder, then continue the update process
+ $gWarnOldPlugins = true;
+ continue;
+ }
+ } else {
+ // there is a 3rd party plugin installed, so we try to move it to the new plugin folder
+ $newPluginPath = ADMIDIO_PATH . FOLDER_PLUGINS . DIRECTORY_SEPARATOR . $folderName;
+ try {
+ FileSystemUtils::moveDirectory($oldPluginPath, $newPluginPath);
+ // now we need to check if there is a menu entry for this plugin and if yes we need to update the path by replacing adm_plugins with plugins
+ $sql = 'UPDATE ' . TBL_MENU . ' SET men_url = REPLACE(men_url, \'adm_plugins/' . $folderName . '\', \'' . DIRECTORY_SEPARATOR . FOLDER_PLUGINS . DIRECTORY_SEPARATOR . $folderName . '\') WHERE men_url LIKE \'%adm_plugins/' . $folderName . '%\' ';
+ self::$db->queryPrepared($sql);
+ $gInfo3rdPartyPlugins = true;
+ } catch (Exception|PDOException|RuntimeException|UnexpectedValueException $exception) {
+ // no rights to move the old folder, then continue the update process
+ $gWarn3rdPartyPlugins = true;
+ continue;
+ }
+ }
+ }
+
+ if ($gWarnOldPlugins) {
+ $gLogger->warning($gL10n->get('INS_WARNING_OLD_ADM_PLUGINS_COULD_NOT_BE_DELETED', array('adm_plugins', 'adm_plugins')));
+ }
+ if ($gWarn3rdPartyPlugins) {
+ $gLogger->warning($gL10n->get('INS_WARNING_3RD_PARRTY_PLUGINS_COULD_NOT_BE_MOVED', array('plugins', 'adm_plugins')));
+ }
+ if ($gInfo3rdPartyPlugins) {
+ $gLogger->info($gL10n->get('INS_INFO_3RD_PARRTY_PLUGINS_HAVE_BEEN_MOVED', array('plugins')));
+ }
+ if (!$gWarnOldPlugins && !$gWarn3rdPartyPlugins) {
+ // if nothing happened we can delete the old adm_plugins folder
+ try {
+ FileSystemUtils::deleteDirectoryIfExists($oldFolderPluginPath, false);
+ } catch (Exception|RuntimeException|UnexpectedValueException $exception) {
+ // no rights to delete the old folder, then continue the update process
+ // but warn the user that the folder could not be deleted
+ $gWarnOldPluginsFolder = true;
+ $gLogger->warning($gL10n->get('INS_WARNING_OLD_ADM_PLUGINS_FOLDER_COULD_NOT_BE_DELETED', array('adm_plugins', 'adm_plugins')));
+ }
+ }
+
+ // set a cookie to show the warnings/information after the update process
+ if ($gWarnOldPlugins || $gWarn3rdPartyPlugins || $gWarnOldPluginsFolder || $gInfo3rdPartyPlugins) {
+ $cookieValue = array(
+ 'warn_old_plugins' => $gWarnOldPlugins,
+ 'warn_3rd_party_plugins' => $gWarn3rdPartyPlugins,
+ 'warn_old_plugins_folder' => $gWarnOldPluginsFolder,
+ 'info_3rd_party_plugins' => $gInfo3rdPartyPlugins
+ );
+ setcookie('adm_update_plugins_warnings', json_encode($cookieValue), time() + 3600, '/');
+ }
+ }
+
+ public static function updateStep51InstallOverviewPlugins(): void
+ {
+ global $gDb;
+
+ // because we added the new column com_overview_plugin to the components table before, we need to reload the database columns
+ $gDb->initializeTableColumnProperties();
+
+ $pluginManager = new PluginManager();
+ $plugins = $pluginManager->getAvailablePlugins();
+
+ foreach ($plugins as $plugin) {
+ // check, if the plugin has an interface, if not, scip it
+ if (!isset($plugin['interface']) || $plugin['interface'] == null) {
+ continue;
+ }
+ // check if the plugin is an overview plugin, if so, install it
+ $instance = $plugin['interface']::getInstance();
+ if ($instance->isAdmidioPlugin()) {
+ // Install the overview plugin
+ $instance->doInstall();
+ }
+ }
+ }
+
/**
* This method will add a new profile field BlueSky to the database,
* but only if the category social networks exists
diff --git a/src/Preferences/Service/PreferencesService.php b/src/Preferences/Service/PreferencesService.php
index cd49ce1f63..107bbe52c1 100644
--- a/src/Preferences/Service/PreferencesService.php
+++ b/src/Preferences/Service/PreferencesService.php
@@ -8,6 +8,8 @@
use Admidio\Infrastructure\Utils\StringUtils;
use Admidio\Infrastructure\Entity\Text;
use Admidio\Infrastructure\Email;
+use Admidio\Infrastructure\Plugins\PluginManager;
+use Admidio\Infrastructure\Language;
/**
* @brief Class with methods to display the module pages.
@@ -21,6 +23,111 @@
*/
class PreferencesService
{
+ /**
+ * Registered presenter callbacks by component ID.
+ * @var array
+ */
+ private static array $pluginPresenters = array();
+ /**
+ * Registered presenter callbacks by component ID.
+ * @var array
+ */
+ private static array $overviewPluginPresenters = array();
+
+ /**
+ * Register a preferences presenter for a plugin.
+ *
+ * @param int $componentId The component ID of the plugin.
+ * @param callable $presenterCallback A callable that renders the plugin's preferences panel.
+ */
+ public static function addPluginPreferencesPresenter(int $componentId, callable $presenterCallback): void
+ {
+ if (!isset(self::$pluginPresenters[$componentId])) {
+ self::$pluginPresenters[$componentId] = array();
+ }
+ self::$pluginPresenters[$componentId][] = $presenterCallback;
+ }
+
+ /**
+ * Get all registered presenter callbacks, grouped by component ID.
+ *
+ * @return array
+ */
+ public static function getPluginPresenters(): array
+ {
+ return array_merge(self::$overviewPluginPresenters, self::$pluginPresenters);
+ }
+
+ /**
+ * Build the panel definitions for the "Plugins" tab.
+ *
+ * This method gathers metadata from each plugin and prepares
+ * the structure used by the PreferencesPresenter to render the accordion.
+ *
+ * @return array
+ */
+ public static function getPluginPanels(): array
+ {
+ $panels = array();
+
+ foreach (self::$pluginPresenters as $comId => $callbacks) {
+ // Retrieve plugin metadata by component ID (you may need to implement this lookup)
+ $pluginManager = new PluginManager();
+ $metadata = $pluginManager->getMetadataByComponentId($comId);
+
+ $panels[] = array(
+ 'id' => preg_replace('/\s+/', '_', preg_replace('/[^a-z0-9_ ]/', '', strtolower(Language::translateIfTranslationStrId($metadata['name'])))),
+ 'title' => Language::translateIfTranslationStrId($metadata['name']),
+ 'icon' => $metadata['icon'] ?? 'bi-puzzle',
+ 'subcards' => $metadata['hasSubcards'] ?? false,
+ );
+ }
+
+ return $panels;
+ }
+
+ /**
+ * Register a preferences presenter for a plugin.
+ *
+ * @param int $componentId The component ID of the plugin.
+ * @param callable $presenterCallback A callable that renders the plugin's preferences panel.
+ */
+ public static function addOverviewPluginPreferencesPresenter(int $componentId, callable $presenterCallback): void
+ {
+ if (!isset(self::$overviewPluginPresenters[$componentId])) {
+ self::$overviewPluginPresenters[$componentId] = array();
+ }
+ self::$overviewPluginPresenters[$componentId][] = $presenterCallback;
+ }
+
+ /**
+ * Build the panel definitions for the "Plugins" tab.
+ *
+ * This method gathers metadata from each plugin and prepares
+ * the structure used by the PreferencesPresenter to render the accordion.
+ *
+ * @return array
+ */
+ public static function getOverviewPluginPanels(): array
+ {
+ $panels = array();
+
+ foreach (self::$overviewPluginPresenters as $comId => $callbacks) {
+ // Retrieve plugin metadata by component ID (you may need to implement this lookup)
+ $pluginManager = new PluginManager();
+ $metadata = $pluginManager->getMetadataByComponentId($comId);
+
+ $panels[] = array(
+ 'id' => preg_replace('/\s+/', '_', preg_replace('/[^a-z0-9_ ]/', '', strtolower(Language::translateIfTranslationStrId($metadata['name'])))),
+ 'title' => Language::translateIfTranslationStrId($metadata['name']),
+ 'icon' => $metadata['icon'] ?? 'bi-puzzle',
+ 'subcards' => $metadata['hasSubcards'] ?? false,
+ );
+ }
+
+ return $panels;
+ }
+
/**
* Function to check an update
* @param string $currentVersion
@@ -290,6 +397,10 @@ public function save(string $panel, array $formData)
$sql = 'DELETE FROM ' . TBL_AUTO_LOGIN;
$gDb->queryPrepared($sql);
$gSettingsManager->set($key, $value);
+ } elseif (is_string($value) && str_starts_with($value, '["') && str_ends_with($value, '"]')) { // check if the value is a JSON array
+ // decode JSON array and save it as an array
+ $value = implode(',', json_decode($value, true));
+ $gSettingsManager->set($key, $value);
} else {
$gSettingsManager->set($key, $value);
}
diff --git a/src/Preferences/ValueObject/SettingsManager.php b/src/Preferences/ValueObject/SettingsManager.php
index 1f0ba32cb9..3e7eba489b 100644
--- a/src/Preferences/ValueObject/SettingsManager.php
+++ b/src/Preferences/ValueObject/SettingsManager.php
@@ -346,6 +346,13 @@ public function setMulti(array $settings, bool $update = true)
if (!self::isValidName($name)) {
throw new Exception('Settings name "' . $name . '" is an invalid string!');
}
+
+ // if array is given as value, convert to string
+ if (is_array($value)) {
+ $value = implode(',', $value);
+ $settings[$name] = $value;
+ }
+
if (!self::isValidValue($value) && $this->throwExceptions) {
throw new Exception('Settings value "' . $value . '" is an invalid value!');
}
diff --git a/src/UI/Presenter/FormPresenter.php b/src/UI/Presenter/FormPresenter.php
index b148d7e4bc..412b22bfe8 100644
--- a/src/UI/Presenter/FormPresenter.php
+++ b/src/UI/Presenter/FormPresenter.php
@@ -1923,6 +1923,101 @@ public function addToSmarty(Smarty $smarty): void
$smarty->assign('hasRequiredFields', ($this->flagRequiredFields && $this->showRequiredFields ? true : false));
}
+ /**
+ * This method appends the form attributes and all form elements to the Smarty object.
+ * It also assigns the template file of the form to the Smarty object.
+ * After this method is called the whole form could be rendered through the Smarty object.
+ *
+ * Important: This method creates an array of the form type, elements, attributes, javascript code and hasRequiredFields variables if they already exist in the Smarty object.
+ * If the variables do not exist, they will be assigned as normal.
+ * The variables urlAdmidio, l10n, and settings are only one time assigned to the Smarty object.
+ * This results in the following structure for the zero-based array variables:
+ * [variableName] => [
+ * 0 => first form element values,
+ * 1 => second form element values,
+ * 2 => ...,
+ * ]
+ *
+ *
+ * @param Smarty $smarty The Smarty object where the form data should be appended.
+ * @return void
+ */
+ public function appendToSmarty(Smarty $smarty): void
+ {
+ global $gL10n, $gSettingsManager;
+
+ if (!isset($smarty->tpl_vars['urlAdmidio'])) {
+ $smarty->assign('urlAdmidio', ADMIDIO_URL);
+ }
+ if (!isset($smarty->tpl_vars['l10n'])) {
+ $smarty->assign('l10n', $gL10n);
+ }
+ if (!isset($smarty->tpl_vars['settings'])) {
+ $smarty->assign('settings', $gSettingsManager);
+ }
+ if (!isset($smarty->tpl_vars['formType'])) {
+ $smarty->assign('formType', $this->type);
+ } else {
+ $curType = $smarty->getTemplateVars('formType');
+ if (is_array($curType) && array_key_first($curType) === 0) {
+ // if type is already an array then append the new type to the existing ones
+ $smarty->assign('formType', array_merge($curType, array($this->type)));
+ } else {
+ // if type is not an array then create a new array with the current and new type
+ $smarty->assign('formType', array($curType, $this->type));
+ }
+ }
+ if (!isset($smarty->tpl_vars['attributes'])) {
+ $smarty->assign('attributes', $this->attributes);
+ } else {
+ $curAttributes = $smarty->getTemplateVars('attributes');
+ if (is_array($curAttributes) && count(array_filter($curAttributes, 'is_array')) > 0 && array_key_first($curAttributes) === 0) {
+ // if attributes are already an array then append the new attributes to the existing ones
+ $smarty->assign('attributes', array_merge($curAttributes, array($this->attributes)));
+ } else {
+ // if attributes are not an array then create a new array with the current and new attributes
+ $smarty->assign('attributes', array($curAttributes, $this->attributes));
+ }
+ }
+ if (!isset($smarty->tpl_vars['elements'])) {
+ $smarty->assign('elements', $this->elements);
+ } else {
+ $curElements = $smarty->getTemplateVars('elements');
+ if (is_array($curElements) && count(array_filter($curElements, 'is_array')) > 0 && array_key_first($curElements) === 0) {
+ // if elements are already an array then append the new elements to the existing ones
+ $smarty->assign('elements', array_merge($curElements, array($this->elements)));
+ } else {
+ // if elements are not an array then create a new array with the current and new elements
+ $smarty->assign('elements', array($curElements, $this->elements));
+ }
+ }
+ if (!isset($smarty->tpl_vars['javascript'])) {
+ $smarty->assign('javascript', $this->javascript);
+ } else {
+ $curJavascript = $smarty->getTemplateVars('javascript');
+ if (is_array($curJavascript) && array_key_first($curJavascript) === 0) {
+ // if javascript is already an array then append the new javascript to the existing ones
+ $smarty->assign('javascript', array_merge($curJavascript, array($this->javascript)));
+ } else {
+ // if javascript is not an array then create a new array with the current and new javascript
+ $smarty->assign('javascript', array($curJavascript, $this->javascript));
+ }
+ }
+ if (!isset($smarty->tpl_vars['hasRequiredFields'])) {
+ $smarty->assign('hasRequiredFields', ($this->flagRequiredFields && $this->showRequiredFields ? true : false));
+ } else {
+ $curRequiredFields = $smarty->getTemplateVars('hasRequiredFields');
+ $newRequiredFields = ($this->flagRequiredFields && $this->showRequiredFields ? true : false);
+ if (is_array($curRequiredFields) && array_key_first($curRequiredFields) === 0) {
+ // if hasRequiredFields is already an array then append the new hasRequiredFields to the existing ones
+ $smarty->assign('hasRequiredFields', array_merge($curRequiredFields, array($newRequiredFields)));
+ } else {
+ // if hasRequiredFields is not an array then create a new array with the current and new hasRequiredFields
+ $smarty->assign('hasRequiredFields', array($curRequiredFields, $newRequiredFields));
+ }
+ }
+ }
+
/**
* This method returns the attributes array.
* @return array Returns all attributes of the form.
diff --git a/src/UI/Presenter/PagePresenter.php b/src/UI/Presenter/PagePresenter.php
index e1aa5c3848..183c4eff6b 100644
--- a/src/UI/Presenter/PagePresenter.php
+++ b/src/UI/Presenter/PagePresenter.php
@@ -413,6 +413,7 @@ public static function createSmartyObject(): Smarty
$smartyObject->registerPlugin('function', 'is_translation_string_id', 'Admidio\Infrastructure\Plugins\Smarty::isTranslationStringID');
$smartyObject->registerPlugin('function', 'load_admidio_plugin', 'Admidio\Infrastructure\Plugins\Smarty::loadAdmidioPlugin');
$smartyObject->registerPlugin('function', 'get_themed_file', 'Admidio\Infrastructure\Plugins\Smarty::smarty_tag_getThemedFile');
+ $smartyObject->registerPlugin('function', 'version_compare', 'Admidio\Infrastructure\Plugins\Smarty::versionCompare');
return $smartyObject;
} catch (\Smarty\Exception $e) {
throw new Exception($e->getMessage());
diff --git a/src/UI/Presenter/PluginsPresenter.php b/src/UI/Presenter/PluginsPresenter.php
new file mode 100644
index 0000000000..78d54361bf
--- /dev/null
+++ b/src/UI/Presenter/PluginsPresenter.php
@@ -0,0 +1,209 @@
+createEditForm();
+ * $page->show();
+ * ```
+ * @copyright The Admidio Team
+ * @see https://www.admidio.org/
+ * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2.0 only
+ */
+class PluginsPresenter extends PagePresenter
+{
+ protected array $templateData = array();
+
+ /**
+ * Create the list of plugins.
+ * @throws Exception
+ */
+ public function createList(): void
+ {
+ global $gL10n, $gCurrentSession;
+
+ $this->setHtmlID('adm_plugins');
+ $this->setHeadline($gL10n->get('SYS_PLUGIN_MANAGER'));
+ $this->setContentFullWidth();
+
+ $this->prepareData();
+
+ $this->addJavascript('
+ $(".admidio-open-close-caret").click(function() {
+ showHideBlock($(this));
+ });
+ ', true
+ );
+
+ $this->smarty->assign('list', $this->templateData);
+ $this->smarty->assign('l10n', $gL10n);
+ try {
+ $this->pageContent .= $this->smarty->fetch('modules/plugins.list.tpl');
+ } catch (\Smarty\Exception $e) {
+ throw new Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * Read all available forum topics from the database and create a Bootstrap card for each topic.
+ * @param int $offset Offset of the first record that should be returned.
+ * @throws Exception
+ * @throws \DateMalformedStringException
+ */
+ public function createCards(): void
+ {
+ global $gL10n;
+
+ $baseUrl = SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php', array('mode' => 'cards'));
+
+ $this->setHtmlID('adm_plugins');
+ $this->setHeadline($gL10n->get('SYS_PLUGIN_MANAGER'));
+
+ $this->prepareData();
+
+ $this->smarty->assign('cards', $this->templateData);
+ $this->smarty->assign('l10n', $gL10n);
+ $this->smarty->assign('pagination', admFuncGeneratePagination($baseUrl, count($this->templateData), 10, 0));
+
+ try {
+ $this->pageContent .= $this->smarty->fetch('modules/plugins.cards.tpl');
+ } catch (\Smarty\Exception $e) {
+ throw new Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * @param int $offset Offset of the first record that should be returned.
+ * @throws \DateMalformedStringException
+ * @throws Exception
+ */
+ public function prepareData(): void
+ {
+ global $gL10n;
+ $pluginManager = new PluginManager();
+ $plugins = $pluginManager->getAvailablePlugins();
+ $templateRowPluginParent['overview'] = array('id' => 'overview_plugins', 'name' => $gL10n->get('SYS_OVERVIEW_EXTENSIONS'), 'entries' => array());
+ $templateRowPluginParent['plugins'] = array('id' => 'plugins', 'name' => $gL10n->get('SYS_EXTENSIONS'), 'entries' => array());
+ $templateRowPluginParent['available'] = array('id' => 'plugins_available', 'name' => $gL10n->get('SYS_EXTENSIONS_AVAILABLE'), 'entries' => array());
+ foreach($plugins as $pluginName => $values) {
+ $templateRow = array();
+ $interface = $values['interface'] instanceof PluginAbstract ? $values['interface']::getInstance() : null;
+
+ if ($interface != null) {
+ $templateRow['id'] = ($interface->getComponentId() !== 0) ? $interface->getComponentId() : $pluginName;
+ $templateRow['name'] = Language::translateIfTranslationStrId($interface->getName());
+ $templateRow['description'] = Language::translateIfTranslationStrId($interface->getMetadata()['description'] ?? '');
+ $templateRow['icon'] = $interface->getMetadata()['icon'] ?? '';
+ $templateRow['url'] = $interface->getMetadata()['url'] ? '' . parse_url($interface->getMetadata()['url'])['host'] . '' : '';
+ $templateRow['author'] = $interface->getMetadata()['author'] ?? '';
+ $templateRow['version'] = $interface->getMetadata()['version'] ?? '';
+ $templateRow['installedVersion'] = $interface->getVersion() !== '0.0.0' ? $interface->getVersion() : '';
+
+ // add actions for the plugin
+ if ($interface->isInstalled()) {
+ // add showPreferences action
+ // if there is a custom defined preferences file in the metadata then use this file
+ if (isset($interface->getMetadata()['preferencesFile']) && !empty($interface->getMetadata()['preferencesFile'])) {
+ // check, if the file starts with a / or \\ indicating an absolute path, if so we don't need to add a directory separator
+ if (str_starts_with($interface->getMetadata()['preferencesFile'], '/') || str_starts_with($interface->getMetadata()['preferencesFile'], '\\')) {
+ $url = ADMIDIO_URL . FOLDER_PLUGINS . DIRECTORY_SEPARATOR . $interface->getComponentName() . $interface->getMetadata()['preferencesFile'];
+ } else {
+ $url = ADMIDIO_URL . FOLDER_PLUGINS . DIRECTORY_SEPARATOR . $interface->getComponentName() . DIRECTORY_SEPARATOR . $interface->getMetadata()['preferencesFile'];
+ }
+ $templateRow['actions'][] = array(
+ 'url' => $url,
+ 'icon' => 'bi bi-gear',
+ 'tooltip' => $gL10n->get('SYS_PLUGIN_PREFERENCES')
+ );
+ } else {
+ // else use the preferences panel based on the plugin name
+ $templateRow['actions'][] = array(
+ 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/preferences.php', array('panel' => preg_replace('/\s+/', '_', preg_replace('/[^a-z0-9_ ]/', '', strtolower(Language::translateIfTranslationStrId($interface->getName())))))),
+ 'icon' => 'bi bi-gear',
+ 'tooltip' => $gL10n->get('SYS_PLUGIN_PREFERENCES')
+ );
+ }
+
+ // add update action if an update is available
+ if ($interface->isUpdateAvailable()) {
+ $templateRow['actions'][] = array(
+ 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php', array('mode' => 'update', 'name' => $pluginName)),
+ 'icon' => 'bi bi-arrow-clockwise',
+ 'tooltip' => $gL10n->get('SYS_PLUGIN_UPDATE')
+ );
+ }
+ if (!$interface->isAdmidioPlugin()) {
+ // add uninstall action
+ $templateRow['actions'][] = array(
+ 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php', array('mode' => 'uninstall', 'name' => $pluginName)),
+ 'icon' => 'bi bi-trash',
+ 'tooltip' => $gL10n->get('SYS_PLUGIN_UNINSTALL')
+ );
+ }
+ } else {
+ // add install action
+ $templateRow['actions'][] = array(
+ 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/plugins.php', array('mode' => 'install', 'name' => $pluginName)),
+ 'icon' => 'bi bi-download',
+ 'tooltip' => $gL10n->get('SYS_PLUGIN_INSTALL')
+ );
+ }
+ } else {
+ // if the plugin does not implement the PluginInterface then we cannot show it
+ $templateRow['id'] = $pluginName;
+ $templateRow['name'] = $pluginName;
+ $templateRow['description'] = $gL10n->get('SYS_PLUGIN_NO_INTERFACE');
+ $templateRow['icon'] = '';
+ $templateRow['url'] = '';
+ $templateRow['author'] = '';
+ $templateRow['version'] = '';
+ $templateRow['installedVersion'] = '';
+ }
+
+ if ($interface !== null && $interface->isOverviewPlugin()) {
+ // add the plugin to the overview plugins
+ // for overview plugins here is a sequence number that is used to sort the plugins on the overview page
+ $sequence = $interface->getPluginSequence();
+ $desiredSequence = $sequence;
+ if (isset($templateRowPluginParent['overview']['entries'][$desiredSequence])) {
+ $desiredSequence++;
+ while (isset($templateRowPluginParent['overview']['entries'][$desiredSequence])) {
+ $desiredSequence++;
+ }
+ }
+ $templateRowPluginParent['overview']['entries'][$desiredSequence] = $templateRow;
+ ksort($templateRowPluginParent['overview']['entries']);
+ } elseif ($interface !== null && $interface->isInstalled()) {
+ // add the plugin to the normal plugins
+ $templateRowPluginParent['plugins']['entries'][] = $templateRow;
+ } else {
+ // add the plugin to the available plugins
+ $templateRowPluginParent['available']['entries'][] = $templateRow;
+ }
+ }
+
+ // remove empty categories
+ foreach ($templateRowPluginParent as $key => $value) {
+ if (empty($value['entries'])) {
+ unset($templateRowPluginParent[$key]);
+ }
+ }
+
+ $this->templateData = $templateRowPluginParent;
+ }
+}
\ No newline at end of file
diff --git a/src/UI/Presenter/PreferencesPresenter.php b/src/UI/Presenter/PreferencesPresenter.php
index f3b47ba4ac..17563b382d 100644
--- a/src/UI/Presenter/PreferencesPresenter.php
+++ b/src/UI/Presenter/PreferencesPresenter.php
@@ -5,6 +5,7 @@
use Admidio\Components\Entity\ComponentUpdate;
use Admidio\Infrastructure\Exception;
use Admidio\Infrastructure\Entity\Text;
+use Admidio\Infrastructure\Language;
use Admidio\Infrastructure\Utils\FileSystemUtils;
use Admidio\Infrastructure\Utils\PhpIniUtils;
use Admidio\Infrastructure\Utils\SecurityUtils;
@@ -12,6 +13,9 @@
use Admidio\Inventory\ValueObjects\ItemsData;
use Admidio\Preferences\Service\PreferencesService;
use Admidio\SSO\Service\KeyService;
+
+use Admidio\Infrastructure\Plugins\PluginManager;
+
use RuntimeException;
/**
@@ -67,12 +71,47 @@ public function __construct(string $panel = '')
parent::__construct();
}
+ public function __call(string $name, array $arguments)
+ {
+ // check if the method exists in the PreferencePresanter class
+ if (method_exists($this, $name)) {
+ return call_user_func_array([$this, $name], $arguments);
+ } else {
+ // Look through every plugin you registered during init()
+ foreach (PreferencesService::getPluginPresenters() as $comId => $callbacks) {
+ foreach ($callbacks as $callback) {
+ // We only stored array callbacks for static methods
+ if (is_array($callback)
+ && isset($callback[1])
+ && $callback[1] === $name
+ && is_callable($callback)
+ ) {
+ // forward all args (usually none) to the real presenter
+ $arguments = array_merge(array($this->getSmartyTemplate()), $arguments);
+ return call_user_func_array($callback, $arguments);
+ }
+ }
+ }
+ }
+
+ // If we get here, there was no presenter registered under that name:
+ throw new \BadMethodCallException(
+ "Call to undefined method " . static::class . "::$name()"
+ );
+ }
+
/**
* @throws Exception
*/
private function initialize(): void
{
global $gL10n;
+ $pluginManager = new PluginManager();
+ foreach ($pluginManager->getInstalledPlugins() as $pluginClass) {
+ $pluginClass::getInstance();
+ $pluginClass::initPreferencePanelCallback();
+ }
+
$this->preferenceTabs = array(
// === 1) System ===
array(
@@ -81,6 +120,7 @@ private function initialize(): void
'panels' => array(
array('id'=>'system_information', 'title'=>$gL10n->get('SYS_INFORMATIONS'), 'icon'=>'bi-info-circle-fill', 'subcards'=>true),
array('id'=>'common', 'title'=>$gL10n->get('SYS_COMMON'), 'icon'=>'bi-gear-fill', 'subcards'=>false),
+ array('id'=>'overview', 'title'=>$gL10n->get('SYS_OVERVIEW'), 'icon'=>'bi-house-door-fill', 'subcards'=>false),
array('id'=>'design', 'title'=>$gL10n->get('SYS_DESIGN'), 'icon'=>'bi-palette', 'subcards'=>false),
array('id'=>'regional_settings', 'title'=>$gL10n->get('ORG_REGIONAL_SETTINGS'), 'icon'=>'bi-globe2', 'subcards'=>false),
array('id'=>'changelog', 'title'=>$gL10n->get('SYS_CHANGE_HISTORY'), 'icon'=>'bi-clock-history', 'subcards'=>false),
@@ -96,7 +136,7 @@ private function initialize(): void
array('id'=>'registration', 'title'=>$gL10n->get('SYS_REGISTRATION'), 'icon'=>'bi-card-checklist', 'subcards'=>false),
array('id'=>'captcha', 'title'=>$gL10n->get('SYS_CAPTCHA'), 'icon'=>'bi-fonts', 'subcards'=>false),
array('id'=>'sso', 'title'=>$gL10n->get('SYS_SSO'), 'icon'=>'bi-key', 'subcards'=>false),
- ),
+ )
),
// === 3) User Management ===
@@ -108,7 +148,7 @@ private function initialize(): void
array('id'=>'profile', 'title'=>$gL10n->get('SYS_PROFILE'), 'icon'=>'bi-person-fill', 'subcards'=>false),
array('id'=>'groups_roles', 'title'=>$gL10n->get('SYS_GROUPS_ROLES'), 'icon'=>'bi-people-fill', 'subcards'=>false),
array('id'=>'category_report', 'title'=>$gL10n->get('SYS_CATEGORY_REPORT'), 'icon'=>'bi-list-stars', 'subcards'=>false),
- ),
+ )
),
// === 4) Communication ===
@@ -121,7 +161,7 @@ private function initialize(): void
array('id'=>'messages', 'title'=>$gL10n->get('SYS_MESSAGES'), 'icon'=>'bi-envelope-fill', 'subcards'=>false),
array('id'=>'announcements', 'title'=>$gL10n->get('SYS_ANNOUNCEMENTS'), 'icon'=>'bi-newspaper', 'subcards'=>false),
array('id'=>'forum', 'title'=>$gL10n->get('SYS_FORUM'), 'icon'=>'bi-chat-dots-fill', 'subcards'=>false),
- ),
+ )
),
// === 5) Contents ===
@@ -134,8 +174,40 @@ private function initialize(): void
array('id'=>'inventory', 'title'=>$gL10n->get('SYS_INVENTORY'), 'icon'=>'bi-box-seam-fill', 'subcards'=>false),
array('id'=>'photos', 'title'=>$gL10n->get('SYS_PHOTOS'), 'icon'=>'bi-image-fill', 'subcards'=>false),
array('id'=>'links', 'title'=>$gL10n->get('SYS_WEBLINKS'), 'icon'=>'bi-link-45deg', 'subcards'=>false),
- ),
+ )
),
+
+ // === 6) Overview Extensions ===
+ array(
+ 'key' => 'overview_extensions',
+ 'label' => $gL10n->get('SYS_OVERVIEW_EXTENSIONS'),
+ // load in all plugin panels that are registered in the PreferencesService
+ 'panels' => array_map(
+ fn(array $entry) => [
+ 'id' => $entry['id'],
+ 'title' => $entry['title'],
+ 'icon' => $entry['icon'] ?? 'bi-puzzle',
+ 'subcards' => $entry['subcards'] ?? false,
+ ],
+ \Admidio\Preferences\Service\PreferencesService::getOverviewPluginPanels()
+ )
+ ),
+
+ // === 7) Extensions ===
+ array(
+ 'key' => 'extensions',
+ 'label' => $gL10n->get('SYS_EXTENSIONS'),
+ // load in all plugin panels that are registered in the PreferencesService
+ 'panels' => array_map(
+ fn(array $entry) => [
+ 'id' => $entry['id'],
+ 'title' => $entry['title'],
+ 'icon' => $entry['icon'] ?? 'bi-puzzle',
+ 'subcards' => $entry['subcards'] ?? false,
+ ],
+ \Admidio\Preferences\Service\PreferencesService::getPluginPanels()
+ )
+ )
);
}
@@ -558,6 +630,80 @@ public function createCommonForm(): string
return $smarty->fetch('preferences/preferences.common.tpl');
}
+ /**
+ * Generates the HTML of the form from the plugin overview preferences and will return the complete HTML.
+ * @return string Returns the complete HTML of the form from the contact preferences.
+ * @throws Exception
+ * @throws \Smarty\Exception
+ */
+ public function createOverviewForm(): string
+ {
+ global $gL10n, $gCurrentSession;
+
+ // get all overview plugins and add them to the template
+ $pluginManager = new PluginManager();
+ $plugins = $pluginManager->getOverviewPlugins();
+
+ $overviewPlugins = array();
+ foreach ($plugins as $sequence => $plugin) {
+ $pluginInstance = $plugin['interface']::getInstance();
+ $pluginConfig = $pluginInstance->getPluginConfig();
+
+ // find the plugin sequence key
+ $sequenceKey = '';
+ $enabled = false;
+ foreach ($pluginConfig as $pluginConfigKey => $pluginConfigValue) {
+ if (str_ends_with($pluginConfigKey, '_overview_sequence')) {
+ $sequenceKey = $pluginConfigKey;
+ } elseif (str_ends_with($pluginConfigKey, '_enabled')) {
+ $enabled = !(($pluginConfigValue['value'] === 0));
+ }
+ }
+ $overviewPlugins[] = array(
+ 'id' => $plugin['id'],
+ 'name' => Language::translateIfTranslationStrId($pluginInstance->getName()),
+ 'icon' => $pluginInstance->getIcon(),
+ 'enabled' => $enabled,
+ 'sequence' => array('key' => $sequenceKey, 'value' => $sequence)
+ );
+ }
+
+ $formOverview = new FormPresenter(
+ 'adm_preferences_form_overview',
+ 'preferences/preferences.overview.tpl',
+ SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/preferences.php', array('mode' => 'save', 'panel' => 'overview')),
+ null,
+ array('class' => 'form-preferences')
+ );
+
+ $formOverview->addDescription(
+ 'adm_overview_description',
+ $gL10n->get('SYS_OVERVIEW_EXTENSIONS_SEQUENCE_DESC')
+ );
+
+ // add hidden inputs for the plugin sequence
+ foreach ($overviewPlugins as $overviewPlugin) {
+ $formOverview->addInput(
+ $overviewPlugin['sequence']['key'],
+ '',
+ $overviewPlugin['sequence']['value'],
+ array('type' => 'number', 'property' => FormPresenter::FIELD_HIDDEN)
+ );
+ }
+
+ $formOverview->addSubmitButton(
+ 'adm_button_save_overview',
+ $gL10n->get('SYS_SAVE'),
+ array('icon' => 'bi-check-lg', 'class' => 'offset-sm-3')
+ );
+
+ $smarty = $this->getSmartyTemplate();
+ $smarty->assign('overviewPlugins', $overviewPlugins);
+ $formOverview->addToSmarty($smarty);
+ $gCurrentSession->addFormObject($formOverview);
+ return $smarty->fetch('preferences/preferences.overview.tpl');
+ }
+
/**
* Generates the HTML of the form from the contact preferences and will return the complete HTML.
* @return string Returns the complete HTML of the form from the contact preferences.
@@ -2723,8 +2869,8 @@ function initializePanelInteractions(panelId) {
// define additional ids that should also be considered for visibility toggling
var additionalIds = [\'#system_notifications_enabled\'];
- // Look for any input whose id ends with "_module_enabled"
- var selectors = ["[id$=\'_module_enabled\']"].concat(additionalIds);
+ // Look for any input whose id ends with "_module_enabled" or "_plugin_enabled"
+ var selectors = ["[id$=\'_module_enabled\']", "[id$=\'_plugin_enabled\']"].concat(additionalIds);
var moduleEnabledField = panelContainer.find(selectors.join(", ")).filter(":visible");
if (moduleEnabledField.length > 0) {
@@ -2824,6 +2970,11 @@ function initializePanelInteractions(panelId) {
$this->addCssFile(ADMIDIO_URL . FOLDER_LIBS . '/bootstrap-tabs-x/css/bootstrap-tabs-x-admidio.css');
$this->addJavascriptFile(ADMIDIO_URL . FOLDER_LIBS . '/bootstrap-tabs-x/js/bootstrap-tabs-x-admidio.js');
+ // remove the plugins array from preferenceTabs if there are no panels defined
+ $this->preferenceTabs = array_filter($this->preferenceTabs, function ($tab) {
+ return !empty($tab['panels']);
+ });
+
$this->assignSmartyVariable('preferenceTabs', $this->preferenceTabs);
$this->addTemplateFile('preferences/preferences.tpl');
diff --git a/system/bootstrap/constants.php b/system/bootstrap/constants.php
index ee4dbd5170..45e3ed931f 100755
--- a/system/bootstrap/constants.php
+++ b/system/bootstrap/constants.php
@@ -79,7 +79,7 @@
const FOLDER_LANGUAGES = '/languages';
const FOLDER_THEMES = '/themes';
const FOLDER_MODULES = '/modules';
-const FOLDER_PLUGINS = '/adm_plugins';
+const FOLDER_PLUGINS = '/plugins';
// ####################
// ### DATE-STUFF ###
diff --git a/system/js/common_functions.js b/system/js/common_functions.js
index 6bf3dd781a..6d0eccc664 100644
--- a/system/js/common_functions.js
+++ b/system/js/common_functions.js
@@ -423,7 +423,7 @@ function updateMoveActions($scope, rowIdPrefix, moveActionClass) {
$($scope).each(function() {
// If the scope is ".card-body", we search for divs with IDs starting with rowIdPrefix.
// Otherwise, we search for table rows with IDs starting with rowIdPrefix.
- if ($scope === ".card-body") {
+ if ($scope === ".card-body" || $scope === ".accordion") {
var $rows = $(this).find("div[id^=" + rowIdPrefix + "]").has("." + moveActionClass).filter(function() {
return $(this).css("display") !== "none";
});
diff --git a/themes/simple/templates/modules/plugins.list.tpl b/themes/simple/templates/modules/plugins.list.tpl
new file mode 100644
index 0000000000..6874d41b2b
--- /dev/null
+++ b/themes/simple/templates/modules/plugins.list.tpl
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+ | {$l10n->get('SYS_NAME')} |
+ {$l10n->get('SYS_DESCRIPTION')} |
+ {$l10n->get('SYS_AUTHOR')} |
+ {$l10n->get('SYS_WEBSITE')} |
+ {$l10n->get('SYS_PLUGIN_VERSION')} |
+ {$l10n->get('SYS_INSTALLED_VERSION')} |
+ | {* actions *}
+
+
+ {foreach $list as $pluginNode}
+
+
+ |
+
+
+ {$pluginNode.name}
+ |
+
+
+ {if isset($pluginNode.entries)}
+
+ {foreach $pluginNode.entries as $pluginEntry}
+
+ | {if isset($pluginEntry.icon)}{/if} {$pluginEntry.name} |
+ {$pluginEntry.description} |
+ {$pluginEntry.author} |
+ {$pluginEntry.url} |
+ {$pluginEntry.version} |
+ {if $pluginEntry.installedVersion === ''}{$l10n->get('SYS_NOT_INSTALLED')}{else}{$pluginEntry.installedVersion}{/if} |
+
+ {include 'sys-template-parts/list.functions.tpl' data=$pluginEntry}
+ |
+
+ {/foreach}
+
+ {/if}
+ {/foreach}
+
+
+
+
+
+
+
+ {foreach $list as $pluginNode name=outer}
+
+
+
+
+ {foreach $pluginNode.entries as $pluginEntry}
+
+
+
+
+
{$l10n->get('SYS_DESCRIPTION')}:
+
{$pluginEntry.description}
+
+
+
{$l10n->get('SYS_AUTHOR')}:
+
{$pluginEntry.author}
+
+
+
{$l10n->get('SYS_PLUGIN_VERSION')}:
+
{$pluginEntry.version}
+
+
+
{$l10n->get('SYS_INSTALLED_VERSION')}:
+ {if $pluginEntry.installedVersion === ''}
+
{$l10n->get('SYS_NOT_INSTALLED')}
+ {else if {version_compare firstVersion=$pluginEntry.installedVersion secondVersion=$pluginEntry.version operator='<'} }
+
{$pluginEntry.installedVersion} ({$l10n->get('SYS_UPDATE_AVAILABLE')})
+ {else}
+
{$pluginEntry.installedVersion} ({$l10n->get('SYS_UP_TO_DATE')})
+ {/if}
+
+
+
+ {/foreach}
+
+
+
+ {/foreach}
+
+
\ No newline at end of file
diff --git a/themes/simple/templates/preferences/preferences.overview.tpl b/themes/simple/templates/preferences/preferences.overview.tpl
new file mode 100644
index 0000000000..63c3bf96ec
--- /dev/null
+++ b/themes/simple/templates/preferences/preferences.overview.tpl
@@ -0,0 +1,187 @@
+
+
+
+
\ No newline at end of file
diff --git a/themes/simple/templates/system/overview.tpl b/themes/simple/templates/system/overview.tpl
index be5b6a0cfd..fd18c5af9e 100644
--- a/themes/simple/templates/system/overview.tpl
+++ b/themes/simple/templates/system/overview.tpl
@@ -1,59 +1,12 @@