4 * Copyright © 2003-2024 The Galette Team
6 * This file is part of Galette (https://galette.eu).
8 * Galette is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * Galette is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
22 namespace Galette\Core
;
25 use Galette\Entity\Adherent
;
26 use Galette\Entity\Document
;
27 use Galette\Util\Release
;
31 * Galette application instance
33 * @author Johan Cwiklinski <johan@x-tnd.be>
37 public const MODE_PROD
= 'PROD';
38 public const MODE_DEV
= 'DEV';
39 public const MODE_MAINT
= 'MAINT';
40 public const MODE_DEMO
= 'DEMO';
43 * Retrieve Galette version from git, if present.
45 * @param boolean $time Include time and timezone. Defaults to false.
49 public static function gitVersion(bool $time = false): string
51 $galette_version = GALETTE_VERSION
;
53 //used for both git and nightly installs
54 $version = str_replace('-dev', '-git', GALETTE_VERSION
);
55 if (strstr($version, '-git') === false) {
59 if (is_dir(GALETTE_ROOT
. '../.git')) {
60 $commitHash = trim(exec('git log --pretty="%h" -n1 HEAD'));
62 $commitDate = new \
DateTime(trim(exec('git log -n1 --pretty=%ci HEAD')));
64 $galette_version = sprintf(
68 $commitDate->format(($time ?
'Y-m-d H:i:s T' : 'Y-m-d'))
70 } elseif (static::isNightly()) {
71 $galette_version = $version . '-' . GALETTE_NIGHTLY
;
73 return $galette_version;
77 * Get Galette new release
79 * @return array<string, string|array<string,mixed>>
81 public static function getNewRelease(): array
83 $release = new Release();
85 'new' => $release->checkNewRelease(),
86 'version' => $release->getLatestRelease()
93 * @return array<string, string|array<string,mixed>>
95 public static function getAllMenus(): array
97 return static::getMenus(true);
103 * @param bool $public Include public menus. Defaults to false
105 * @return array<string, string|array<string,mixed>>
107 public static function getMenus(bool $public = false): array
111 * @var Preferences $preferences
112 * @var Plugins $plugins
114 global $login, $preferences, $plugins;
118 if ($login->isLogged()) {
119 if (!$login->isSuperAdmin()) {
121 $menus['myaccount'] = [
122 'title' => _T("My Account"),
126 'label' => _T('My contributions'),
127 'title' => _T('View and filter all my contributions'),
129 'name' => 'myContributions',
130 'args' => ['type' => 'contributions']
134 'label' => _T('My transactions'),
135 'title' => _T('View and filter all my transactions'),
137 'name' => 'myContributions',
138 'args' => ['type' => 'transactions']
142 'label' => _T('My information'),
143 'title' => _T('View my member card'),
152 if ($preferences->pref_bool_create_member
) {
153 $menus['myaccount']['items'][] = [
154 'label' => _T('Add a child member'),
155 'title' => _T('Add new child member in database'),
157 'name' => 'addMemberChild',
164 $menus['members'] = [
165 'title' => _T("Members"),
170 if ($login->isAdmin() ||
$login->isStaff() ||
$login->isGroupManager()) {
171 $menus['members']['items'] = [
173 'label' => _T("List of members"),
174 'title' => _T("View, search into and filter member's list"),
177 'aliases' => ['editMember', 'member']
181 'label' => _T("Advanced search"),
182 'title' => _T("Perform advanced search into members list"),
184 'name' => 'advanced-search'
188 'label' => _T("Saved searches"),
199 ||
($login->isGroupManager() && $preferences->pref_bool_groupsmanagers_create_member
)
201 $menus['members']['items'][] = [
202 'label' => _T("Add a member"),
203 'title' => _T("Add new member in database"),
205 'name' => 'addMember'
210 if ($login->isAdmin() ||
$login->isStaff()) {
211 $menus['contributions'] = [
212 'title' => _T('Contributions'),
216 'label' => _T("List of contributions"),
217 'title' => _T("View and filter contributions"),
219 'name' => 'contributions',
220 'args' => ['type' => 'contributions'],
221 'aliases' => ['editContribution']
225 'label' => _T("List of transactions"),
226 'title' => _T("View and filter transactions"),
228 'name' => 'contributions',
229 'args' => ['type' => 'transactions'],
230 'aliases' => ['editTransaction']
234 'label' => _T("Add a membership fee"),
235 'title' => _T("Add new membership fee in database"),
237 'name' => 'addContribution',
238 'args' => ['type' => \Galette\Entity\Contribution
::TYPE_FEE
]
242 'label' => _T("Add a donation"),
243 'title' => _T("Add new donation in database"),
245 'name' => 'addContribution',
246 'args' => ['type' => \Galette\Entity\Contribution
::TYPE_DONATION
]
250 'label' => _T("Add a transaction"),
251 'title' => _T("Add new transaction in database"),
253 'name' => 'addTransaction'
257 'label' => _T("Reminders"),
258 'title' => _T("Send reminders to late members"),
260 'name' => 'reminders'
267 if ($login->isAdmin() ||
$login->isStaff() ||
$login->isGroupManager()) {
268 $menus['management'] = [
269 'title' => _T("Management"),
270 'icon' => 'dharmachakra',
273 'label' => _T("Manage groups"),
274 'title' => _T("View and manage groups"),
282 if ($login->isAdmin() ||
$login->isStaff()) {
283 $menus['management']['items'] = array_merge($menus['management']['items'], [
285 'label' => _T("Logs"),
286 'title' => _T("View application's logs"),
292 'label' => _T("Manage mailings"),
293 'title' => _T("Manage mailings that has been sent"),
299 'label' => _T("Exports"),
300 'title' => _T("Export some data in various formats"),
306 'label' => _T("Imports"),
307 'title' => _T("Import members from CSV files"),
310 'aliases' => ['importModel']
314 'label' => _T("Charts"),
315 'title' => _T("Various charts"),
320 'label' => _T("Documents"),
321 'title' => _T("Add documents to share related to your association (status, rules of procedure, ...)"),
323 'name' => 'documentsList',
324 'aliases' => ['editDocument', 'addDocument']
330 if ($login->isAdmin()) {
331 $menus['configuration'] = [
332 'title' => _T("Configuration"),
336 'label' => _T("Settings"),
337 'title' => _T("Set applications preferences (address, website, member's cards configuration, ...)"),
339 'name' => 'preferences'
343 'label' => _T("Plugins"),
344 'title' => _T("Information about available plugins"),
350 'label' => _T("Core lists"),
351 'title' => _T("Customize lists fields and order"),
353 'name' => 'configureListFields',
354 'args' => ['table' => 'adherents']
358 'label' => _T("Core fields"),
359 'title' => _T("Customize fields order, set which are required, and for who they're visibles"),
361 'name' => 'configureCoreFields'
365 'label' => _T("Dynamic fields"),
366 'title' => _T("Manage additional fields for various forms"),
368 'name' => 'configureDynamicFields',
369 'aliases' => ['editDynamicField'],
373 'label' => _T("Translate labels"),
374 'title' => _T("Translate additional fields labels"),
376 'name' => 'dynamicTranslations'
380 'label' => _T("Manage statuses"),
383 'aliases' => ['editStatus'],
384 'sub_select' => false
388 'label' => _T("Contributions types"),
389 'title' => _T("Manage contributions types"),
391 'name' => 'contributionsTypes',
395 'label' => _T("Emails content"),
396 'title' => _T("Manage emails texts and subjects"),
402 'label' => _T("Titles"),
403 'title' => _T("Manage titles"),
406 'aliases' => ['editTitle']
410 'label' => _T("PDF models"),
411 'title' => _T("Manage PDF models"),
413 'name' => 'pdfModels'
417 'label' => _T("Payment types"),
418 'title' => _T("Manage payment types"),
420 'name' => 'paymentTypes',
421 'aliases' => ['editPaymentType']
425 'label' => _T("Empty adhesion form"),
426 'title' => _T("Download empty adhesion form"),
428 'name' => 'emptyAdhesionForm'
434 if ($login->isSuperAdmin()) {
435 $menus['configuration']['items'][] = [
436 'label' => _T("Admin tools"),
437 'title' => _T("Various administrative tools"),
439 'name' => 'adminTools'
447 foreach (array_keys($plugins->getModules()) as $module_id) {
448 //get plugins menus entries
449 $plugin_class = $plugins->getClassName($module_id, true);
450 if (class_exists($plugin_class)) {
451 $plugin = new $plugin_class();
452 $menus = array_merge_recursive(
460 $menus +
= static::getPublicMenus();
463 //cleanup empty entries (no items)
464 foreach ($menus as $key => $menu) {
465 if (!count($menu['items'])) {
476 * @return array<string, string|array<string,mixed>>
478 public static function getPublicMenus(): array
481 * @var Preferences $preferences
483 * @var Plugins $plugins
485 global $preferences, $login, $plugins, $zdb;
488 if ($preferences->showPublicPages($login)) {
490 'title' => _T("Public pages"),
491 'icon' => 'eye outline',
494 'label' => _T("Members list"),
496 'name' => 'publicList',
497 'args' => ['type' => 'list']
499 'icon' => 'address book'
502 'label' => _T("Trombinoscope"),
504 'name' => 'publicList',
505 'args' => ['type' => 'trombi']
507 'icon' => 'user friends'
512 //display documents menu if at least one document is present with current ACLs
513 $document = new Document($zdb);
514 $documents = $document->getList();
515 if ($login->isSuperAdmin() ||
count($documents)) {
516 $menus['public']['items'][] = [
517 'label' => _T("Documents"),
518 'title' => _T("View documents related to your association"),
520 'name' => 'documentsPublicList'
522 'icon' => 'file alternate'
526 foreach (array_keys($plugins->getModules()) as $module_id) {
527 //get plugins public menus entries
528 $plugin_class = $plugins->getClassName($module_id, true);
529 if (class_exists($plugin_class)) {
530 $plugin = new $plugin_class();
531 $menus['public']['items'] = array_merge(
532 $menus['public']['items'],
533 $plugin->getPublicMenuItems()
545 * @return array<string, string|array<string,mixed>>
547 public static function getDashboards(): array
551 * @var Plugins $plugins
554 global $login, $plugins, $zdb;
558 if ($login->isAdmin() ||
$login->isStaff() ||
$login->isGroupManager()) {
559 $dashboards = array_merge(
563 'label' => _T("Members"),
564 'title' => _T("View, search into and filter member's list"),
571 'label' => _T("Groups"),
572 'title' => _T("View and manage groups"),
576 'icon' => 'busts_in_silhouette'
582 if ($login->isAdmin() ||
$login->isStaff()) {
583 $dashboards = array_merge(
587 'label' => _T("Mailings"),
588 'title' => _T("Manage mailings that has been sent"),
595 'label' => _T("Contributions"),
596 'title' => _T("View and filter contributions"),
598 'name' => 'contributions',
599 'args' => ['type' => 'contributions']
604 'label' => _T("Transactions"),
605 'title' => _T("View and filter transactions"),
607 'name' => 'contributions',
608 'args' => ['type' => 'transactions']
613 'label' => _T("Reminders"),
614 'title' => _T("Send reminders to late members"),
616 'name' => 'reminders'
624 //display documents menu if at least one document is present with current ACLs
625 $document = new Document($zdb);
626 $documents = $document->getList();
627 if ($login->isSuperAdmin() ||
count($documents)) {
628 $dashboards = array_merge(
632 'label' => _T("Documents"),
633 'title' => _T("View documents related to your association"),
635 'name' => 'documentsPublicList'
643 if ($login->isAdmin()) {
644 $dashboards = array_merge(
648 'label' => _T("Settings"),
649 'title' => _T("Set applications preferences (address, website, member's cards configuration, ...)"),
651 'name' => 'preferences'
653 'icon' => 'control_knobs'
656 'label' => _T("Plugins"),
657 'title' => _T("Information about available plugins"),
667 if ($login->isLogged() && !$login->isSuperAdmin()) {
669 $dashboards = array_merge(
673 'label' => _T("My information"),
674 'title' => _T("View my member card"),
678 'icon' => 'bust_in_silhouette'
681 'label' => _T("My contributions"),
682 'title' => _T("View and filter all my contributions"),
684 'name' => 'myContributions',
685 'args' => ['type' => 'contributions']
690 'label' => _T("My transactions"),
691 'title' => _T("View and filter all my transactions"),
693 'name' => 'myContributions',
694 'args' => ['type' => 'transactions']
702 foreach (array_keys($plugins->getModules()) as $module_id) {
703 //get plugins menus entries
704 $plugin_class = $plugins->getClassName($module_id, true);
705 if (class_exists($plugin_class)) {
706 /** @var GalettePlugin $plugin */
707 $plugin = new $plugin_class();
708 $dashboards = array_merge_recursive(
710 $plugin->getDashboards()
719 * Get members list actions
721 * @param Adherent $member Current member
723 * @return array<string, string|array<string,mixed>>
725 public static function getListActions(Adherent
$member): array
729 * @var Plugins $plugins
731 global $login, $plugins;
735 if ($member->canEdit($login)) {
737 'label' => str_replace(
740 _T("%membername: edit information")
742 'title' => str_replace(
745 _T("%membername: edit information")
748 'name' => 'editMember',
749 'args' => ['id' => $member->id
]
751 'icon' => 'user edit'
755 if ($login->isAdmin() ||
$login->isStaff()) {
756 $actions = array_merge($actions, [
758 'label' => str_replace(
761 _T("%membername: contributions")
763 'title' => str_replace(
766 _T("%membername: contributions")
769 'name' => 'contributions',
771 "type" => "contributions",
772 "option" => "member",
773 'value' => $member->id
776 'icon' => 'receipt green'
779 'label' => str_replace(
782 _T("%membername: remove from database")
784 'title' => str_replace(
787 _T("%membername: remove from database")
790 'name' => 'removeMember',
795 'icon' => 'user times red',
796 'extra_class' => 'delete'
801 if ($login->isSuperAdmin()) {
803 'label' => str_replace(
806 _T("Log in in as %membername")
808 'title' => str_replace(
811 _T("Log in in as %membername")
814 'name' => 'impersonate',
819 'icon' => 'user secret grey'
823 foreach (array_keys($plugins->getModules()) as $module_id) {
824 //get plugins menus entries
825 $plugin_class = $plugins->getClassName($module_id, true);
826 if (class_exists($plugin_class)) {
827 /** @var GalettePlugin $plugin */
828 $plugin = new $plugin_class();
829 $actions = array_merge_recursive(
831 $plugin->getListActions($member)
839 * Get member show actions
841 * @param Adherent $member Current member
843 * @return array<string, string|array<string,mixed>>
845 public static function getDetailedActions(Adherent
$member): array
849 * @var Plugins $plugins
851 global $login, $plugins;
855 //TODO: add core detailed actions
857 foreach (array_keys($plugins->getModules()) as $module_id) {
858 //get plugins menus entries
859 $plugin_class = $plugins->getClassName($module_id, true);
860 if (class_exists($plugin_class)) {
861 /** @var GalettePlugin $plugin */
862 $plugin = new $plugin_class();
863 $actions = array_merge_recursive(
865 $plugin->getDetailedActions($member)
873 * Get members list batch actions
875 * @return array<string,array<string, string>>
877 public static function getBatchActions(): array
881 * @var Plugins $plugins
882 * @var Preferences $preferences
884 global $login, $plugins, $preferences;
892 $actions = array_merge(
896 'name' => 'masschange',
897 'label' => _T('Mass change'),
898 'icon' => 'user edit blue'
901 'name' => 'masscontributions',
902 'label' => _T('Mass add contributions'),
903 'icon' => 'receipt bite green'
907 'label' => _T('Delete'),
908 'icon' => 'user times red'
917 ||
$login->isGroupManager()
918 && $preferences->pref_bool_groupsmanagers_mailings
)
919 && $preferences->pref_mail_method
!= \Galette\Core\GaletteMail
::METHOD_DISABLED
922 'name' => 'sendmail',
923 'label' => _T('Mail'),
924 'icon' => 'mail bulk'
929 $login->isGroupManager()
930 && $preferences->pref_bool_groupsmanagers_exports
934 $actions = array_merge(
938 'name' => 'attendance_sheet',
939 'label' => _T('Attendance sheet'),
940 'icon' => 'file alternate'
943 'name' => 'labels__directdownload',
944 'label' => _T('Generate labels'),
945 'icon' => 'address card'
948 'name' => 'cards__directdownload',
949 'label' => _T('Generate Member Cards'),
953 'name' => 'csv__directdownload',
954 'label' => _T('Export as CSV'),
961 foreach (array_keys($plugins->getModules()) as $module_id) {
962 //get plugins menus entries
963 $plugin_class = $plugins->getClassName($module_id, true);
964 if (class_exists($plugin_class)) {
965 /** @var GalettePlugin $plugin */
966 $plugin = new $plugin_class();
967 $actions = array_merge_recursive(
969 $plugin->getBatchActions()
977 * Is demonstration mode enabled
981 public static function isDemo(): bool
983 return GALETTE_MODE
=== static::MODE_DEMO
;
987 * Is debug mode enabled
991 public static function isDebugEnabled(): bool
993 if (GALETTE_MODE
=== static::MODE_DEV
) {
994 //since 1.1.0, GALETTE_MODE with DEV value is deprecated.
996 'Using GALETTE_MODE set to DEV is deprecated. Use GALETTE_DEBUG.',
1001 // @const bool GALETTE_DEBUG
1002 return GALETTE_DEBUG
=== true;
1006 * Is SQL debug mode enabled
1010 public static function isSqlDebugEnabled(): bool
1012 return defined('GALETTE_SQL_DEBUG') ||
static::isDebugEnabled();
1016 * Is a nightly build
1020 public static function isNightly(): bool
1022 return GALETTE_NIGHTLY
!== false;
1026 * Check if a string is serialized
1028 * @param string $string String to check
1032 public static function isSerialized(string $string): bool
1034 return (@unserialize
($string) !== false);
1038 * JSON decode with exception
1040 * @param string $string JSON encoded string to decode
1042 * @return array<string|int, mixed>
1043 * @throws RuntimeException
1045 public static function jsonDecode(string $string): array
1047 $decoded = json_decode($string, true);
1048 if (json_last_error() !== JSON_ERROR_NONE
) {
1049 throw new RuntimeException('JSON decode error: ' . json_last_error_msg());
1056 * JSON encode with exception
1058 * @param array<string|int, mixed>|object $data Data to encode
1061 * @throws RuntimeException
1063 public static function jsonEncode(array|
object $data): string
1065 $encoded = json_encode($data);
1066 if (json_last_error() !== JSON_ERROR_NONE
) {
1067 throw new RuntimeException('JSON encode error: ' . json_last_error_msg());