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 Galette\Core\Galette
;
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
;
54 * Galette Mailing controller
56 * @category Controllers
57 * @name MailingsController
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
66 class MailingsController
extends CrudController
73 * @param Request $request PSR Request
74 * @param Response $response PSR Response
78 public function add(Request
$request, Response
$response): Response
80 $get = $request->getQueryParams();
84 isset($get['mailing_new'])
85 ||
isset($get['reminder'])
87 if ($this->session
->mailing
!== null) {
88 // check for temporary attachments to remove
89 $m = $this->session
->mailing
;
90 $m->removeAttachments(true);
92 $this->session
->mailing
= null;
93 $this->session
->redirect_mailing
= null;
99 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
100 && !GALETTE_MODE
=== Galette
::MODE_DEMO
103 _T("Trying to load mailing while email is disabled in preferences.")
105 $this->flash
->addMessage(
107 _T("Trying to load mailing while email is disabled in preferences.")
111 ->withHeader('Location', $this->router
->pathFor('slash'));
113 if (isset($this->session
->filter_mailing
)) {
114 $filters = $this->session
->filter_mailing
;
115 } elseif (isset($this->session
->filter_members
)) {
116 $filters = $this->session
->filter_members
;
118 $filters = new MembersList();
122 $this->session
->mailing
!== null
123 && !isset($get['from'])
124 && !isset($get['reset'])
126 $mailing = $this->session
->mailing
;
127 } elseif (isset($get['from']) && is_numeric($get['from'])) {
128 $mailing = new Mailing($this->preferences
, [], (int)$get['from']);
129 MailingHistory
::loadFrom($this->zdb
, (int)$get['from'], $mailing);
130 } elseif (isset($get['reminder'])) {
131 //FIXME: use a constant!
133 $filters->membership_filter
= Members
::MEMBERSHIP_LATE
;
134 $filters->filter_account
= Members
::ACTIVE_ACCOUNT
;
135 $m = new Members($filters);
136 $members = $m->getList(true);
137 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : []);
140 count($filters->selected
) == 0
141 && !isset($get['mailing_new'])
142 && !isset($get['reminder'])
145 '[Mailings] No member selected for mailing',
149 $this->flash
->addMessage(
151 _T('No member selected for mailing!')
154 if (isset($profiler)) {
158 $redirect_url = ($this->session
->redirect_mailing
!== null) ?
159 $this->session
->redirect_mailing
: $this->router
->pathFor('members');
163 ->withHeader('Location', $redirect_url);
166 $members = $m->getArrayList($filters->selected
);
167 $mailing = new Mailing($this->preferences
, ($members !== false) ?
$members : []);
170 if (isset($get['remove_attachment'])) {
171 $mailing->removeAttachment($get['remove_attachment']);
174 if ($mailing->current_step
!== Mailing
::STEP_SENT
) {
175 $this->session
->mailing
= $mailing;
178 /** TODO: replace that... */
179 $this->session
->labels
= $mailing->unreachables
;
181 if (!$this->login
->isSuperAdmin()) {
182 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
183 $params['sender_current'] = [
184 'name' => $member->sname
,
185 'email' => $member->getEmail()
189 $params = array_merge(
192 'mailing' => $mailing,
193 'attachments' => $mailing->attachments
,
194 'html_editor' => true,
195 'html_editor_active' => $this->preferences
->pref_editor_enabled
203 'mailing_adherents.tpl',
206 'page_title' => _T("Mailing")
217 * @param Request $request PSR Request
218 * @param Response $response PSR Response
222 public function doAdd(Request
$request, Response
$response): Response
224 $post = $request->getParsedBody();
225 $error_detected = [];
226 $success_detected = [];
228 $goto = $this->router
->pathFor('mailings');
229 $redirect_url = $this->session
->redirect_mailing ??
$this->router
->pathFor('members');
233 isset($post['mailing_done'])
234 ||
isset($post['mailing_cancel'])
236 if ($this->session
->mailing
!== null) {
237 // check for temporary attachments to remove
238 $m = $this->session
->mailing
;
239 $m->removeAttachments(true);
241 $this->session
->mailing
= null;
242 $this->session
->redirect_mailing
= null;
243 if (isset($this->session
->filter_mailing
)) {
244 $filters = $this->session
->filter_mailing
;
245 $filters->selected
= [];
246 $this->session
->filter_mailing
= $filters;
251 ->withHeader('Location', $redirect_url);
255 $this->preferences
->pref_mail_method
== Mailing
::METHOD_DISABLED
256 && !GALETTE_MODE
=== 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'] ??
false) {
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['attachment'])) {
338 $cnt_files = count($_FILES['attachment']['name']);
339 for ($i = 0; $i < $cnt_files; $i++
) {
340 if ($_FILES['attachment']['error'][$i] === UPLOAD_ERR_OK
) {
341 if ($_FILES['attachment']['tmp_name'][$i] != '') {
342 if (is_uploaded_file($_FILES['attachment']['tmp_name'][$i])) {
344 foreach (array_keys($_FILES['attachment']) as $key) {
345 $da_file[$key] = $_FILES['attachment'][$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['attachment']['error'][$i] !== UPLOAD_ERR_NO_FILE
) {
356 $this->logo
->getPhpErrorMessage($_FILES['attachment']['error'][$i]),
359 $error_detected[] = $this->logo
->getPhpErrorMessage(
360 $_FILES['attachment']['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
, $this->preferences
, 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 $this->session
->redirect_mailing
= null;
406 $success_detected[] = _T("Mailing has been successfully sent!");
407 $goto = $redirect_url;
411 if ($mailing->current_step
!== Mailing
::STEP_SENT
) {
412 $this->session
->mailing
= $mailing;
415 /** TODO: replace that... */
416 $this->session
->labels
= $mailing->unreachables
;
419 !isset($post['html_editor_active'])
420 ||
trim($post['html_editor_active']) == ''
422 $post['html_editor_active'] = $this->preferences
->pref_editor_enabled
;
425 if (isset($post['mailing_save'])) {
426 //user requested to save the mailing
427 $histo = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, null, $mailing);
428 if ($histo->storeMailing() !== false) {
429 $success_detected[] = _T("Mailing has been successfully saved.");
430 $this->session
->mailing
= null;
431 $this->session
->redirect_mailing
= null;
432 $goto = $this->router
->pathFor('mailings');
437 //flash messages if any
438 if (count($error_detected) > 0) {
439 foreach ($error_detected as $error) {
440 $this->flash
->addMessage('error_detected', $error);
443 if (count($success_detected) > 0) {
444 foreach ($success_detected as $success) {
445 $this->flash
->addMessage('success_detected', $success);
451 ->withHeader('Location', $goto);
458 * Mailings history page
460 * @param Request $request PSR Request
461 * @param Response $response PSR Response
462 * @param string $option One of 'page' or 'order'
463 * @param string|integer $value Value of the option
467 public function list(Request
$request, Response
$response, $option = null, $value = null): Response
469 if (isset($this->session
->filter_mailings
)) {
470 $filters = $this->session
->filter_mailings
;
472 $filters = new MailingsList();
475 if (isset($request->getQueryParams()['nbshow'])) {
476 $filters->show
= $request->getQueryParams()['nbshow'];
479 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, $filters);
481 if ($option !== null) {
484 $filters->current_page
= (int)$value;
487 $filters->orderby
= $value;
491 //reinitialize object after flush
492 $filters = new MailingsList();
493 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
, $filters);
498 $this->session
->filter_mailings
= $filters;
500 //assign pagination variables to the template and add pagination links
501 $mailhist->filters
->setSmartyPagination($this->router
, $this->view
->getSmarty());
502 $history_list = $mailhist->getHistory();
503 //assign pagination variables to the template and add pagination links
504 $mailhist->filters
->setSmartyPagination($this->router
, $this->view
->getSmarty());
509 'gestion_mailings.tpl',
511 'page_title' => _T("Mailings"),
512 'logs' => $history_list,
513 'history' => $mailhist
522 * @param Request $request PSR Request
523 * @param Response $response PSR Response
527 public function filter(Request
$request, Response
$response): Response
529 $post = $request->getParsedBody();
530 $error_detected = [];
532 if ($this->session
->filter_mailings
!== null) {
533 $filters = $this->session
->filter_mailings
;
535 $filters = new MailingsList();
538 if (isset($post['clear_filter'])) {
542 (isset($post['nbshow']) && is_numeric($post['nbshow']))
544 $filters->show
= $post['nbshow'];
547 if (isset($post['end_date_filter']) ||
isset($post['start_date_filter'])) {
549 if (isset($post['start_date_filter'])) {
550 $filters->start_date_filter
= $post['start_date_filter'];
552 if (isset($post['end_date_filter'])) {
553 $filters->end_date_filter
= $post['end_date_filter'];
555 } catch (Throwable
$e) {
556 $error_detected[] = $e->getMessage();
560 if (isset($post['sender_filter'])) {
561 $filters->sender_filter
= $post['sender_filter'];
564 if (isset($post['sent_filter'])) {
565 $filters->sent_filter
= $post['sent_filter'];
569 if (isset($post['subject_filter'])) {
570 $filters->subject_filter
= $post['subject_filter'];
574 $this->session
->filter_mailings
= $filters;
576 if (count($error_detected) > 0) {
578 foreach ($error_detected as $error) {
579 $this->flash
->addMessage(
588 ->withHeader('Location', $this->router
->pathFor('mailings'));
594 * @param Request $request PSR Request
595 * @param Response $response PSR Response
596 * @param integer $id Record id
600 public function edit(Request
$request, Response
$response, int $id): Response
602 //no edit page, just to satisfy inheritance
608 * @param Request $request PSR Request
609 * @param Response $response PSR Response
610 * @param integer $id Record id
614 public function doEdit(Request
$request, Response
$response, int $id): Response
616 //no edit page, just to satisfy inheritance
623 * Get redirection URI
625 * @param array $args Route arguments
629 public function redirectUri(array $args)
631 return $this->router
->pathFor('mailings');
637 * @param array $args Route arguments
641 public function formUri(array $args)
643 return $this->router
->pathFor(
645 ['id' => $args['id'] ??
null]
650 * Get confirmation removal page title
652 * @param array $args Route arguments
656 public function confirmRemoveTitle(array $args)
659 _T('Remove mailing #%1$s'),
667 * @param array $args Route arguments
668 * @param array $post POST values
672 protected function doDelete(array $args, array $post)
674 $mailhist = new MailingHistory($this->zdb
, $this->login
, $this->preferences
);
675 return $mailhist->removeEntries($args['id'], $this->history
);
683 * @param Request $request PSR Request
684 * @param Response $response PSR Response
685 * @param integer $id Mailing id
689 public function preview(Request
$request, Response
$response, int $id = null): Response
691 $post = $request->getParsedBody();
692 // check for ajax mode
696 ||
isset($post['ajax'])
697 && $post['ajax'] == 'true'
704 $mailing = new Mailing($this->preferences
);
705 MailingHistory
::loadFrom($this->zdb
, $id, $mailing, false);
706 $attachments = $mailing->attachments
;
708 $mailing = $this->session
->mailing
;
710 switch ($post['sender']) {
711 case GaletteMail
::SENDER_CURRENT
:
712 $member = new Adherent($this->zdb
, (int)$this->login
->id
, false);
718 case GaletteMail
::SENDER_OTHER
:
720 $post['sender_name'],
721 $post['sender_address']
724 case GaletteMail
::SENDER_PREFS
:
726 //nothing to do; this is the default :)
730 $mailing->subject
= $post['subject'];
731 $mailing->message
= $post['body'];
732 $mailing->html
= ($post['html'] === 'true');
733 $attachments = $mailing->attachments
;
739 'mailing_preview.tpl',
741 'page_title' => _T("Mailing preview"),
743 'mode' => ($ajax ?
'ajax' : ''),
744 'mailing' => $mailing,
745 'recipients' => $mailing->recipients
,
746 'sender' => $mailing->getSenderName() . ' <' .
747 $mailing->getSenderAddress() . '>',
748 'attachments' => $attachments
756 * Preview attachement action
758 * @param Request $request PSR Request
759 * @param Response $response PSR Response
760 * @param integer $id Mailiung id
761 * @param integer $pos Attachement position in list
765 public function previewAttachment(Request
$request, Response
$response, int $id, $pos): Response
767 $mailing = new Mailing($this->preferences
);
768 MailingHistory
::loadFrom($this->zdb
, $id, $mailing, false);
769 $attachments = $mailing->attachments
;
770 $attachment = $attachments[$pos];
771 $filepath = $attachment->getDestDir() . $attachment->getFileName();
773 $response = $response->withHeader('Content-type', $attachment->getMimeType($filepath));
775 $body = $response->getBody();
776 $body->write(file_get_contents($filepath));
781 * Set recipients action
783 * @param Request $request PSR Request
784 * @param Response $response PSR Response
788 public function setRecipients(Request
$request, Response
$response): Response
790 $post = $request->getParsedBody();
791 $mailing = $this->session
->mailing
;
795 $members = $m->getArrayList(
805 $mailing->setRecipients($members);
807 $this->session
->mailing
= $mailing;
812 'mailing_recipients.tpl',
814 'mailing' => $mailing