]> git.agnieray.net Git - galette.git/blob - galette/includes/dependencies.php
8a751a9c00e9fa16e7532549be5b55b00225e501
[galette.git] / galette / includes / dependencies.php
1 <?php
2
3 /**
4 * Dependency injection configuration
5 *
6 * PHP version 5
7 *
8 * Copyright © 2003-2023 The Galette Team
9 *
10 * This file is part of Galette (http://galette.tuxfamily.org).
11 *
12 * Galette is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * Galette is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * @category Functions
26 * @package Galette
27 *
28 * @author Frédéric Jacquot <unknown@unknow.com>
29 * @author Georges Khaznadar (password encryption, images) <unknown@unknow.com>
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2003-2023 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 http://galette.tuxfamily.org
34 */
35
36 use Psr\Container\ContainerInterface;
37 use Psr\Http\Message\ServerRequestInterface;
38 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
39 use Slim\Routing\RouteContext;
40 use Slim\Routing\RouteParser;
41 use Slim\Views\Twig;
42
43 $container = $app->getContainer();
44
45 $routeParser = $app->getRouteCollector()->getRouteParser();
46 $container->set(RouteParser::class, $routeParser);
47
48 // -----------------------------------------------------------------------------
49 // Service providers
50 // -----------------------------------------------------------------------------
51
52 $container->set(
53 \Slim\Routing\RouteCollector::class,
54 function () use ($app) {
55 return $app->getRouteCollector();
56 }
57 );
58
59 // Register View helper
60 $container->set('Slim\Views\Twig', function (ContainerInterface $c) {
61
62 $templates = ['__main__' => GALETTE_TPL_THEME_DIR];
63 foreach ($c->get('plugins')->getModules() as $module_id => $module) {
64 $dir = $module['root'] . '/templates/' . $c->get('preferences')->pref_theme;
65 if (!is_dir($dir)) {
66 continue;
67 }
68 $templates[$c->get('plugins')->getClassName($module_id)] = $dir;
69 }
70
71 $view = Twig::create(
72 $templates,
73 [
74 'cache' => rtrim(GALETTE_CACHE_DIR, DIRECTORY_SEPARATOR),
75 'debug' => \Galette\Core\Galette::isDebugEnabled(),
76 'strict_variables' => \Galette\Core\Galette::isDebugEnabled()
77 ]
78 );
79
80 //Twig extensions
81 $view->addExtension(new \Galette\Twig\CsrfExtension($c->get('csrf')));
82 if (\Galette\Core\Galette::isDebugEnabled()) {
83 $view->addExtension(new \Twig\Extension\DebugExtension());
84 }
85 //End Twig extensions
86
87 //Twig functions
88 $function = new \Twig\TwigFunction('__', function ($string, $domain = 'galette') {
89 return __($string, $domain);
90 });
91 $view->getEnvironment()->addFunction($function);
92
93 $function = new \Twig\TwigFunction('_T', function ($string, $domain = 'galette') {
94 return _T($string, $domain);
95 });
96 $view->getEnvironment()->addFunction($function);
97
98 $function = new \Twig\TwigFunction('_Tn', function ($singular, $plural, $count, $domain = 'galette') {
99 return _Tn($singular, $plural, $count, $domain);
100 });
101 $view->getEnvironment()->addFunction($function);
102
103 $function = new \Twig\TwigFunction('_Tx', function ($context, $string, $domain = 'galette') {
104 return _Tx($context, $string, $domain);
105 });
106 $view->getEnvironment()->addFunction($function);
107
108 $function = new \Twig\TwigFunction('_Tnx', function ($context, $singular, $plural, $count, $domain = 'galette') {
109 return _Tnx($context, $singular, $plural, $count, $domain);
110 });
111 $view->getEnvironment()->addFunction($function);
112
113 $function = new \Twig\TwigFunction('file_exists', function ($file) {
114 return file_exists($file);
115 });
116 $view->getEnvironment()->addFunction($function);
117
118 $function = new \Twig\TwigFunction('get_class', function ($object) {
119 return get_class($object);
120 });
121 $view->getEnvironment()->addFunction($function);
122
123 $function = new \Twig\TwigFunction('memberName', function (...$params) use ($c) {
124 extract($params[0]);
125 return Galette\Entity\Adherent::getSName($c->get('zdb'), $id);
126 });
127 $view->getEnvironment()->addFunction($function);
128
129 $function = new \Twig\TwigFunction('statusLabel', function (...$params) {
130 extract($params);
131 global $statuses_list;
132 return $statuses_list[$id];
133 });
134 $view->getEnvironment()->addFunction($function);
135
136 $view->getEnvironment()->addFunction(
137 new \Twig\TwigFunction('callstatic', function ($class, $method, ...$args) {
138 if (!class_exists($class)) {
139 throw new \Exception("Cannot call static method $method on Class $class: Invalid Class");
140 }
141
142 if (!method_exists($class, $method)) {
143 throw new \Exception("Cannot call static method $method on Class $class: Invalid method");
144 }
145
146 return forward_static_call_array([$class, $method], $args);
147 })
148 );
149 //End Twig functions
150
151 //Twig globals
152 $view->getEnvironment()->addGlobal('flash', $c->get('flash'));
153 $view->getEnvironment()->addGlobal('login', $c->get('login'));
154 $view->getEnvironment()->addGlobal('logo', $c->get('logo'));
155
156 $view->getEnvironment()->addGlobal('plugin_headers', $c->get('plugins')->getTplHeaders());
157
158 // galette_lang should be removed and languages used instead
159 $view->getEnvironment()->addGlobal('galette_lang', $c->get('i18n')->getAbbrev());
160 $view->getEnvironment()->addGlobal('galette_lang_name', $c->get('i18n')->getName());
161 $view->getEnvironment()->addGlobal('languages', $c->get('i18n')->getList());
162 $view->getEnvironment()->addGlobal('i18n', $c->get('i18n'));
163 $view->getEnvironment()->addGlobal('plugins', $c->get('plugins'));
164 $view->getEnvironment()->addGlobal('preferences', $c->get('preferences'));
165 $view->getEnvironment()->addGlobal('existing_mailing', $c->get('session')->mailing !== null);
166 $view->getEnvironment()->addGlobal('html_editor', false);
167 $view->getEnvironment()->addGlobal('require_charts', false);
168 $view->getEnvironment()->addGlobal('require_mass', false);
169 $view->getEnvironment()->addGlobal('autocomplete', false);
170 if ($c->get('login')->isAdmin() && $c->get('preferences')->pref_telemetry_date) {
171 $telemetry = new \Galette\Util\Telemetry(
172 $c->get('zdb'),
173 $c->get('preferences'),
174 $c->get('plugins')
175 );
176 if ($telemetry->shouldRenew()) {
177 $view->getEnvironment()->addGlobal('renew_telemetry', true);
178 }
179 }
180
181 $view->getEnvironment()->addGlobal('cur_route', null);
182 $view->getEnvironment()->addGlobal('cur_subroute', null);
183 $view->getEnvironment()->addGlobal('navigate', null);
184
185 //TRANS: see https://fomantic-ui.com/modules/calendar.html#custom-format - must be the same as Y-m-d for PHP https://www.php.net/manual/datetime.format.php
186 $view->getEnvironment()->addGlobal('fui_dateformatter', __("YYYY-MM-DD"));
187 //End Twig globals
188
189 return $view;
190 });
191
192 // Flash messages
193 //TODO: old way - to drop
194 $container->set(
195 'flash',
196 DI\get('Slim\Flash\Messages')
197 );
198 $container->set('Slim\Flash\Messages', DI\autowire());
199
200 //TODO: old way - to drop
201 $container->set(
202 'plugins',
203 \DI\get(Galette\Core\Plugins::class)
204 );
205
206 $container->set(Galette\Core\Plugins::class, function (ContainerInterface $c) use ($plugins) {
207 $i18n = $c->get('i18n');
208 $plugins->loadModules($c->get('preferences'), GALETTE_PLUGINS_PATH, $i18n->getLongID());
209 return $plugins;
210 });
211
212 //TODO: old way - to drop
213 $container->set(
214 'i18n',
215 \DI\get('Galette\Core\I18n')
216 );
217
218 $container->set('Galette\Core\I18n', function (ContainerInterface $c) {
219 $i18n = $c->get('session')->i18n;
220 if (!$i18n || !$i18n->getId() || isset($_GET['ui_pref_lang']) && $_GET['ui_pref_lang']) {
221 $i18n = new Galette\Core\I18n($_GET['ui_pref_lang'] ?? false);
222 $c->get('session')->i18n = $i18n;
223 }
224 return $i18n;
225 });
226
227 $container->set('l10n', function (ContainerInterface $c) {
228 $l10n = new Galette\Core\L10n(
229 $c->get('zdb'),
230 $c->get('i18n')
231 );
232 return $l10n;
233 });
234
235 //TODO: old way - to drop
236 $container->set(
237 'zdb',
238 DI\get('Galette\Core\Db')
239 );
240 $container->set('Galette\Core\Db', DI\autowire());
241
242 //TODO: old way - to drop
243 $container->set(
244 'preferences',
245 DI\get('Galette\Core\Preferences')
246 );
247 $container->set('Galette\Core\Preferences', DI\autowire());
248
249 //TODO: old way - to drop
250 $container->set(
251 'login',
252 DI\get('Galette\Core\Login')
253 );
254 $container->set('Galette\Core\Login', function (ContainerInterface $c) {
255 $login = $c->get('session')->login;
256 if (!$login) {
257 $login = new Galette\Core\Login(
258 $c->get('zdb'),
259 $c->get('i18n')
260 );
261 }
262 return $login;
263 });
264
265 //TODO: old way - to drop
266 $container->set(
267 'logo',
268 DI\get('Galette\Core\Logo')
269 );
270 $container->set('Galette\Core\Logo', DI\autowire());
271
272 //TODO: old way - to drop
273 $container->set(
274 'print_logo',
275 DI\get('Galette\Core\PrintLogo')
276 );
277 $container->set('Galette\Core\PrintLogo', DI\autowire());
278
279 //TODO: old way - to drop
280 $container->set(
281 'history',
282 DI\get('Galette\Core\History')
283 );
284 $container->set('Galette\Core\History', \DI\autowire());
285
286 $container->set('acls', function (ContainerInterface $c) {
287 include GALETTE_ROOT . 'includes/core_acls.php';
288 $acls = $core_acls;
289
290 foreach ($c->get('plugins')->getModules() as $plugin) {
291 $acls[$plugin['route'] . 'Info'] = 'member';
292 }
293
294 //use + to be sure core ACLs are not overrided by plugins ones.
295 $acls = $acls + $c->get('plugins')->getAcls();
296
297 //load user defined ACLs
298 if (file_exists(GALETTE_CONFIG_PATH . 'local_acls.inc.php')) {
299 //use array_merge here, we want $local_acls to override core ones.
300 $acls = array_merge($acls, $local_acls);
301 }
302
303 return $acls;
304 });
305
306 $container->set('texts_fields', function (ContainerInterface $c) {
307 include_once GALETTE_ROOT . 'includes/fields_defs/texts_fields.php';
308 return $texts_fields;
309 });
310
311 $container->set('members_fields', function (ContainerInterface $c) {
312 include GALETTE_ROOT . 'includes/fields_defs/members_fields.php';
313 return $members_fields;
314 });
315
316 $container->set('members_form_fields', function (ContainerInterface $c) {
317 $fields = $c->get('members_fields');
318 foreach ($fields as $k => $field) {
319 if ($field['position'] == -1) {
320 unset($fields[$k]);
321 }
322 }
323 return $fields;
324 });
325
326 $container->set('members_fields_cats', function (ContainerInterface $c) {
327 include GALETTE_ROOT . 'includes/fields_defs/members_fields_cats.php';
328 return $members_fields_cats;
329 });
330
331 // -----------------------------------------------------------------------------
332 // Service factories
333 // -----------------------------------------------------------------------------
334
335 // monolog
336 $container->set('logger', function (ContainerInterface $c) {
337 $settings = $c->get('settings');
338 $logger = new \Monolog\Logger($settings['logger']['name']);
339 $logger->pushProcessor(new \Monolog\Processor\UidProcessor());
340 $logger->pushHandler(new \Monolog\Handler\StreamHandler($settings['logger']['path'], $settings['logger']['level']));
341 return $logger;
342 });
343
344 //TODO: old way - to drop
345 $container->set(
346 'fields_config',
347 DI\get('Galette\Entity\FieldsConfig')
348 );
349 $container->set('Galette\Entity\FieldsConfig', function (ContainerInterface $c) {
350 $fc = new Galette\Entity\FieldsConfig(
351 $c->get('zdb'),
352 Galette\Entity\Adherent::TABLE,
353 $c->get('members_fields'),
354 $c->get('members_fields_cats')
355 );
356 return $fc;
357 });
358
359 //TODO: old way - to drop
360 $container->set(
361 'lists_config',
362 DI\get('Galette\Entity\ListsConfig')
363 );
364 $container->set('Galette\Entity\ListsConfig', function (ContainerInterface $c) {
365 $fc = new Galette\Entity\ListsConfig(
366 $c->get('zdb'),
367 Galette\Entity\Adherent::TABLE,
368 $c->get('members_fields'),
369 $c->get('members_fields_cats')
370 );
371 return $fc;
372 });
373
374 //TODO: old way - to drop
375 $container->set(
376 'translator',
377 DI\get('Galette\Core\Translator')
378 );
379 $container->set('Galette\Core\Translator', function (ContainerInterface $c) {
380 $translator = new Galette\Core\Translator();
381
382 $domains = ['galette'];
383 foreach ($domains as $domain) {
384 //load translation file for domain
385 $translator->addTranslationFilePattern(
386 'gettext',
387 GALETTE_ROOT . '/lang/',
388 '/%s/LC_MESSAGES/' . $domain . '.mo',
389 $domain
390 );
391
392 //check if a local lang file exists and load it
393 $translator->addTranslationFilePattern(
394 'phparray',
395 GALETTE_ROOT . '/lang/',
396 $domain . '_%s_local_lang.php',
397 $domain
398 );
399 }
400
401 $translator->setLocale($c->get('i18n')->getLongID());
402 return $translator;
403 });
404
405 // Add Event manager to dependency.
406 $container->set(
407 'event_manager',
408 DI\create(\League\Event\EventDispatcher::class)
409 ->method(
410 'subscribeListenersFrom',
411 DI\get('Galette\Events\MemberListener')
412 )
413 ->method(
414 'subscribeListenersFrom',
415 DI\get('Galette\Events\ContribListener')
416 )
417 );
418
419 $container->set(
420 'CsrfExclusions',
421 function (ContainerInterface $c): array {
422 return $c->get('plugins')->getCsrfExclusions();
423 }
424 );
425
426 $container->set(
427 'csrf',
428 function (ContainerInterface $c) use ($app) {
429 $responseFactory = $app->getResponseFactory();
430 $storage = null;
431 $guard = new \Slim\Csrf\Guard(
432 $responseFactory,
433 'csrf',
434 $storage,
435 null,
436 200,
437 16,
438 true
439 );
440
441 $exclusions = $c->get('CsrfExclusions');
442 $guard->setFailureHandler(function (ServerRequestInterface $request, RequestHandler $handler) use ($exclusions) {
443 $response = $handler->handle($request);
444 $routeContext = RouteContext::fromRequest($request);
445 $route = $routeContext->getRoute();
446
447 foreach ($exclusions as $exclusion) {
448 if (preg_match($exclusion, $route->getname())) {
449 //route is excluded form CSRF checks
450 return $response;
451 }
452 }
453 Analog::log(
454 'CSRF check has failed',
455 Analog::CRITICAL
456 );
457 throw new \RuntimeException(_T('Failed CSRF check!'));
458 });
459
460 return $guard;
461 }
462 );
463
464 //For bad existing globals can be used...
465 global $translator, $i18n;
466 if (
467 !$container->has('galette.mode')
468 || $container->get('galette.mode') !== 'INSTALL'
469 && $container->get('galette.mode') !== 'NEED_UPDATE'
470 ) {
471 global $zdb, $preferences, $login, $hist, $l10n, $emitter;
472 $zdb = $container->get('zdb');
473 $preferences = $container->get('preferences');
474 $login = $container->get('login');
475 $hist = $container->get('history');
476 $l10n = $container->get('l10n');
477 $emitter = $container->get('event_manager');
478 $routeparser = $container->get(RouteParser::class);
479 }
480 $i18n = $container->get('i18n');
481 $translator = $container->get('translator');
482 require_once GALETTE_ROOT . 'includes/i18n.inc.php';