3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Galette Mailing controller
10 * Copyright © 2019-2020 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
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.
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.
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/>.
27 * @category Controllers
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
37 namespace Galette\Controllers\Crud
;
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
;
52 * Galette Mailing controller
54 * @category Controllers
55 * @name MailingsController
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
64 class MailingsController
extends CrudController
71 * @param Request $request PSR Request
72 * @param Response $response PSR Response
73 * @param array $args Request arguments
77 public function add(Request
$request, Response
$response, array $args = []): Response
79 $get = $request->getQueryParams();
83 isset($get['mailing_new'])
84 ||
isset($get['reminder'])
86 if ($this->session
->mailing
!== null) {
87 // check for temporary attachments to remove
88 $m = $this->session
->mailing
;
89 $m->removeAttachments(true);
91 $this->session
->mailing
= null;
97 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
98 && !GALETTE_MODE
=== 'DEMO'
101 _T("Trying to load mailing while email is disabled in preferences.")
103 $this->flash
->addMessage(
105 _T("Trying to load mailing while email is disabled in preferences.")
109 ->withHeader('Location', $this->router
->pathFor('slash'));
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
;
116 $filters = new MembersList();
120 $this->session
->mailing
!== null
121 && !isset($get['from'])
122 && !isset($get['reset'])
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!
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);
138 count($filters->selected
) == 0
139 && !isset($get['mailing_new'])
140 && !isset($get['reminder'])
143 '[Mailings] No member selected for mailing',
147 $this->flash
->addMessage(
149 _T('No member selected for mailing!')
152 if (isset($profiler)) {
156 $redirect_url = ($this->session
->redirect_mailing
!== null) ?
157 $this->session
->redirect_mailing
: $this->router
->pathFor('members');
161 ->withHeader('Location', $redirect_url);
164 $members = $m->getArrayList($filters->selected
);
165 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : null);
168 if (isset($get['remove_attachment'])) {
169 $mailing->removeAttachment($get['remove_attachment']);
172 if ($mailing->current_step
!== Mailing
::STEP_SENT
) {
173 $this->session
->mailing
= $mailing;
176 /** TODO: replace that... */
177 $this->session
->labels
= $mailing->unreachables
;
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()
187 $params = array_merge(
190 'mailing' => $mailing,
191 'attachments' => $mailing->attachments
,
192 'html_editor' => true,
193 'html_editor_active' => $this->preferences
->pref_editor_enabled
201 'mailing_adherents.tpl',
204 'page_title' => _T("Mailing")
215 * @param Request $request PSR Request
216 * @param Response $response PSR Response
217 * @param array $args Request arguments
221 public function doAdd(Request
$request, Response
$response, array $args = []): Response
223 $post = $request->getParsedBody();
224 $error_detected = [];
225 $success_detected = [];
227 $goto = $this->router
->pathFor('mailings');
228 $redirect_url = $this->session
->redirect_mailing ??
$this->router
->pathFor('members');
232 isset($post['mailing_done'])
233 ||
isset($post['mailing_cancel'])
235 if ($this->session
->mailing
!== null) {
236 // check for temporary attachments to remove
237 $m = $this->session
->mailing
;
238 $m->removeAttachments(true);
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;
249 ->withHeader('Location', $redirect_url);
255 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
256 && !GALETTE_MODE
=== 'DEMO'
259 _T("Trying to load mailing while email is disabled in preferences.")
261 $error_detected[] = _T("Trying to load mailing while email is disabled in preferences.");
262 $goto = $this->router
->pathFor('slash');
264 if (isset($this->session
->filter_members
)) {
265 $filters = $this->session
->filter_members
;
267 $filters = new MembersList();
271 $this->session
->mailing
!== null
272 && !isset($post['mailing_cancel'])
274 $mailing = $this->session
->mailing
;
276 if (count($filters->selected
) == 0) {
278 '[Mailings] No member selected for mailing',
282 $this->flash
->addMessage(
284 _T('No member selected for mailing!')
289 ->withHeader('Location', $redirect_url);
292 $members = $m->getArrayList($filters->selected
);
293 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : null);
297 isset($post['mailing_go'])
298 ||
isset($post['mailing_reset'])
299 ||
isset($post['mailing_confirm'])
300 ||
isset($post['mailing_save'])
302 if (trim($post['mailing_objet']) == '') {
303 $error_detected[] = _T("Please type an object for the message.");
305 $mailing->subject
= $post['mailing_objet'];
308 if (trim($post['mailing_corps']) == '') {
309 $error_detected[] = _T("Please enter a message.");
311 $mailing->message
= $post['mailing_corps'];
314 switch ($post['sender']) {
315 case GaletteMail
::SENDER_CURRENT
:
316 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
322 case GaletteMail
::SENDER_OTHER
:
324 $post['sender_name'],
325 $post['sender_address']
328 case GaletteMail
::SENDER_PREFS
:
330 //nothing to do; this is the default :)
334 $mailing->html
= (isset($post['mailing_html'])) ?
true : false;
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])) {
344 foreach (array_keys($_FILES['files']) as $key) {
345 $da_file[$key] = $_FILES['files'][$key][$i];
347 $res = $mailing->store($da_file);
349 //what to do if one of attachments fail? should other be removed?
350 $error_detected[] = $mailing->getAttachmentErrorMessage($res);
354 } elseif ($_FILES['files']['error'][$i] !== UPLOAD_ERR_NO_FILE
) {
356 $this->logo
->getPhpErrorMessage($_FILES['files']['error'][$i]),
359 $error_detected[] = $this->logo
->getPhpErrorMessage(
360 $_FILES['files']['error'][$i]
367 count($error_detected) == 0
368 && !isset($post['mailing_reset'])
369 && !isset($post['mailing_save'])
371 $mailing->current_step
= Mailing
::STEP_PREVIEW
;
373 $mailing->current_step
= Mailing
::STEP_START
;
375 //until mail is sent (above), we redirect to mailing page
376 $goto = $this->router
->pathFor('mailing');
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
;
386 '[Mailings] Message was not sent. Errors: ' .
387 print_r($mailing->errors
, true),
390 foreach ($mailing->errors
as $e) {
391 $error_detected[] = $e;
394 $mlh = new MailingHistory($this->zdb
, $this->login
, null, $mailing);
395 $mlh->storeMailing(true);
397 '[Mailings] Message has been sent.',
400 $mailing->current_step
= Mailing
::STEP_SENT
;
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;
410 if ($mailing->current_step
!== Mailing
::STEP_SENT
) {
411 $this->session
->mailing
= $mailing;
414 /** TODO: replace that... */
415 $this->session
->labels
= $mailing->unreachables
;
418 !isset($post['html_editor_active'])
419 ||
trim($post['html_editor_active']) == ''
421 $post['html_editor_active'] = $this->preferences
->pref_editor_enabled
;
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;
434 //flash messages if any
435 if (count($error_detected) > 0) {
436 foreach ($error_detected as $error) {
437 $this->flash
->addMessage('error_detected', $error);
440 if (count($success_detected) > 0) {
441 foreach ($success_detected as $success) {
442 $this->flash
->addMessage('success_detected', $success);
448 ->withHeader('Location', $goto);
455 * Mailings history page
457 * @param Request $request PSR Request
458 * @param Response $response PSR Response
459 * @param array $args Request arguments
463 public function list(Request
$request, Response
$response, array $args = []): Response
466 if (isset($args['option'])) {
467 $option = $args['option'];
471 if (isset($args['value'])) {
472 $value = $args['value'];
475 if (isset($this->session
->filter_mailings
)) {
476 $filters = $this->session
->filter_mailings
;
478 $filters = new MailingsList();
481 if (isset($request->getQueryParams()['nbshow'])) {
482 $filters->show
= $request->getQueryParams()['nbshow'];
485 $mailhist = new MailingHistory($this->zdb
, $this->login
, $filters);
487 if ($option !== null) {
490 $filters->current_page
= (int)$value;
493 $filters->orderby
= $value;
497 //reinitialize object after flush
498 $filters = new MailingsList();
499 $mailhist = new MailingHistory($this->zdb
, $this->login
, $filters);
504 $this->session
->filter_mailings
= $filters;
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());
515 'gestion_mailings.tpl',
517 'page_title' => _T("Mailings"),
518 'logs' => $history_list,
519 'history' => $mailhist
528 * @param Request $request PSR Request
529 * @param Response $response PSR Response
533 public function filter(Request
$request, Response
$response): Response
535 $post = $request->getParsedBody();
536 $error_detected = [];
538 if ($this->session
->filter_mailings
!== null) {
539 $filters = $this->session
->filter_mailings
;
541 $filters = new MailingsList();
544 if (isset($post['clear_filter'])) {
548 (isset($post['nbshow']) && is_numeric($post['nbshow']))
550 $filters->show
= $post['nbshow'];
553 if (isset($post['end_date_filter']) ||
isset($post['start_date_filter'])) {
555 if (isset($post['start_date_filter'])) {
556 $filters->start_date_filter
= $post['start_date_filter'];
558 if (isset($post['end_date_filter'])) {
559 $filters->end_date_filter
= $post['end_date_filter'];
561 } catch (\Exception
$e) {
562 $error_detected[] = $e->getMessage();
566 if (isset($post['sender_filter'])) {
567 $filters->sender_filter
= $post['sender_filter'];
570 if (isset($post['sent_filter'])) {
571 $filters->sent_filter
= $post['sent_filter'];
575 if (isset($post['subject_filter'])) {
576 $filters->subject_filter
= $post['subject_filter'];
580 $this->session
->filter_mailings
= $filters;
582 if (count($error_detected) > 0) {
584 foreach ($error_detected as $error) {
585 $this->flash
->addMessage(
594 ->withHeader('Location', $this->router
->pathFor('mailings'));
600 * @param Request $request PSR Request
601 * @param Response $response PSR Response
602 * @param array $args Request arguments
606 public function edit(Request
$request, Response
$response, array $args = []): Response
614 * @param Request $request PSR Request
615 * @param Response $response PSR Response
616 * @param array $args Request arguments
620 public function doEdit(Request
$request, Response
$response, array $args = []): Response
629 * Get redirection URI
631 * @param array $args Route arguments
635 public function redirectUri(array $args = [])
637 return $this->router
->pathFor('mailings');
643 * @param array $args Route arguments
647 public function formUri(array $args = [])
649 return $this->router
->pathFor(
651 ['id' => $args['id'] ??
null]
656 * Get confirmation removal page title
658 * @param array $args Route arguments
662 public function confirmRemoveTitle(array $args = [])
665 _T('Remove mailing #%1$s'),
673 * @param array $args Route arguments
674 * @param array $post POST values
678 protected function doDelete(array $args, array $post)
680 $mailhist = new MailingHistory($this->zdb
, $this->login
);
681 return $mailhist->removeEntries($args['id'], $this->history
);
689 * @param Request $request PSR Request
690 * @param Response $response PSR Response
691 * @param array $args Request arguments
695 public function preview(Request
$request, Response
$response, array $args = []): Response
697 $post = $request->getParsedBody();
698 // check for ajax mode
702 ||
isset($post['ajax'])
703 && $post['ajax'] == 'true'
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
;
714 $mailing = $this->session
->mailing
;
716 switch ($post['sender']) {
717 case GaletteMail
::SENDER_CURRENT
:
718 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
724 case GaletteMail
::SENDER_OTHER
:
726 $post['sender_name'],
727 $post['sender_address']
730 case GaletteMail
::SENDER_PREFS
:
732 //nothing to do; this is the default :)
736 $mailing->subject
= $post['subject'];
737 $mailing->message
= $post['body'];
738 $mailing->html
= ($post['html'] === 'true');
739 $attachments = (isset($post['attachments']) ?
$post['attachments'] : []);
745 'mailing_preview.tpl',
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() . ' <' .
753 $mailing->getSenderAddress() . '>',
754 'attachments' => $attachments
762 * Preview attachement action
764 * @param Request $request PSR Request
765 * @param Response $response PSR Response
766 * @param array $args Request arguments
770 public function previewAttachment(Request
$request, Response
$response, array $args = []): Response
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();
778 $ext = pathinfo($attachment->getFileName())['extension'];
779 $response = $response->withHeader('Content-type', $attachment->getMimeType($filepath));
781 $body = $response->getBody();
782 $body->write(file_get_contents($filepath));
787 * Set recipients action
789 * @param Request $request PSR Request
790 * @param Response $response PSR Response
791 * @param array $args Request arguments
795 public function setRecipients(Request
$request, Response
$response, array $args = []): Response
797 $post = $request->getParsedBody();
798 $mailing = $this->session
->mailing
;
802 $members = $m->getArrayList(
812 $mailing->setRecipients($members);
814 $this->session
->mailing
= $mailing;
819 'mailing_recipients.tpl',
821 'mailing' => $mailing