]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Controllers/Crud/MailingsController.php
bfa7ae9af5b8738ef92a718fb79f4bcb44cbde0a
[galette.git] / galette / lib / Galette / Controllers / Crud / MailingsController.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette Mailing controller
7 *
8 * PHP version 5
9 *
10 * Copyright © 2019-2020 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 Controllers
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2019-2020 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-06
35 */
36
37 namespace Galette\Controllers\Crud;
38
39 use Galette\Controllers\CrudController;
40 use Slim\Http\Request;
41 use Slim\Http\Response;
42 use Galette\Core\GaletteMail;
43 use Galette\Core\Mailing;
44 use Galette\Core\MailingHistory;
45 use Galette\Entity\Adherent;
46 use Galette\Filters\MailingsList;
47 use Galette\Filters\MembersList;
48 use Galette\Repository\Members;
49 use Analog\Analog;
50
51 /**
52 * Galette Mailing controller
53 *
54 * @category Controllers
55 * @name MailingsController
56 * @package Galette
57 * @author Johan Cwiklinski <johan@x-tnd.be>
58 * @copyright 2019-2020 The Galette Team
59 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
60 * @link http://galette.tuxfamily.org
61 * @since Available since 0.9.4dev - 2019-12-06
62 */
63
64 class MailingsController extends CrudController
65 {
66 // CRUD - Create
67
68 /**
69 * Add page
70 *
71 * @param Request $request PSR Request
72 * @param Response $response PSR Response
73 * @param array $args Request arguments
74 *
75 * @return Response
76 */
77 public function add(Request $request, Response $response, array $args = []): Response
78 {
79 $get = $request->getQueryParams();
80
81 //We're done :-)
82 if (
83 isset($get['mailing_new'])
84 || isset($get['reminder'])
85 ) {
86 if ($this->session->mailing !== null) {
87 // check for temporary attachments to remove
88 $m = $this->session->mailing;
89 $m->removeAttachments(true);
90 }
91 $this->session->mailing = null;
92 }
93
94 $params = array();
95
96 if (
97 $this->preferences->pref_mail_method == Mailing::METHOD_DISABLED
98 && !GALETTE_MODE === 'DEMO'
99 ) {
100 $this->history->add(
101 _T("Trying to load mailing while email is disabled in preferences.")
102 );
103 $this->flash->addMessage(
104 'error_detected',
105 _T("Trying to load mailing while email is disabled in preferences.")
106 );
107 return $response
108 ->withStatus(301)
109 ->withHeader('Location', $this->router->pathFor('slash'));
110 } else {
111 if (isset($this->session->filter_mailing)) {
112 $filters = $this->session->filter_mailing;
113 } elseif (isset($this->session->filter_members)) {
114 $filters = $this->session->filter_members;
115 } else {
116 $filters = new MembersList();
117 }
118
119 if (
120 $this->session->mailing !== null
121 && !isset($get['from'])
122 && !isset($get['reset'])
123 ) {
124 $mailing = $this->session->mailing;
125 } elseif (isset($get['from']) && is_numeric($get['from'])) {
126 $mailing = new Mailing($this->preferences, null, $get['from']);
127 MailingHistory::loadFrom($this->zdb, (int)$get['from'], $mailing);
128 } elseif (isset($get['reminder'])) {
129 //FIXME: use a constant!
130 $filters->reinit();
131 $filters->membership_filter = Members::MEMBERSHIP_LATE;
132 $filters->filter_account = Members::ACTIVE_ACCOUNT;
133 $m = new Members($filters);
134 $members = $m->getList(true);
135 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
136 } else {
137 if (
138 count($filters->selected) == 0
139 && !isset($get['mailing_new'])
140 && !isset($get['reminder'])
141 ) {
142 Analog::log(
143 '[Mailings] No member selected for mailing',
144 Analog::WARNING
145 );
146
147 $this->flash->addMessage(
148 'error_detected',
149 _T('No member selected for mailing!')
150 );
151
152 if (isset($profiler)) {
153 $profiler->stop();
154 }
155
156 $redirect_url = ($this->session->redirect_mailing !== null) ?
157 $this->session->redirect_mailing : $this->router->pathFor('members');
158
159 return $response
160 ->withStatus(301)
161 ->withHeader('Location', $redirect_url);
162 }
163 $m = new Members();
164 $members = $m->getArrayList($filters->selected);
165 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
166 }
167
168 if (isset($get['remove_attachment'])) {
169 $mailing->removeAttachment($get['remove_attachment']);
170 }
171
172 if ($mailing->current_step !== Mailing::STEP_SENT) {
173 $this->session->mailing = $mailing;
174 }
175
176 /** TODO: replace that... */
177 $this->session->labels = $mailing->unreachables;
178
179 if (!$this->login->isSuperAdmin()) {
180 $member = new Adherent($this->zdb, (int)$this->login->id, false);
181 $params['sender_current'] = [
182 'name' => $member->sname,
183 'email' => $member->getEmail()
184 ];
185 }
186
187 $params = array_merge(
188 $params,
189 array(
190 'mailing' => $mailing,
191 'attachments' => $mailing->attachments,
192 'html_editor' => true,
193 'html_editor_active' => $this->preferences->pref_editor_enabled
194 )
195 );
196 }
197
198 // display page
199 $this->view->render(
200 $response,
201 'mailing_adherents.tpl',
202 array_merge(
203 array(
204 'page_title' => _T("Mailing")
205 ),
206 $params
207 )
208 );
209 return $response;
210 }
211
212 /**
213 * Add action
214 *
215 * @param Request $request PSR Request
216 * @param Response $response PSR Response
217 * @param array $args Request arguments
218 *
219 * @return Response
220 */
221 public function doAdd(Request $request, Response $response, array $args = []): Response
222 {
223 $post = $request->getParsedBody();
224 $error_detected = [];
225 $success_detected = [];
226
227 $goto = $this->router->pathFor('mailings');
228 $redirect_url = $this->session->redirect_mailing ?? $this->router->pathFor('members');
229
230 //We're done :-)
231 if (
232 isset($post['mailing_done'])
233 || isset($post['mailing_cancel'])
234 ) {
235 if ($this->session->mailing !== null) {
236 // check for temporary attachments to remove
237 $m = $this->session->mailing;
238 $m->removeAttachments(true);
239 }
240 $this->session->mailing = null;
241 if (isset($this->session->filter_mailing)) {
242 $filters = $this->session->filter_mailing;
243 $filters->selected = [];
244 $this->session->filter_mailing = $filters;
245 }
246
247 return $response
248 ->withStatus(301)
249 ->withHeader('Location', $redirect_url);
250 }
251
252 $params = array();
253
254 if (
255 $this->preferences->pref_mail_method == Mailing::METHOD_DISABLED
256 && !GALETTE_MODE === 'DEMO'
257 ) {
258 $this->history->add(
259 _T("Trying to load mailing while email is disabled in preferences.")
260 );
261 $error_detected[] = _T("Trying to load mailing while email is disabled in preferences.");
262 $goto = $this->router->pathFor('slash');
263 } else {
264 if (isset($this->session->filter_members)) {
265 $filters = $this->session->filter_members;
266 } else {
267 $filters = new MembersList();
268 }
269
270 if (
271 $this->session->mailing !== null
272 && !isset($post['mailing_cancel'])
273 ) {
274 $mailing = $this->session->mailing;
275 } else {
276 if (count($filters->selected) == 0) {
277 Analog::log(
278 '[Mailings] No member selected for mailing',
279 Analog::WARNING
280 );
281
282 $this->flash->addMessage(
283 'error_detected',
284 _T('No member selected for mailing!')
285 );
286
287 return $response
288 ->withStatus(301)
289 ->withHeader('Location', $redirect_url);
290 }
291 $m = new Members();
292 $members = $m->getArrayList($filters->selected);
293 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
294 }
295
296 if (
297 isset($post['mailing_go'])
298 || isset($post['mailing_reset'])
299 || isset($post['mailing_confirm'])
300 || isset($post['mailing_save'])
301 ) {
302 if (trim($post['mailing_objet']) == '') {
303 $error_detected[] = _T("Please type an object for the message.");
304 } else {
305 $mailing->subject = $post['mailing_objet'];
306 }
307
308 if (trim($post['mailing_corps']) == '') {
309 $error_detected[] = _T("Please enter a message.");
310 } else {
311 $mailing->message = $post['mailing_corps'];
312 }
313
314 switch ($post['sender']) {
315 case GaletteMail::SENDER_CURRENT:
316 $member = new Adherent($this->zdb, (int)$this->login->id, false);
317 $mailing->setSender(
318 $member->sname,
319 $member->getEmail()
320 );
321 break;
322 case GaletteMail::SENDER_OTHER:
323 $mailing->setSender(
324 $post['sender_name'],
325 $post['sender_address']
326 );
327 break;
328 case GaletteMail::SENDER_PREFS:
329 default:
330 //nothing to do; this is the default :)
331 break;
332 }
333
334 $mailing->html = (isset($post['mailing_html'])) ? true : false;
335
336 //handle attachments
337 if (isset($_FILES['files'])) {
338 $cnt_files = count($_FILES['files']['name']);
339 for ($i = 0; $i < $cnt_files; $i++) {
340 if ($_FILES['files']['error'][$i] === UPLOAD_ERR_OK) {
341 if ($_FILES['files']['tmp_name'][$i] != '') {
342 if (is_uploaded_file($_FILES['files']['tmp_name'][$i])) {
343 $da_file = array();
344 foreach (array_keys($_FILES['files']) as $key) {
345 $da_file[$key] = $_FILES['files'][$key][$i];
346 }
347 $res = $mailing->store($da_file);
348 if ($res < 0) {
349 //what to do if one of attachments fail? should other be removed?
350 $error_detected[] = $mailing->getAttachmentErrorMessage($res);
351 }
352 }
353 }
354 } elseif ($_FILES['files']['error'][$i] !== UPLOAD_ERR_NO_FILE) {
355 Analog::log(
356 $this->logo->getPhpErrorMessage($_FILES['files']['error'][$i]),
357 Analog::WARNING
358 );
359 $error_detected[] = $this->logo->getPhpErrorMessage(
360 $_FILES['files']['error'][$i]
361 );
362 }
363 }
364 }
365
366 if (
367 count($error_detected) == 0
368 && !isset($post['mailing_reset'])
369 && !isset($post['mailing_save'])
370 ) {
371 $mailing->current_step = Mailing::STEP_PREVIEW;
372 } else {
373 $mailing->current_step = Mailing::STEP_START;
374 }
375 //until mail is sent (above), we redirect to mailing page
376 $goto = $this->router->pathFor('mailing');
377 }
378
379 if (isset($post['mailing_confirm']) && count($error_detected) == 0) {
380 $mailing->current_step = Mailing::STEP_SEND;
381 //ok... let's go for fun
382 $sent = $mailing->send();
383 if ($sent == Mailing::MAIL_ERROR) {
384 $mailing->current_step = Mailing::STEP_START;
385 Analog::log(
386 '[Mailings] Message was not sent. Errors: ' .
387 print_r($mailing->errors, true),
388 Analog::ERROR
389 );
390 foreach ($mailing->errors as $e) {
391 $error_detected[] = $e;
392 }
393 } else {
394 $mlh = new MailingHistory($this->zdb, $this->login, null, $mailing);
395 $mlh->storeMailing(true);
396 Analog::log(
397 '[Mailings] Message has been sent.',
398 Analog::INFO
399 );
400 $mailing->current_step = Mailing::STEP_SENT;
401 //cleanup
402 $filters->selected = null;
403 $this->session->filter_members = $filters;
404 $this->session->mailing = null;
405 $success_detected[] = _T("Mailing has been successfully sent!");
406 $goto = $redirect_url;
407 }
408 }
409
410 if ($mailing->current_step !== Mailing::STEP_SENT) {
411 $this->session->mailing = $mailing;
412 }
413
414 /** TODO: replace that... */
415 $this->session->labels = $mailing->unreachables;
416
417 if (
418 !isset($post['html_editor_active'])
419 || trim($post['html_editor_active']) == ''
420 ) {
421 $post['html_editor_active'] = $this->preferences->pref_editor_enabled;
422 }
423
424 if (isset($post['mailing_save'])) {
425 //user requested to save the mailing
426 $histo = new MailingHistory($this->zdb, $this->login, null, $mailing);
427 if ($histo->storeMailing() !== false) {
428 $success_detected[] = _T("Mailing has been successfully saved.");
429 $this->session->mailing = null;
430 }
431 }
432 }
433
434 //flash messages if any
435 if (count($error_detected) > 0) {
436 foreach ($error_detected as $error) {
437 $this->flash->addMessage('error_detected', $error);
438 }
439 }
440 if (count($success_detected) > 0) {
441 foreach ($success_detected as $success) {
442 $this->flash->addMessage('success_detected', $success);
443 }
444 }
445
446 return $response
447 ->withStatus(301)
448 ->withHeader('Location', $goto);
449 }
450
451 // /CRUD - Create
452 // CRUD - Read
453
454 /**
455 * Mailings history page
456 *
457 * @param Request $request PSR Request
458 * @param Response $response PSR Response
459 * @param array $args Request arguments
460 *
461 * @return Response
462 */
463 public function list(Request $request, Response $response, array $args = []): Response
464 {
465 $option = null;
466 if (isset($args['option'])) {
467 $option = $args['option'];
468 }
469
470 $value = null;
471 if (isset($args['value'])) {
472 $value = $args['value'];
473 }
474
475 if (isset($this->session->filter_mailings)) {
476 $filters = $this->session->filter_mailings;
477 } else {
478 $filters = new MailingsList();
479 }
480
481 if (isset($request->getQueryParams()['nbshow'])) {
482 $filters->show = $request->getQueryParams()['nbshow'];
483 }
484
485 $mailhist = new MailingHistory($this->zdb, $this->login, $filters);
486
487 if ($option !== null) {
488 switch ($option) {
489 case 'page':
490 $filters->current_page = (int)$value;
491 break;
492 case 'order':
493 $filters->orderby = $value;
494 break;
495 case 'reset':
496 $mailhist->clean();
497 //reinitialize object after flush
498 $filters = new MailingsList();
499 $mailhist = new MailingHistory($this->zdb, $this->login, $filters);
500 break;
501 }
502 }
503
504 $this->session->filter_mailings = $filters;
505
506 //assign pagination variables to the template and add pagination links
507 $mailhist->filters->setSmartyPagination($this->router, $this->view->getSmarty());
508 $history_list = $mailhist->getHistory();
509 //assign pagination variables to the template and add pagination links
510 $mailhist->filters->setSmartyPagination($this->router, $this->view->getSmarty());
511
512 // display page
513 $this->view->render(
514 $response,
515 'gestion_mailings.tpl',
516 array(
517 'page_title' => _T("Mailings"),
518 'logs' => $history_list,
519 'history' => $mailhist
520 )
521 );
522 return $response;
523 }
524
525 /**
526 * Mailings filtering
527 *
528 * @param Request $request PSR Request
529 * @param Response $response PSR Response
530 *
531 * @return Response
532 */
533 public function filter(Request $request, Response $response): Response
534 {
535 $post = $request->getParsedBody();
536 $error_detected = [];
537
538 if ($this->session->filter_mailings !== null) {
539 $filters = $this->session->filter_mailings;
540 } else {
541 $filters = new MailingsList();
542 }
543
544 if (isset($post['clear_filter'])) {
545 $filters->reinit();
546 } else {
547 if (
548 (isset($post['nbshow']) && is_numeric($post['nbshow']))
549 ) {
550 $filters->show = $post['nbshow'];
551 }
552
553 if (isset($post['end_date_filter']) || isset($post['start_date_filter'])) {
554 try {
555 if (isset($post['start_date_filter'])) {
556 $filters->start_date_filter = $post['start_date_filter'];
557 }
558 if (isset($post['end_date_filter'])) {
559 $filters->end_date_filter = $post['end_date_filter'];
560 }
561 } catch (\Exception $e) {
562 $error_detected[] = $e->getMessage();
563 }
564 }
565
566 if (isset($post['sender_filter'])) {
567 $filters->sender_filter = $post['sender_filter'];
568 }
569
570 if (isset($post['sent_filter'])) {
571 $filters->sent_filter = $post['sent_filter'];
572 }
573
574
575 if (isset($post['subject_filter'])) {
576 $filters->subject_filter = $post['subject_filter'];
577 }
578 }
579
580 $this->session->filter_mailings = $filters;
581
582 if (count($error_detected) > 0) {
583 //report errors
584 foreach ($error_detected as $error) {
585 $this->flash->addMessage(
586 'error_detected',
587 $error
588 );
589 }
590 }
591
592 return $response
593 ->withStatus(301)
594 ->withHeader('Location', $this->router->pathFor('mailings'));
595 }
596
597 /**
598 * Edit page
599 *
600 * @param Request $request PSR Request
601 * @param Response $response PSR Response
602 * @param array $args Request arguments
603 *
604 * @return Response
605 */
606 public function edit(Request $request, Response $response, array $args = []): Response
607 {
608 //TODO
609 }
610
611 /**
612 * Edit action
613 *
614 * @param Request $request PSR Request
615 * @param Response $response PSR Response
616 * @param array $args Request arguments
617 *
618 * @return Response
619 */
620 public function doEdit(Request $request, Response $response, array $args = []): Response
621 {
622 //TODO
623 }
624
625 // /CRUD - Update
626 // CRUD - Delete
627
628 /**
629 * Get redirection URI
630 *
631 * @param array $args Route arguments
632 *
633 * @return string
634 */
635 public function redirectUri(array $args = [])
636 {
637 return $this->router->pathFor('mailings');
638 }
639
640 /**
641 * Get form URI
642 *
643 * @param array $args Route arguments
644 *
645 * @return string
646 */
647 public function formUri(array $args = [])
648 {
649 return $this->router->pathFor(
650 'doRemoveMailing',
651 ['id' => $args['id'] ?? null]
652 );
653 }
654
655 /**
656 * Get confirmation removal page title
657 *
658 * @param array $args Route arguments
659 *
660 * @return string
661 */
662 public function confirmRemoveTitle(array $args = [])
663 {
664 return sprintf(
665 _T('Remove mailing #%1$s'),
666 $args['id'] ?? ''
667 );
668 }
669
670 /**
671 * Remove object
672 *
673 * @param array $args Route arguments
674 * @param array $post POST values
675 *
676 * @return boolean
677 */
678 protected function doDelete(array $args, array $post)
679 {
680 $mailhist = new MailingHistory($this->zdb, $this->login);
681 return $mailhist->removeEntries($args['id'], $this->history);
682 }
683 // /CRUD - Delete
684 // /CRUD
685
686 /**
687 * Preview action
688 *
689 * @param Request $request PSR Request
690 * @param Response $response PSR Response
691 * @param array $args Request arguments
692 *
693 * @return Response
694 */
695 public function preview(Request $request, Response $response, array $args = []): Response
696 {
697 $post = $request->getParsedBody();
698 // check for ajax mode
699 $ajax = false;
700 if (
701 $request->isXhr()
702 || isset($post['ajax'])
703 && $post['ajax'] == 'true'
704 ) {
705 $ajax = true;
706 }
707
708 $mailing = null;
709 if (isset($args['id'])) {
710 $mailing = new Mailing($this->preferences, null);
711 MailingHistory::loadFrom($this->zdb, (int)$args['id'], $mailing, false);
712 $attachments = $mailing->attachments;
713 } else {
714 $mailing = $this->session->mailing;
715
716 switch ($post['sender']) {
717 case GaletteMail::SENDER_CURRENT:
718 $member = new Adherent($this->zdb, (int)$this->login->id, false);
719 $mailing->setSender(
720 $member->sname,
721 $member->getEmail()
722 );
723 break;
724 case GaletteMail::SENDER_OTHER:
725 $mailing->setSender(
726 $post['sender_name'],
727 $post['sender_address']
728 );
729 break;
730 case GaletteMail::SENDER_PREFS:
731 default:
732 //nothing to do; this is the default :)
733 break;
734 }
735
736 $mailing->subject = $post['subject'];
737 $mailing->message = $post['body'];
738 $mailing->html = ($post['html'] === 'true');
739 $attachments = (isset($post['attachments']) ? $post['attachments'] : []);
740 }
741
742 // display page
743 $this->view->render(
744 $response,
745 'mailing_preview.tpl',
746 [
747 'page_title' => _T("Mailing preview"),
748 'mailing_id' => $args['id'],
749 'mode' => ($ajax ? 'ajax' : ''),
750 'mailing' => $mailing,
751 'recipients' => $mailing->recipients,
752 'sender' => $mailing->getSenderName() . ' &lt;' .
753 $mailing->getSenderAddress() . '&gt;',
754 'attachments' => $attachments
755
756 ]
757 );
758 return $response;
759 }
760
761 /**
762 * Preview attachement action
763 *
764 * @param Request $request PSR Request
765 * @param Response $response PSR Response
766 * @param array $args Request arguments
767 *
768 * @return Response
769 */
770 public function previewAttachment(Request $request, Response $response, array $args = []): Response
771 {
772 $mailing = new Mailing($this->preferences, null);
773 MailingHistory::loadFrom($this->zdb, (int)$args['id'], $mailing, false);
774 $attachments = $mailing->attachments;
775 $attachment = $attachments[$args['pos']];
776 $filepath = $attachment->getDestDir() . $attachment->getFileName();
777
778 $ext = pathinfo($attachment->getFileName())['extension'];
779 $response = $response->withHeader('Content-type', $attachment->getMimeType($filepath));
780
781 $body = $response->getBody();
782 $body->write(file_get_contents($filepath));
783 return $response;
784 }
785
786 /**
787 * Set recipients action
788 *
789 * @param Request $request PSR Request
790 * @param Response $response PSR Response
791 * @param array $args Request arguments
792 *
793 * @return Response
794 */
795 public function setRecipients(Request $request, Response $response, array $args = []): Response
796 {
797 $post = $request->getParsedBody();
798 $mailing = $this->session->mailing;
799
800 $m = new Members();
801
802 $members = $m->getArrayList(
803 $post['recipients'],
804 null,
805 false,
806 true,
807 null,
808 false,
809 false,
810 true
811 );
812 $mailing->setRecipients($members);
813
814 $this->session->mailing = $mailing;
815
816 // display page
817 $this->view->render(
818 $response,
819 'mailing_recipients.tpl',
820 [
821 'mailing' => $mailing
822
823 ]
824 );
825 return $response;
826 }
827 }