]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Controllers/PdfController.php
Add methods to manage dependencies to load on members
[galette.git] / galette / lib / Galette / Controllers / PdfController.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette PDF controller
7 *
8 * PHP version 5
9 *
10 * Copyright © 2019 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 Entity
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2019 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 * @since Available since 0.9.4dev - 2019-12-05
35 */
36
37 namespace Galette\Controllers;
38
39 use Throwable;
40 use Slim\Http\Request;
41 use Slim\Http\Response;
42 use Analog\Analog;
43 use Galette\Core\Links;
44 use Galette\Core\Login;
45 use Galette\Entity\Adherent;
46 use Galette\Entity\Contribution;
47 use Galette\Entity\PdfModel;
48 use Galette\Filters\MembersList;
49 use Galette\IO\Pdf;
50 use Galette\IO\PdfAttendanceSheet;
51 use Galette\IO\PdfContribution;
52 use Galette\IO\PdfGroups;
53 use Galette\IO\PdfMembersCards;
54 use Galette\IO\PdfMembersLabels;
55 use Galette\Repository\Members;
56 use Galette\Repository\Groups;
57 use Galette\Repository\PdfModels;
58
59 /**
60 * Galette PDF controller
61 *
62 * @category Controllers
63 * @name GaletteController
64 * @package Galette
65 * @author Johan Cwiklinski <johan@x-tnd.be>
66 * @copyright 2019 The Galette Team
67 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
68 * @link http://galette.tuxfamily.org
69 * @since Available since 0.9.4dev - 2019-12-05
70 */
71
72 class PdfController extends AbstractController
73 {
74 /**
75 * Send response
76 *
77 * @param Response $response PSR Response
78 * @param Pdf $pdf PDF to output
79 *
80 * @return Response
81 */
82 protected function sendResponse(Response $response, Pdf $pdf): Response
83 {
84 return $response
85 ->withHeader('Content-type', 'application/pdf')
86 ->withHeader('Content-Disposition', 'attachment;filename="' . $pdf->getFileName() . '"')
87 ->write($pdf->download());
88 }
89
90 /**
91 * Members PDF card
92 *
93 * @param Request $request PSR Request
94 * @param Response $response PSR Response
95 * @param integer $id_adh Member id
96 *
97 * @return Response
98 */
99 public function membersCards(Request $request, Response $response, int $id_adh = null): Response
100 {
101 if ($this->session->filter_members) {
102 $filters = $this->session->filter_members;
103 } else {
104 $filters = new MembersList();
105 }
106
107 if ($id_adh !== null && $id_adh > 0) {
108 $adh = new Adherent($this->zdb);
109 $adh->enableDep('dynamics');
110 if ($this->login->id != $id_adh) {
111 $adh->disableDep('dues');
112 }
113 $adh->load($id_adh);
114
115 if (!$adh->canEdit($this->login)) {
116 $this->flash->addMessage(
117 'error_detected',
118 _T("You do not have permission for requested URL.")
119 );
120
121 return $response
122 ->withStatus(403)
123 ->withHeader(
124 'Location',
125 $this->router->pathFor('me')
126 );
127 }
128
129 //check if member is up to date
130 if ($this->login->id == $id_adh) {
131 if (!$adh->isUp2Date()) {
132 Analog::log(
133 'Member ' . $id_adh . ' is not up to date; cannot get his PDF member card',
134 Analog::WARNING
135 );
136 return $response
137 ->withStatus(301)
138 ->withHeader('Location', $this->router->pathFor('slash'));
139 }
140 }
141
142 // If we are called from a member's card, get unique id value
143 $unique = $id_adh;
144 } else {
145 if (count($filters->selected) == 0) {
146 Analog::log(
147 'No member selected to generate members cards',
148 Analog::INFO
149 );
150 $this->flash->addMessage(
151 'error_detected',
152 _T("No member was selected, please check at least one name.")
153 );
154
155 return $response
156 ->withStatus(301)
157 ->withHeader('Location', $this->router->pathFor('members'));
158 }
159 }
160
161 // Fill array $selected with selected ids
162 $selected = array();
163 if (isset($unique) && $unique) {
164 $selected[] = $unique;
165 } else {
166 $selected = $filters->selected;
167 }
168
169 $m = new Members();
170 $members = $m->getArrayList(
171 $selected,
172 array('nom_adh', 'prenom_adh'),
173 true
174 );
175
176 if (!is_array($members) || count($members) < 1) {
177 Analog::log(
178 'An error has occurred, unable to get members list.',
179 Analog::ERROR
180 );
181
182 $this->flash->addMessage(
183 'error_detected',
184 _T("Unable to get members list.")
185 );
186
187 return $response
188 ->withStatus(301)
189 ->withHeader('Location', $this->router->pathFor('members'));
190 }
191
192 $pdf = new PdfMembersCards($this->preferences);
193 $pdf->drawCards($members);
194
195 return $this->sendResponse($response, $pdf);
196 }
197
198 /**
199 * Members PDF label
200 *
201 * @param Request $request PSR Request
202 * @param Response $response PSR Response
203 *
204 * @return Response
205 */
206 public function membersLabels(Request $request, Response $response): Response
207 {
208 $post = $request->getParsedBody();
209 $get = $request->getQueryParams();
210
211 $session_var = $post['session_var'] ?? $get['session_var'] ?? 'filter_members';
212
213 if (isset($this->session->$session_var)) {
214 $filters = $this->session->$session_var;
215 } else {
216 $filters = new MembersList();
217 }
218
219 $members = null;
220 if (
221 isset($get['from'])
222 && $get['from'] === 'mailing'
223 ) {
224 //if we're from mailing, we have to retrieve
225 //its unreachables members for labels
226 $mailing = $this->session->mailing;
227 $members = $mailing->unreachables;
228 } else {
229 if (count($filters->selected) == 0) {
230 Analog::log('No member selected to generate labels', Analog::INFO);
231 $this->flash->addMessage(
232 'error_detected',
233 _T("No member was selected, please check at least one name.")
234 );
235
236 return $response
237 ->withStatus(301)
238 ->withHeader('Location', $this->router->pathFor('members'));
239 }
240
241 $m = new Members();
242 $members = $m->getArrayList(
243 $filters->selected,
244 array('nom_adh', 'prenom_adh')
245 );
246 }
247
248 if (!is_array($members) || count($members) < 1) {
249 Analog::log(
250 'An error has occurred, unable to get members list.',
251 Analog::ERROR
252 );
253
254 $this->flash->addMessage(
255 'error_detected',
256 _T("Unable to get members list.")
257 );
258
259 return $response
260 ->withStatus(301)
261 ->withHeader('Location', $this->router->pathFor('members'));
262 }
263
264 $pdf = new PdfMembersLabels($this->preferences);
265 $pdf->drawLabels($members);
266
267 return $this->sendResponse($response, $pdf);
268 }
269
270 /**
271 * PDF adhesion form
272 *
273 * @param Request $request PSR Request
274 * @param Response $response PSR Response
275 * @param integer $id_adh Member id
276 *
277 * @return Response
278 */
279 public function adhesionForm(Request $request, Response $response, int $id_adh = null): Response
280 {
281 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true]);
282
283 if ($id_adh !== null && !$adh->canEdit($this->login)) {
284 $this->flash->addMessage(
285 'error_detected',
286 _T("You do not have permission for requested URL.")
287 );
288
289 return $response
290 ->withStatus(403)
291 ->withHeader(
292 'Location',
293 $this->router->pathFor('me')
294 );
295 }
296
297 $form = $this->preferences->pref_adhesion_form;
298 $pdf = new $form($adh, $this->zdb, $this->preferences);
299
300 return $this->sendResponse($response, $pdf);
301 }
302
303 /**
304 * PDF attendance sheet configuration page
305 *
306 * @param Request $request PSR Request
307 * @param Response $response PSR Response
308 *
309 * @return Response
310 */
311 public function attendanceSheetConfig(Request $request, Response $response): Response
312 {
313 $post = $request->getParsedBody();
314
315 if ($this->session->filter_members !== null) {
316 $filters = $this->session->filter_members;
317 } else {
318 $filters = new MembersList();
319 }
320
321 // check for ajax mode
322 $ajax = false;
323 if (
324 $request->isXhr()
325 || (isset($post['ajax'])
326 && $post['ajax'] == 'true')
327 ) {
328 $ajax = true;
329
330 //retrieve selected members
331 $selection = $post['selection'] ?? array();
332
333 $filters->selected = $selection;
334 $this->session->filter_members = $filters;
335 } else {
336 $selection = $filters->selected;
337 }
338
339 // display page
340 $this->view->render(
341 $response,
342 'attendance_sheet_details.tpl',
343 [
344 'page_title' => _T("Attendance sheet configuration"),
345 'ajax' => $ajax,
346 'selection' => $selection
347 ]
348 );
349 return $response;
350 }
351
352 /**
353 * PDF attendance sheet
354 *
355 * @param Request $request PSR Request
356 * @param Response $response PSR Response
357 *
358 * @return Response
359 */
360 public function attendanceSheet(Request $request, Response $response): Response
361 {
362 $post = $request->getParsedBody();
363
364 if ($this->session->filter_members !== null) {
365 $filters = $this->session->filter_members;
366 } else {
367 $filters = new MembersList();
368 }
369
370 //retrieve selected members
371 $selection = (isset($post['selection'])) ? $post['selection'] : array();
372
373 $filters->selected = $selection;
374 $this->session->filter_members = $filters;
375
376 if (count($filters->selected) == 0) {
377 Analog::log('No member selected to generate attendance sheet', Analog::INFO);
378 $this->flash->addMessage(
379 'error_detected',
380 _T("No member selected to generate attendance sheet")
381 );
382
383 return $response
384 ->withStatus(301)
385 ->withHeader('Location', $this->router->pathFor('members'));
386 }
387
388 $m = new Members();
389 $members = $m->getArrayList(
390 $filters->selected,
391 array('nom_adh', 'prenom_adh'),
392 true
393 );
394
395 if (!is_array($members) || count($members) < 1) {
396 Analog::log('No member selected to generate attendance sheet', Analog::INFO);
397 $this->flash->addMessage(
398 'error_detected',
399 _T("No member selected to generate attendance sheet")
400 );
401
402 return $response
403 ->withStatus(301)
404 ->withHeader('Location', $this->router->pathFor('members'));
405 }
406
407 $doc_title = _T("Attendance sheet");
408 if (isset($post['sheet_type']) && trim($post['sheet_type']) != '') {
409 $doc_title = $post['sheet_type'];
410 }
411
412 $data = [
413 'doc_title' => $doc_title,
414 'title' => $post['sheet_title'] ?? null,
415 'subtitle' => $post['sheet_sub_title'] ?? null,
416 'sheet_date' => $post['sheet_date'] ?? null
417 ];
418 $pdf = new PdfAttendanceSheet($this->zdb, $this->preferences, $data);
419 //with or without images?
420 if (isset($post['sheet_photos']) && $post['sheet_photos'] === '1') {
421 $pdf->withImages();
422 }
423 $pdf->drawSheet($members);
424
425 return $this->sendResponse($response, $pdf);
426 }
427
428 /**
429 * Contribution PDF
430 *
431 * @param Request $request PSR Request
432 * @param Response $response PSR Response
433 * @param integer $id Contribution id
434 *
435 * @return Response
436 */
437 public function contribution(Request $request, Response $response, int $id): Response
438 {
439 $contribution = new Contribution($this->zdb, $this->login, $id);
440 if ($contribution->id == '') {
441 //not possible to load contribution, exit
442 $this->flash->addMessage(
443 'error_detected',
444 str_replace(
445 '%id',
446 $id,
447 _T("Unable to load contribution #%id!")
448 )
449 );
450 return $response
451 ->withStatus(301)
452 ->withHeader('Location', $this->router->pathFor(
453 'contributions',
454 ['type' => 'contributions']
455 ));
456 }
457
458 $pdf = new PdfContribution($contribution, $this->zdb, $this->preferences);
459 return $this->sendResponse($response, $pdf);
460 }
461
462 /**
463 * Groups PDF
464 *
465 * @param Request $request PSR Request
466 * @param Response $response PSR Response
467 * @param integer $id Group id
468 *
469 * @return Response
470 */
471 public function group(Request $request, Response $response, int $id = null): Response
472 {
473 $groups = new Groups($this->zdb, $this->login);
474
475 $groups_list = null;
476 if ($id !== null) {
477 $groups_list = $groups->getList(true, $id);
478 } else {
479 $groups_list = $groups->getList();
480 }
481
482 if (!is_array($groups_list) || count($groups_list) < 1) {
483 Analog::log(
484 'An error has occurred, unable to get groups list.',
485 Analog::ERROR
486 );
487
488 $this->flash->addMessage(
489 'error_detected',
490 _T("Unable to get groups list.")
491 );
492
493 return $response
494 ->withStatus(301)
495 ->withHeader('Location', $this->router->pathFor('groups'));
496 }
497
498 $pdf = new PdfGroups($this->preferences);
499 $pdf->draw($groups_list, $this->login);
500
501 return $this->sendResponse($response, $pdf);
502 }
503
504 /**
505 * PDF models list
506 *
507 * @param Request $request PSR Request
508 * @param Response $response PSR Response
509 * @param integer $id Model id
510 *
511 * @return Response
512 */
513 public function models(Request $request, Response $response, int $id = null): Response
514 {
515 $mid = 1;
516 if (isset($_POST[PdfModel::PK])) {
517 $mid = (int)$_POST[PdfModel::PK];
518 } elseif ($id !== null) {
519 $mid = $id;
520 }
521
522
523 $ms = new PdfModels($this->zdb, $this->preferences, $this->login);
524 $models = $ms->getList();
525
526 $model = null;
527 foreach ($models as $m) {
528 if ($m->id === $mid) {
529 $model = $m;
530 break;
531 }
532 }
533
534 $tpl = null;
535 $params = ['model' => $model];
536
537 //Render directly template if we called from ajax,
538 //render in a full page otherwise
539 if (
540 $request->isXhr()
541 || (isset($request->getQueryParams()['ajax'])
542 && $request->getQueryParams()['ajax'] == 'true')
543 ) {
544 $tpl = 'gestion_pdf_content.tpl';
545 } else {
546 $tpl = 'gestion_pdf.tpl';
547 $params += [
548 'page_title' => _T("PDF models"),
549 'models' => $models
550 ];
551 }
552
553 // display page
554 $this->view->render(
555 $response,
556 $tpl,
557 $params
558 );
559 return $response;
560 }
561
562 /**
563 * Store PDF models
564 *
565 * @param Request $request PSR Request
566 * @param Response $response PSR Response
567 *
568 * @return Response
569 */
570 public function storeModels(Request $request, Response $response): Response
571 {
572 $post = $request->getParsedBody();
573 $error_detected = [];
574
575 if (!isset($post['model_type'])) {
576 $error_detected[] = _T("Missing PDF model type!");
577 } else {
578 $type = (int)$post['model_type'];
579 $class = PdfModel::getTypeClass($type);
580 if (isset($post[PdfModel::PK])) {
581 $model = new $class($this->zdb, $this->preferences, (int)$post[PdfModel::PK]);
582 } else {
583 $model = new $class($this->zdb, $this->preferences);
584 }
585
586 try {
587 $fields = [
588 'model_header' => 'header',
589 'model_footer' => 'footer',
590 'model_body' => 'body',
591 'model_title' => 'title',
592 'model_subtitle' => 'subtitle',
593 'model_styles' => 'styles'
594 ];
595
596 $model->type = $type;
597 foreach ($fields as $pvar => $prop) {
598 if (isset($post[$pvar])) {
599 $model->$prop = $post[$pvar];
600 }
601 }
602
603 $res = $model->store();
604 if ($res === true) {
605 $this->flash->addMessage(
606 'success_detected',
607 _T("Model has been successfully stored!")
608 );
609 } else {
610 $error_detected[] = _T("Model has not been stored :(");
611 }
612 } catch (Throwable $e) {
613 $error_detected[] = $e->getMessage();
614 }
615 }
616
617 if (count($error_detected) > 0) {
618 foreach ($error_detected as $error) {
619 $this->flash->addMessage(
620 'error_detected',
621 $error
622 );
623 }
624 }
625
626 return $response
627 ->withStatus(301)
628 ->withHeader('Location', $this->router->pathFor('pdfModels', ['id' => $model->id ?? null]));
629 }
630
631
632 /**
633 * Get direct document
634 *
635 * @param Request $request PSR Request
636 * @param Response $response PSR Response
637 * @param string $hash Hash
638 *
639 * @return Response
640 */
641 public function directlinkDocument(Request $request, Response $response, string $hash): Response
642 {
643 $post = $request->getParsedBody();
644 $email = $post['email'];
645
646 $links = new Links($this->zdb);
647 $valid = $links->isHashValid($hash, $email);
648
649 if ($valid === false) {
650 $this->flash->addMessage(
651 'error_detected',
652 _T("Invalid link!")
653 );
654
655 return $response->withStatus(301)
656 ->withHeader('Location', $this->router->pathFor('directlink', ['hash' => $hash]));
657 }
658
659 $target = $valid[0];
660 $id = (int)$valid[1];
661
662 //get user information (like id...) from DB since its missing
663 $select = $this->zdb->select(Adherent::TABLE, 'a');
664 $select->where(['email_adh' => $post['email']]);
665 $results = $this->zdb->execute($select);
666 $row = $results->current();
667
668 //create a new login instance, to not break current session if any
669 //this will be passed directly to Contribution constructor
670 $login = new Login(
671 $this->zdb,
672 $this->i18n
673 );
674 $login->setId((int)$row['id_adh']);
675
676 if ($target === Links::TARGET_MEMBERCARD) {
677 $m = new Members();
678 $members = $m->getArrayList(
679 [$id],
680 array('nom_adh', 'prenom_adh'),
681 true
682 );
683
684 if (!is_array($members) || count($members) < 1) {
685 Analog::log(
686 'An error has occurred, unable to get members list.',
687 Analog::ERROR
688 );
689
690 $this->flash->addMessage(
691 'error_detected',
692 _T("Unable to get members list.")
693 );
694
695 return $response
696 ->withStatus(301)
697 ->withHeader('Location', $this->router->pathFor('directlink', ['hash' => $hash]));
698 }
699
700 $pdf = new PdfMembersCards($this->preferences);
701 $pdf->drawCards($members);
702 } else {
703 $contribution = new Contribution($this->zdb, $login, $id);
704 if ($contribution->id == '') {
705 //not possible to load contribution, exit
706 $this->flash->addMessage(
707 'error_detected',
708 str_replace(
709 '%id',
710 $id,
711 _T("Unable to load contribution #%id!")
712 )
713 );
714 return $response
715 ->withStatus(301)
716 ->withHeader('Location', $this->router->pathFor(
717 'directlink',
718 ['hash' => $hash]
719 ));
720 }
721 $pdf = new PdfContribution($contribution, $this->zdb, $this->preferences);
722 }
723
724 return $this->sendResponse($response, $pdf);
725 }
726 }