]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Core/Galette.php
0caafec145ee83d9750a4ee0f311822d4534759f
[galette.git] / galette / lib / Galette / Core / Galette.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette application instance
7 *
8 * PHP version 5
9 *
10 * Copyright © 2020-2022 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Core
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2020-2022 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link https://galette.eu
34 * @since Available since 0.9.4-dev - 2020-05-18
35 */
36
37 namespace Galette\Core;
38
39 use Galette\Entity\Adherent;
40
41 /**
42 * Galette application instance
43 *
44 * @category Core
45 * @name Galette
46 * @package Galette
47 * @author Johan Cwiklinski <johan@x-tnd.be>
48 * @copyright 2020-2022 The Galette Team
49 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
50 * @link https://galette.eu
51 * @since Available since 0.9.4-dev - 2020-05-18
52 */
53 class Galette
54 {
55 public const MODE_PROD = 'PROD';
56 public const MODE_DEV = 'DEV';
57 public const MODE_MAINT = 'MAINT';
58 public const MODE_DEMO = 'DEMO';
59
60 /**
61 * Retrieve Galette version from git, if present.
62 *
63 * @param boolean $time Include time and timezone. Defaults to false.
64 *
65 * @return string
66 */
67 public static function gitVersion($time = false)
68 {
69 $galette_version = GALETTE_VERSION;
70
71 //used for both gith and nightly installs
72 $version = str_replace('-dev', '-git', GALETTE_VERSION);
73 if (strstr($version, '-git') === false) {
74 $version .= '-git';
75 }
76
77 if (is_dir(GALETTE_ROOT . '../.git')) {
78 $commitHash = trim(exec('git log --pretty="%h" -n1 HEAD'));
79
80 $commitDate = new \DateTime(trim(exec('git log -n1 --pretty=%ci HEAD')));
81
82 $galette_version = sprintf(
83 '%s-%s (%s)',
84 $version,
85 $commitHash,
86 $commitDate->format(($time ? 'Y-m-d H:i:s T' : 'Y-m-d'))
87 );
88 } elseif (GALETTE_NIGHTLY !== false) {
89 $galette_version = $version . '-' . GALETTE_NIGHTLY;
90 }
91 return $galette_version;
92 }
93
94 /**
95 * Get all menus
96 *
97 * @return array
98 */
99 public static function getAllMenus(): array
100 {
101 return static::getMenus(true);
102 }
103
104 /**
105 * Get menus
106 *
107 * @param bool $public Include public menus. Defaults to false
108 *
109 * @return array
110 */
111 public static function getMenus(bool $public = false): array
112 {
113 /**
114 * @var Login $login
115 * @var Preferences $preferences
116 * @var Plugins $plugins
117 */
118 global $login, $preferences, $plugins;
119
120 $menus = [];
121
122 if ($login->isLogged()) {
123 if (!$login->isSuperAdmin()) {
124 //member menu
125 $menus['myaccount'] = [
126 'title' => _T("My Account"),
127 'icon' => 'user',
128 'items' => [
129 [
130 'label' => _T('My contributions'),
131 'title' => _T('View and filter all my contributions'),
132 'route' => [
133 'name' => 'myContributions',
134 'args' => ['type' => 'contributions']
135 ]
136 ],
137 [
138 'label' => _T('My transactions'),
139 'title' => _T('View and filter all my transactions'),
140 'route' => [
141 'name' => 'myContributions',
142 'args' => ['type' => 'transactions']
143 ]
144 ],
145 [
146 'label' => _T('My information'),
147 'title' => _T('View my member card'),
148 'route' => [
149 'name' => 'me',
150 'args' => []
151 ]
152 ]
153 ]
154 ];
155
156 if ($preferences->pref_bool_create_member) {
157 $menus['myaccount']['items'][] = [
158 'label' => _T('Add a child member'),
159 'title' => _T('Add new child member in database'),
160 'route' => [
161 'name' => 'addMemberChild',
162 'args' => []
163 ]
164 ];
165 }
166 }
167
168 $menus['members'] = [
169 'title' => _T("Members"),
170 'icon' => 'users',
171 'items' => []
172 ];
173
174 if ($login->isAdmin() || $login->isStaff() || $login->isGroupManager()) {
175 $menus['members']['items'] = [
176 [
177 'label' => _T("List of members"),
178 'title' => _T("View, search into and filter member's list"),
179 'route' => [
180 'name' => 'members',
181 'aliases' => ['editMember', 'member']
182 ]
183 ],
184 [
185 'label' => _T("Advanced search"),
186 'title' => _T("Perform advanced search into members list"),
187 'route' => [
188 'name' => 'advanced-search'
189 ]
190 ],
191 [
192 'label' => _T("Saved searches"),
193 'title' => _T("Saved searches"),
194 'route' => [
195 'name' => 'searches'
196 ]
197 ]
198 ];
199 }
200
201 if (
202 $login->isAdmin()
203 || $login->isStaff()
204 || ($login->isGroupManager() && $preferences->pref_bool_groupsmanagers_create_member)
205 ) {
206 $menus['members']['items'][] = [
207 'label' => _T("Add a member"),
208 'title' => _T("Add new member in database"),
209 'route' => [
210 'name' => 'addMember'
211 ]
212 ];
213 }
214
215 if ($login->isAdmin() || $login->isStaff()) {
216 $menus['contributions'] = [
217 'title' => _T('Contributions'),
218 'icon' => 'receipt',
219 'items' => [
220 [
221 'label' => _T("List of contributions"),
222 'title' => _T("View and filter contributions"),
223 'route' => [
224 'name' => 'contributions',
225 'args' => ['type' => 'contributions'],
226 'aliases' => ['editContribution']
227 ]
228 ],
229 [
230 'label' => _T("List of transactions"),
231 'title' => _T("View and filter transactions"),
232 'route' => [
233 'name' => 'contributions',
234 'args' => ['type' => 'transactions'],
235 'aliases' => ['editTransaction']
236 ]
237 ],
238 [
239 'label' => _T("Add a membership fee"),
240 'title' => _T("Add new membership fee in database"),
241 'route' => [
242 'name' => 'addContribution',
243 'args' => ['type' => \Galette\Entity\Contribution::TYPE_FEE]
244 ]
245 ],
246 [
247 'label' => _T("Add a donation"),
248 'title' => _T("Add new donation in database"),
249 'route' => [
250 'name' => 'addContribution',
251 'args' => ['type' => \Galette\Entity\Contribution::TYPE_DONATION]
252 ]
253 ],
254 [
255 'label' => _T("Add a transaction"),
256 'title' => _T("Add new transaction in database"),
257 'route' => [
258 'name' => 'addTransaction'
259 ]
260 ],
261 [
262 'label' => _T("Reminders"),
263 'title' => _T("Send reminders to late members"),
264 'route' => [
265 'name' => 'reminders'
266 ]
267 ]
268 ]
269 ];
270 } //admin or staff
271
272 if ($login->isAdmin() || $login->isStaff() || $login->isGroupManager()) {
273 $menus['management'] = [
274 'title' => _T("Management"),
275 'icon' => 'dharmachakra',
276 'items' => [
277 [
278 'label' => _T("Manage groups"),
279 'title' => _T("View and manage groups"),
280 'route' => [
281 'name' => 'groups'
282 ]
283 ]
284 ]
285 ];
286
287 if ($login->isAdmin() || $login->isStaff()) {
288 $menus['management']['items'] = array_merge($menus['management']['items'], [
289 [
290 'label' => _T("Logs"),
291 'title' => _T("View application's logs"),
292 'route' => [
293 'name' => 'history'
294 ]
295 ],
296 [
297 'label' => _T("Manage mailings"),
298 'title' => _T("Manage mailings that has been sent"),
299 'route' => [
300 'name' => 'mailings'
301 ]
302 ],
303 [
304 'label' => _T("Exports"),
305 'title' => _T("Export some data in various formats"),
306 'route' => [
307 'name' => 'export'
308 ]
309 ],
310 [
311 'label' => _T("Imports"),
312 'title' => _T("Import members from CSV files"),
313 'route' => [
314 'name' => 'import',
315 'aliases' => ['importModel']
316 ]
317 ],
318 [
319 'label' => _T("Charts"),
320 'title' => _T("Various charts"),
321 'route' => [
322 'name' => 'charts'
323 ]
324 ]
325 ]);
326 }//admin or staff
327
328 if ($login->isAdmin()) {
329 $menus['configuration'] = [
330 'title' => _T("Configuration"),
331 'icon' => 'tools',
332 'items' => [
333 [
334 'label' => _T("Settings"),
335 'title' => _T("Set applications preferences (address, website, member's cards configuration, ...)"),
336 'route' => [
337 'name' => 'preferences'
338 ]
339 ],
340 [
341 'label' => _T("Plugins"),
342 'title' => _T("Information about available plugins"),
343 'route' => [
344 'name' => 'plugins'
345 ]
346 ],
347 [
348 'label' => _T("Core lists"),
349 'title' => _T("Customize lists fields and order"),
350 'route' => [
351 'name' => 'configureListFields',
352 'args' => ['table' => 'adherents']
353 ]
354 ],
355 [
356 'label' => _T("Core fields"),
357 'title' => _T("Customize fields order, set which are required, and for who they're visibles"),
358 'route' => [
359 'name' => 'configureCoreFields'
360 ]
361 ],
362 [
363 'label' => _T("Dynamic fields"),
364 'title' => _T("Manage additional fields for various forms"),
365 'route' => [
366 'name' => 'configureDynamicFields',
367 'aliases' => ['editDynamicField'],
368 ]
369 ],
370 [
371 'label' => _T("Translate labels"),
372 'title' => _T("Translate additionnals fields labels"),
373 'route' => [
374 'name' => 'dynamicTranslations'
375 ]
376 ],
377 [
378 'label' => _T("Manage statuses"),
379 'title' => _T("Manage statuses"),
380 'route' => [
381 'name' => 'entitleds',
382 'args' => ['class' => 'status'],
383 'aliases' => ['editEntitled'],
384 'sub_select' => false
385 ]
386 ],
387 [
388 'label' => _T("Contributions types"),
389 'title' => _T("Manage contributions types"),
390 'route' => [
391 'name' => 'entitleds',
392 'args' => ['class' => 'contributions-types']
393 ]
394 ],
395 [
396 'label' => _T("Emails content"),
397 'title' => _T("Manage emails texts and subjects"),
398 'route' => [
399 'name' => 'texts'
400 ]
401 ],
402 [
403 'label' => _T("Titles"),
404 'title' => _T("Manage titles"),
405 'route' => [
406 'name' => 'titles',
407 'aliases' => ['editTitle']
408 ]
409 ],
410 [
411 'label' => _T("PDF models"),
412 'title' => _T("Manage PDF models"),
413 'route' => [
414 'name' => 'pdfModels'
415 ]
416 ],
417 [
418 'label' => _T("Payment types"),
419 'title' => _T("Manage payment types"),
420 'route' => [
421 'name' => 'paymentTypes',
422 'aliases' => ['editPaymentType']
423 ]
424 ],
425 [
426 'label' => _T("Empty adhesion form"),
427 'title' => _T("Download empty adhesion form"),
428 'route' => [
429 'name' => 'emptyAdhesionForm'
430 ]
431 ]
432 ]
433 ];
434
435 if ($login->isSuperAdmin()) {
436 $menus['configuration']['items'][] = [
437 'label' => _T("Admin tools"),
438 'title' => _T("Various administrative tools"),
439 'route' => [
440 'name' => 'adminTools'
441 ]
442 ];
443 }
444 }
445 }
446 } // /isLogged
447
448 foreach (array_keys($plugins->getModules()) as $module_id) {
449 //get plugins menus entries
450 $plugin_class = $plugins->getClassName($module_id, true);
451 if (class_exists($plugin_class)) {
452 $plugin = new $plugin_class();
453 $menus = array_merge_recursive(
454 $menus,
455 $plugin->getMenus()
456 );
457 }
458 }
459
460 if ($public) {
461 $menus += static::getPublicMenus();
462 }
463
464 //cleanup empty entries (no items)
465 foreach ($menus as $key => $menu) {
466 if (!count($menu['items'])) {
467 unset($menus[$key]);
468 }
469 }
470
471 return $menus;
472 }
473
474 /**
475 * Get public menus
476 *
477 * @return array
478 */
479 public static function getPublicMenus(): array
480 {
481 /**
482 * @var Preferences $preferences
483 * @var Login $login
484 * @var Plugins $plugins
485 */
486 global $preferences, $login, $plugins;
487
488 $menus = [];
489 if ($preferences->showPublicPages($login)) {
490 $menus['public'] = [
491 'title' => _T("Public pages"),
492 'icon' => 'eye outline',
493 'items' => [
494 [
495 'label' => _T("Members list"),
496 'title' => _T("Members list"),
497 'route' => [
498 'name' => 'publicList',
499 'args' => ['type' => 'list']
500 ],
501 'icon' => 'address book'
502 ],
503 [
504 'label' => _T("Trombinoscope"),
505 'title' => _T("Trombinoscope"),
506 'route' => [
507 'name' => 'publicList',
508 'args' => ['type' => 'trombi']
509 ],
510 'icon' => 'user friends'
511 ]
512 ]
513 ];
514
515 foreach (array_keys($plugins->getModules()) as $module_id) {
516 //get plugins public menus entries
517 $plugin_class = $plugins->getClassName($module_id, true);
518 if (class_exists($plugin_class)) {
519 $plugin = new $plugin_class();
520 $menus['public']['items'] = array_merge(
521 $menus['public']['items'],
522 $plugin->getPublicMenuItems()
523 );
524 }
525 }
526 }
527
528 return $menus;
529 }
530
531 /**
532 * Get dashboards
533 *
534 * @return array
535 */
536 public static function getDashboards(): array
537 {
538 /**
539 * @var Login $login
540 * @var Plugins $plugins
541 */
542 global $login, $plugins;
543
544 $dashboards = [];
545
546 if ($login->isAdmin() || $login->isStaff() || $login->isGroupManager()) {
547 $dashboards = array_merge(
548 $dashboards,
549 [
550 [
551 'label' => _T("Members"),
552 'title' => _T("View, search into and filter member's list"),
553 'route' => [
554 'name' => 'members'
555 ],
556 'icon' => 'card_box'
557 ],
558 [
559 'label' => _T("Groups"),
560 'title' => _T("View and manage groups"),
561 'route' => [
562 'name' => 'groups'
563 ],
564 'icon' => 'busts_in_silhouette'
565 ],
566 ]
567 );
568 }
569
570 if ($login->isAdmin() || $login->isStaff()) {
571 $dashboards = array_merge(
572 $dashboards,
573 [
574 [
575 'label' => _T("Mailings"),
576 'title' => _T("Manage mailings that has been sent"),
577 'route' => [
578 'name' => 'mailings'
579 ],
580 'icon' => 'postbox'
581 ],
582 [
583 'label' => _T("Contributions"),
584 'title' => _T("View and filter contributions"),
585 'route' => [
586 'name' => 'contributions',
587 'args' => ['type' => 'contributions']
588 ],
589 'icon' => 'receipt'
590 ],
591 [
592 'label' => _T("Transactions"),
593 'title' => _T("View and filter transactions"),
594 'route' => [
595 'name' => 'contributions',
596 'args' => ['type' => 'transactions']
597 ],
598 'icon' => 'book'
599 ],
600 [
601 'label' => _T("Reminders"),
602 'title' => _T("Send reminders to late members"),
603 'route' => [
604 'name' => 'reminders'
605 ],
606 'icon' => 'bell'
607 ],
608 ]
609 );
610 }
611
612 if ($login->isAdmin()) {
613 $dashboards = array_merge(
614 $dashboards,
615 [
616 [
617 'label' => _T("Settings"),
618 'title' => _T("Set applications preferences (address, website, member's cards configuration, ...)"),
619 'route' => [
620 'name' => 'preferences'
621 ],
622 'icon' => 'control_knobs'
623 ],
624 [
625 'label' => _T("Plugins"),
626 'title' => _T("Information about available plugins"),
627 'route' => [
628 'name' => 'plugins'
629 ],
630 'icon' => 'package'
631 ],
632 ]
633 );
634 }
635
636 if (!$login->isAdmin() && !$login->isStaff() && !$login->isGroupManager()) {
637 // Single member
638 $dashboards = array_merge(
639 $dashboards,
640 [
641 [
642 'label' => _T("My information"),
643 'title' => _T("View my member card"),
644 'route' => [
645 'name' => 'me'
646 ],
647 'icon' => 'bust_in_silhouette'
648 ],
649 [
650 'label' => _T("My contributions"),
651 'title' => _T("View and filter all my contributions"),
652 'route' => [
653 'name' => 'contributions',
654 'args' => ['type' => 'contributions']
655 ],
656 'icon' => 'receipt'
657 ],
658 [
659 'label' => _T("My transactions"),
660 'title' => _T("View and filter all my transactions"),
661 'route' => [
662 'name' => 'contributions',
663 'args' => ['type' => 'transactions']
664 ],
665 'icon' => 'book'
666 ],
667
668 ]
669 );
670 }
671
672 foreach (array_keys($plugins->getModules()) as $module_id) {
673 //get plugins menus entries
674 $plugin_class = $plugins->getClassName($module_id, true);
675 if (class_exists($plugin_class)) {
676 /** @var GalettePlugin $plugin */
677 $plugin = new $plugin_class();
678 $dashboards = array_merge_recursive(
679 $dashboards,
680 $plugin->getDashboards()
681 );
682 }
683 }
684
685 return $dashboards;
686 }
687
688 /**
689 * Get members list actions
690 *
691 * @param Adherent $member Current member
692 *
693 * @return array
694 */
695 public static function getListActions(Adherent $member): array
696 {
697 /**
698 * @var Login $login
699 * @var Plugins $plugins
700 */
701 global $login, $plugins;
702
703 $actions = [];
704
705 if ($member->canEdit($login)) {
706 $actions[] = [
707 'label' => str_replace(
708 "%membername",
709 $member->sname,
710 _T("%membername: edit information")
711 ),
712 'title' => str_replace(
713 "%membername",
714 $member->sname,
715 _T("%membername: edit information")
716 ),
717 'route' => [
718 'name' => 'editMember',
719 'args' => ['id' => $member->id]
720 ],
721 'icon' => 'user edit'
722 ];
723 }
724
725 if ($login->isAdmin() || $login->isStaff()) {
726 $actions = array_merge($actions, [
727 [
728 'label' => str_replace(
729 "%membername",
730 $member->sname,
731 _T("%membername: contributions")
732 ),
733 'title' => str_replace(
734 "%membername",
735 $member->sname,
736 _T("%membername: contributions")
737 ),
738 'route' => [
739 'name' => 'contributions',
740 'args' => [
741 "type" => "contributions",
742 "option" => "member",
743 'value' => $member->id
744 ]
745 ],
746 'icon' => 'receipt yellow'
747 ],
748 [
749 'label' => str_replace(
750 "%membername",
751 $member->sname,
752 _T("%membername: remove from database")
753 ),
754 'title' => str_replace(
755 "%membername",
756 $member->sname,
757 _T("%membername: remove from database")
758 ),
759 'route' => [
760 'name' => 'removeMember',
761 'args' => [
762 'id' => $member->id
763 ]
764 ],
765 'icon' => 'user times red',
766 'extra_class' => 'delete'
767 ]
768 ]);
769 }
770
771 if ($login->isSuperAdmin()) {
772 $actions[] = [
773 'label' => str_replace(
774 "%membername",
775 $member->sname,
776 _T("Log in in as %membername")
777 ),
778 'title' => str_replace(
779 "%membername",
780 $member->sname,
781 _T("Log in in as %membername")
782 ),
783 'route' => [
784 'name' => 'impersonate',
785 'args' => [
786 'id' => $member->id
787 ]
788 ],
789 'icon' => 'user secret grey'
790 ];
791 }
792
793 foreach (array_keys($plugins->getModules()) as $module_id) {
794 //get plugins menus entries
795 $plugin_class = $plugins->getClassName($module_id, true);
796 if (class_exists($plugin_class)) {
797 /** @var GalettePlugin $plugin */
798 $plugin = new $plugin_class();
799 $actions = array_merge_recursive(
800 $actions,
801 $plugin->getListActions($member)
802 );
803 }
804 }
805 return $actions;
806 }
807
808 /**
809 * Get member show actions
810 *
811 * @param Adherent $member Current member
812 *
813 * @return array
814 */
815 public static function getDetailedActions(Adherent $member): array
816 {
817 /**
818 * @var Login $login
819 * @var Plugins $plugins
820 */
821 global $login, $plugins;
822
823 $actions = [];
824
825 //TODO: add core detailled actions
826
827 foreach (array_keys($plugins->getModules()) as $module_id) {
828 //get plugins menus entries
829 $plugin_class = $plugins->getClassName($module_id, true);
830 if (class_exists($plugin_class)) {
831 /** @var GalettePlugin $plugin */
832 $plugin = new $plugin_class();
833 $actions = array_merge_recursive(
834 $actions,
835 $plugin->getDetailedActions($member)
836 );
837 }
838 }
839 return $actions;
840 }
841
842 /**
843 * Get members list batch actions
844 *
845 * @return array
846 */
847 public static function getBatchActions(): array
848 {
849 /**
850 * @var Login $login
851 * @var Plugins $plugins
852 * @var Preferences $preferences
853 */
854 global $login, $plugins, $preferences;
855
856 $actions = [];
857
858 if (
859 $login->isAdmin()
860 || $login->isStaff()
861 ) {
862 $actions = array_merge(
863 $actions,
864 [
865 [
866 'name' => 'masschange',
867 'label' => _T('Mass change'),
868 'icon' => 'user edit blue'
869 ],
870 [
871 'name' => 'masscontributions',
872 'label' => _T('Mass add contributions'),
873 'icon' => 'receipt bite yellow'
874 ],
875 [
876 'name' => 'delete',
877 'label' => _T('Delete'),
878 'icon' => 'user times red'
879 ]
880 ]
881 );
882 }
883
884 if (
885 ($login->isAdmin()
886 || $login->isStaff()
887 || $login->isGroupManager()
888 && $preferences->pref_bool_groupsmanagers_mailings)
889 && $preferences->pref_mail_method != \Galette\Core\GaletteMail::METHOD_DISABLED
890 ) {
891 $actions[] = [
892 'name' => 'sendmail',
893 'label' => _T('Mail'),
894 'icon' => 'mail bulk'
895 ];
896 }
897
898 if (
899 $login->isGroupManager()
900 && $preferences->pref_bool_groupsmanagers_exports
901 || $login->isAdmin()
902 || $login->isStaff()
903 ) {
904 $actions = array_merge(
905 $actions,
906 [
907 [
908 'name' => 'attendance_sheet',
909 'label' => _T('Attendance sheet'),
910 'icon' => 'file alternate'
911 ],
912 [
913 'name' => 'labels__directdownload',
914 'label' => _T('Generate labels'),
915 'icon' => 'address card'
916 ],
917 [
918 'name' => 'cards__directdownload',
919 'label' => _T('Generate Member Cards'),
920 'icon' => 'id badge'
921 ],
922 [
923 'name' => 'csv__directdownload',
924 'label' => _T('Export as CSV'),
925 'icon' => 'file csv'
926 ],
927 ]
928 );
929 }
930
931 foreach (array_keys($plugins->getModules()) as $module_id) {
932 //get plugins menus entries
933 $plugin_class = $plugins->getClassName($module_id, true);
934 if (class_exists($plugin_class)) {
935 /** @var GalettePlugin $plugin */
936 $plugin = new $plugin_class();
937 $actions = array_merge_recursive(
938 $actions,
939 $plugin->getBatchActions()
940 );
941 }
942 }
943 return $actions;
944 }
945 }