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