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
;
40 use Galette\Controllers\CrudController
;
41 use Slim\Http\Request
;
42 use Slim\Http\Response
;
43 use Galette\Core\GaletteMail
;
44 use Galette\Core\Mailing
;
45 use Galette\Core\MailingHistory
;
46 use Galette\Entity\Adherent
;
47 use Galette\Filters\MailingsList
;
48 use Galette\Filters\MembersList
;
49 use Galette\Repository\Members
;
53 * Galette Mailing controller
55 * @category Controllers
56 * @name MailingsController
58 * @author Johan Cwiklinski <johan@x-tnd.be>
59 * @copyright 2019-2020 The Galette Team
60 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
61 * @link http://galette.tuxfamily.org
62 * @since Available since 0.9.4dev - 2019-12-06
65 class MailingsController
extends CrudController
72 * @param Request $request PSR Request
73 * @param Response $response PSR Response
77 public function add(Request
$request, Response
$response): 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;
92 $this->session
->redirect_mailing
= null;
98 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
99 && !GALETTE_MODE
=== 'DEMO'
102 _T("Trying to load mailing while email is disabled in preferences.")
104 $this->flash
->addMessage(
106 _T("Trying to load mailing while email is disabled in preferences.")
110 ->withHeader('Location', $this->router
->pathFor('slash'));
112 if (isset($this->session
->filter_mailing
)) {
113 $filters = $this->session
->filter_mailing
;
114 } elseif (isset($this->session
->filter_members
)) {
115 $filters = $this->session
->filter_members
;
117 $filters = new MembersList();
121 $this->session
->mailing
!== null
122 && !isset($get['from'])
123 && !isset($get['reset'])
125 $mailing = $this->session
->mailing
;
126 } elseif (isset($get['from']) && is_numeric($get['from'])) {
127 $mailing = new Mailing($this->preferences
, [], (int)$get['from']);
128 MailingHistory
::loadFrom($this->zdb
, (int)$get['from'], $mailing);
129 } elseif (isset($get['reminder'])) {
130 //FIXME: use a constant!
132 $filters->membership_filter
= Members
::MEMBERSHIP_LATE
;
133 $filters->filter_account
= Members
::ACTIVE_ACCOUNT
;
134 $m = new Members($filters);
135 $members = $m->getList(true);
136 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : []);
139 count($filters->selected
) == 0
140 && !isset($get['mailing_new'])
141 && !isset($get['reminder'])
144 '[Mailings] No member selected for mailing',
148 $this->flash
->addMessage(
150 _T('No member selected for mailing!')
153 if (isset($profiler)) {
157 $redirect_url = ($this->session
->redirect_mailing
!== null) ?
158 $this->session
->redirect_mailing
: $this->router
->pathFor('members');
162 ->withHeader('Location', $redirect_url);
165 $members = $m->getArrayList($filters->selected
);
166 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : []);
169 if (isset($get['remove_attachment'])) {
170 $mailing->removeAttachment($get['remove_attachment']);
173 if ($mailing->current_step
!== Mailing
::STEP_SENT
) {
174 $this->session
->mailing
= $mailing;
177 /** TODO: replace that... */
178 $this->session
->labels
= $mailing->unreachables
;
180 if (!$this->login
->isSuperAdmin()) {
181 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
182 $params['sender_current'] = [
183 'name' => $member->sname
,
184 'email' => $member->getEmail()
188 $params = array_merge(
191 'mailing' => $mailing,
192 'attachments' => $mailing->attachments
,
193 'html_editor' => true,
194 'html_editor_active' => $this->preferences
->pref_editor_enabled
202 'mailing_adherents.tpl',
205 'page_title' => _T("Mailing")
216 * @param Request $request PSR Request
217 * @param Response $response PSR Response
221 public function doAdd(Request
$request, Response
$response): 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 $this->session
->redirect_mailing
= null;
242 if (isset($this->session
->filter_mailing
)) {
243 $filters = $this->session
->filter_mailing
;
244 $filters->selected
= [];
245 $this->session
->filter_mailing
= $filters;
250 ->withHeader('Location', $redirect_url);
254 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
255 && !GALETTE_MODE
=== 'DEMO'
258 _T("Trying to load mailing while email is disabled in preferences.")
260 $error_detected[] = _T("Trying to load mailing while email is disabled in preferences.");
261 $goto = $this->router
->pathFor('slash');
263 if (isset($this->session
->filter_members
)) {
264 $filters = $this->session
->filter_members
;
266 $filters = new MembersList();
270 $this->session
->mailing
!== null
271 && !isset($post['mailing_cancel'])
273 $mailing = $this->session
->mailing
;
275 if (count($filters->selected
) == 0) {
277 '[Mailings] No member selected for mailing',
281 $this->flash
->addMessage(
283 _T('No member selected for mailing!')
288 ->withHeader('Location', $redirect_url);
291 $members = $m->getArrayList($filters->selected
);
292 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : null);
296 isset($post['mailing_go'])
297 ||
isset($post['mailing_reset'])
298 ||
isset($post['mailing_confirm'])
299 ||
isset($post['mailing_save'])
301 if (trim($post['mailing_objet']) == '') {
302 $error_detected[] = _T("Please type an object for the message.");
304 $mailing->subject
= $post['mailing_objet'];
307 if (trim($post['mailing_corps']) == '') {
308 $error_detected[] = _T("Please enter a message.");
310 $mailing->message
= $post['mailing_corps'];
313 switch ($post['sender'] ??
false) {
314 case GaletteMail
::SENDER_CURRENT
:
315 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
321 case GaletteMail
::SENDER_OTHER
:
323 $post['sender_name'],
324 $post['sender_address']
327 case GaletteMail
::SENDER_PREFS
:
329 //nothing to do; this is the default :)
333 $mailing->html
= (isset($post['mailing_html'])) ?
true : false;
336 if (isset($_FILES['attachment'])) {
337 $cnt_files = count($_FILES['attachment']['name']);
338 for ($i = 0; $i < $cnt_files; $i++
) {
339 if ($_FILES['attachment']['error'][$i] === UPLOAD_ERR_OK
) {
340 if ($_FILES['attachment']['tmp_name'][$i] != '') {
341 if (is_uploaded_file($_FILES['attachment']['tmp_name'][$i])) {
343 foreach (array_keys($_FILES['attachment']) as $key) {
344 $da_file[$key] = $_FILES['attachment'][$key][$i];
346 $res = $mailing->store($da_file);
348 //what to do if one of attachments fail? should other be removed?
349 $error_detected[] = $mailing->getAttachmentErrorMessage($res);
353 } elseif ($_FILES['attachment']['error'][$i] !== UPLOAD_ERR_NO_FILE
) {
355 $this->logo
->getPhpErrorMessage($_FILES['attachment']['error'][$i]),
358 $error_detected[] = $this->logo
->getPhpErrorMessage(
359 $_FILES['attachment']['error'][$i]
366 count($error_detected) == 0
367 && !isset($post['mailing_reset'])
368 && !isset($post['mailing_save'])
370 $mailing->current_step
= Mailing
::STEP_PREVIEW
;
372 $mailing->current_step
= Mailing
::STEP_START
;
374 //until mail is sent (above), we redirect to mailing page
375 $goto = $this->router
->pathFor('mailing');
378 if (isset($post['mailing_confirm']) && count($error_detected) == 0) {
379 $mailing->current_step
= Mailing
::STEP_SEND
;
380 //ok... let's go for fun
381 $sent = $mailing->send();
382 if ($sent == Mailing
::MAIL_ERROR
) {
383 $mailing->current_step
= Mailing
::STEP_START
;
385 '[Mailings] Message was not sent. Errors: ' .
386 print_r($mailing->errors
, true),
389 foreach ($mailing->errors
as $e) {
390 $error_detected[] = $e;
393 $mlh = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, null, $mailing);
394 $mlh->storeMailing(true);
396 '[Mailings] Message has been sent.',
399 $mailing->current_step
= Mailing
::STEP_SENT
;
401 $filters->selected
= null;
402 $this->session
->filter_members
= $filters;
403 $this->session
->mailing
= null;
404 $this->session
->redirect_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
, $this->preferences
, null, $mailing);
427 if ($histo->storeMailing() !== false) {
428 $success_detected[] = _T("Mailing has been successfully saved.");
429 $this->session
->mailing
= null;
430 $this->session
->redirect_mailing
= null;
431 $goto = $this->router
->pathFor('mailings');
436 //flash messages if any
437 if (count($error_detected) > 0) {
438 foreach ($error_detected as $error) {
439 $this->flash
->addMessage('error_detected', $error);
442 if (count($success_detected) > 0) {
443 foreach ($success_detected as $success) {
444 $this->flash
->addMessage('success_detected', $success);
450 ->withHeader('Location', $goto);
457 * Mailings history page
459 * @param Request $request PSR Request
460 * @param Response $response PSR Response
461 * @param string $option One of 'page' or 'order'
462 * @param string|integer $value Value of the option
466 public function list(Request
$request, Response
$response, $option = null, $value = null): Response
468 if (isset($this->session
->filter_mailings
)) {
469 $filters = $this->session
->filter_mailings
;
471 $filters = new MailingsList();
474 if (isset($request->getQueryParams()['nbshow'])) {
475 $filters->show
= $request->getQueryParams()['nbshow'];
478 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, $filters);
480 if ($option !== null) {
483 $filters->current_page
= (int)$value;
486 $filters->orderby
= $value;
490 //reinitialize object after flush
491 $filters = new MailingsList();
492 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, $filters);
497 $this->session
->filter_mailings
= $filters;
499 //assign pagination variables to the template and add pagination links
500 $mailhist->filters
->setSmartyPagination($this->router
, $this->view
->getSmarty());
501 $history_list = $mailhist->getHistory();
502 //assign pagination variables to the template and add pagination links
503 $mailhist->filters
->setSmartyPagination($this->router
, $this->view
->getSmarty());
508 'gestion_mailings.tpl',
510 'page_title' => _T("Mailings"),
511 'logs' => $history_list,
512 'history' => $mailhist
521 * @param Request $request PSR Request
522 * @param Response $response PSR Response
526 public function filter(Request
$request, Response
$response): Response
528 $post = $request->getParsedBody();
529 $error_detected = [];
531 if ($this->session
->filter_mailings
!== null) {
532 $filters = $this->session
->filter_mailings
;
534 $filters = new MailingsList();
537 if (isset($post['clear_filter'])) {
541 (isset($post['nbshow']) && is_numeric($post['nbshow']))
543 $filters->show
= $post['nbshow'];
546 if (isset($post['end_date_filter']) ||
isset($post['start_date_filter'])) {
548 if (isset($post['start_date_filter'])) {
549 $filters->start_date_filter
= $post['start_date_filter'];
551 if (isset($post['end_date_filter'])) {
552 $filters->end_date_filter
= $post['end_date_filter'];
554 } catch (Throwable
$e) {
555 $error_detected[] = $e->getMessage();
559 if (isset($post['sender_filter'])) {
560 $filters->sender_filter
= $post['sender_filter'];
563 if (isset($post['sent_filter'])) {
564 $filters->sent_filter
= $post['sent_filter'];
568 if (isset($post['subject_filter'])) {
569 $filters->subject_filter
= $post['subject_filter'];
573 $this->session
->filter_mailings
= $filters;
575 if (count($error_detected) > 0) {
577 foreach ($error_detected as $error) {
578 $this->flash
->addMessage(
587 ->withHeader('Location', $this->router
->pathFor('mailings'));
593 * @param Request $request PSR Request
594 * @param Response $response PSR Response
595 * @param integer $id Record id
599 public function edit(Request
$request, Response
$response, int $id): Response
601 //no edit page, just to satisfy inheritance
607 * @param Request $request PSR Request
608 * @param Response $response PSR Response
609 * @param integer $id Record id
613 public function doEdit(Request
$request, Response
$response, int $id): Response
615 //no edit page, just to satisfy inheritance
622 * Get redirection URI
624 * @param array $args Route arguments
628 public function redirectUri(array $args)
630 return $this->router
->pathFor('mailings');
636 * @param array $args Route arguments
640 public function formUri(array $args)
642 return $this->router
->pathFor(
644 ['id' => $args['id'] ??
null]
649 * Get confirmation removal page title
651 * @param array $args Route arguments
655 public function confirmRemoveTitle(array $args)
658 _T('Remove mailing #%1$s'),
666 * @param array $args Route arguments
667 * @param array $post POST values
671 protected function doDelete(array $args, array $post)
673 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
);
674 return $mailhist->removeEntries($args['id'], $this->history
);
682 * @param Request $request PSR Request
683 * @param Response $response PSR Response
684 * @param integer $id Mailing id
688 public function preview(Request
$request, Response
$response, int $id = null): Response
690 $post = $request->getParsedBody();
691 // check for ajax mode
695 ||
isset($post['ajax'])
696 && $post['ajax'] == 'true'
703 $mailing = new Mailing($this->preferences
);
704 MailingHistory
::loadFrom($this->zdb
, $id, $mailing, false);
705 $attachments = $mailing->attachments
;
707 $mailing = $this->session
->mailing
;
709 switch ($post['sender']) {
710 case GaletteMail
::SENDER_CURRENT
:
711 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
717 case GaletteMail
::SENDER_OTHER
:
719 $post['sender_name'],
720 $post['sender_address']
723 case GaletteMail
::SENDER_PREFS
:
725 //nothing to do; this is the default :)
729 $mailing->subject
= $post['subject'];
730 $mailing->message
= $post['body'];
731 $mailing->html
= ($post['html'] === 'true');
732 $attachments = $mailing->attachments
;
738 'mailing_preview.tpl',
740 'page_title' => _T("Mailing preview"),
742 'mode' => ($ajax ?
'ajax' : ''),
743 'mailing' => $mailing,
744 'recipients' => $mailing->recipients
,
745 'sender' => $mailing->getSenderName() . ' <' .
746 $mailing->getSenderAddress() . '>',
747 'attachments' => $attachments
755 * Preview attachement action
757 * @param Request $request PSR Request
758 * @param Response $response PSR Response
759 * @param integer $id Mailiung id
760 * @param integer $pos Attachement position in list
764 public function previewAttachment(Request
$request, Response
$response, int $id, $pos): Response
766 $mailing = new Mailing($this->preferences
);
767 MailingHistory
::loadFrom($this->zdb
, $id, $mailing, false);
768 $attachments = $mailing->attachments
;
769 $attachment = $attachments[$pos];
770 $filepath = $attachment->getDestDir() . $attachment->getFileName();
772 $response = $response->withHeader('Content-type', $attachment->getMimeType($filepath));
774 $body = $response->getBody();
775 $body->write(file_get_contents($filepath));
780 * Set recipients action
782 * @param Request $request PSR Request
783 * @param Response $response PSR Response
787 public function setRecipients(Request
$request, Response
$response): Response
789 $post = $request->getParsedBody();
790 $mailing = $this->session
->mailing
;
794 $members = $m->getArrayList(
804 $mailing->setRecipients($members);
806 $this->session
->mailing
= $mailing;
811 'mailing_recipients.tpl',
813 'mailing' => $mailing