diff --git a/VERSIONS_HOTLINE/134652 b/VERSIONS_HOTLINE/134652 new file mode 100644 index 0000000000000000000000000000000000000000..8c4574c69efb992eaf9653bee9d24d2a115255fb --- /dev/null +++ b/VERSIONS_HOTLINE/134652 @@ -0,0 +1 @@ + - ticket #134652 : Articles : la variable pour changer l'expéditeur utilisé dans le workflow des articles est aussi utilisée pour la notification des articles à valider \ No newline at end of file diff --git a/library/Class/Article/WorkflowNotification.php b/library/Class/Article/WorkflowNotification.php new file mode 100644 index 0000000000000000000000000000000000000000..8dbdafc26b3e57b8e909eff309a0cdd63aef350f --- /dev/null +++ b/library/Class/Article/WorkflowNotification.php @@ -0,0 +1,220 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Article_WorkflowNotification { + use Trait_Translator; + + protected + $_article, + $_helper; + + + public static function newFor($article, $helper) { + if ($article->getStatus() == $article->old_status) + return new static($article, $helper); + + if (static::_isValidationPending($article)) + return new Class_Article_WorkflowNotification_ValidationPending($article, $helper); + + if (static::_isBecoming($article, Class_Article::STATUS_VALIDATED)) + return new Class_Article_WorkflowNotification_Validated($article, $helper); + + if (static::_isBecoming($article, Class_Article::STATUS_REFUSED)) + return new Class_Article_WorkflowNotification_Refused($article, $helper); + + return new static($article, $helper); + } + + + protected static function _isValidationPending($article) { + return static::_isBecoming($article, Class_Article::STATUS_VALIDATION_PENDING) + || ($article->getStatus() > Class_Article::STATUS_REFUSED); + } + + + protected static function _isBecoming($article, $new_status) { + return $article->getStatus() == $new_status; + } + + + public function __construct($article, $helper) { + $this->_article = $article; + $this->_helper = $helper; + $this->_user = Class_Users::getIdentity(); + } + + + public function notify() { + if (!Class_AdminVar::isWorkflowEnabled() || !$this->_user) + return $this; + + if (!$recipients = $this->_loadRecipients()) + return $this; + + $mail = (new ZendAfi_Mail) + ->addTo(implode(',', $recipients)) + ->setSubject($this->_subject()) + ->setBodyText($this->_bodyText()) + ->setFrom(Class_AdminVar::getValueOrDefault('WORKFLOW_MAIL_FROM')) + ; + + if ($this->_sendMail($mail)) + $this->_helper->notify($this->_successMessage($mail)); + + return $this; + } + + + protected function _bodyText() { + $replacements = ['TITRE_ARTICLE' => $this->_article->getTitre(), + 'URL_ARTICLE' => Class_Url::absolute($this->_article->getUrl(), null, true), + 'AUTHOR_ARTICLE' => $this->_article->getNomCompletAuteur(), + 'SAVED_BY_ARTICLE' => $this->_user->getNomComplet(), + 'NEXT_STATUS_ARTICLE' => $this->_article->getNextWorkflowStatusLabel(), + 'STATUS_ARTICLE' => $this->_article->getStatusLabel()]; + + return str_replace(array_keys($replacements), + array_values($replacements), + $this->_bodyTextTemplate()); + } + + + + protected function _sendMail($mail) { + try { + $mail->send(); + return true; + } catch (Exception $e) { + $this->_helper + ->notify($this->_('Mail non envoyé: vérifier la configuration du serveur de mail.')); + return false; + } + } + + + protected function _loadRecipients() { + return []; + } + + + protected function _subject() { + return ''; + } + + + protected function _bodyTextTemplate() { + return ''; + } + + + protected function _successMessage($sent_mail) { + return ''; + } +} + + + + +class Class_Article_WorkflowNotification_ValidationPending + extends Class_Article_WorkflowNotification { + + protected function _loadRecipients() { + return array_unique(Class_Permission::getWorkflow($this->_article->getNextWorkflowStatus()) + ->getUsersOnModel($this->_article->getCategorie()) + ->collect('mail') + ->getArrayCopy()); + } + + + protected function _subject() { + return $this->_('[Bokeh] Validation d\'article en attente: %s', + $this->_article->getTitre()); + } + + + protected function _bodyTextTemplate() { + return Class_AdminVar::getWorkflowTextMailArticlePending(); + } + + + protected function _successMessage($sent_mail) { + return $this->_('Mail de validation envoyé aux validateurs.'); + } +} + + + + +class Class_Article_WorkflowNotification_ToAuthor + extends Class_Article_WorkflowNotification { + + protected function _loadRecipients() { + if (!$author = $this->_article->getAuteur()) { + $this->_helper->notify($this->_('Mail non envoyé: article sans auteur')); + return []; + } + + if (!$mail_address = $author->getMail()) { + $this->_helper->notify($this->_('Mail non envoyé: %s sans mail.', $author->getNomComplet())); + return []; + } + + return [$mail_address]; + } + + + protected function _successMessage($sent_mail) { + return $this->_('Mail envoyé à : %s', $sent_mail->getRecipients()[0]); + } +} + + + + +class Class_Article_WorkflowNotification_Validated + extends Class_Article_WorkflowNotification_ToAuthor { + + protected function _subject() { + return $this->_('[Bokeh] Validation de l\'article %s', $this->_article->getTitre()); + } + + + protected function _bodyTextTemplate() { + return $this->_article->getValideMessage(); + } +} + + + + +class Class_Article_WorkflowNotification_Refused + extends Class_Article_WorkflowNotification_ToAuthor { + + protected function _subject() { + return $this->_('[Bokeh] Refus de l\'article %s', $this->_article->getTitre()); + } + + + protected function _bodyTextTemplate() { + return $this->_article->getRefusMessage(); + } +} diff --git a/library/ZendAfi/Controller/Plugin/Manager/Article.php b/library/ZendAfi/Controller/Plugin/Manager/Article.php index c090644422418adb3822d37becfcf446a1f92715..97475c779ebb76cc3918a2188b9a014fd4111cd8 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/Article.php +++ b/library/ZendAfi/Controller/Plugin/Manager/Article.php @@ -27,13 +27,10 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi return; } - $this->_view->titre = $this->_view->_('Supprimer l\'article : %s', - $article->getTitre()); - + $this->_view->titre = $this->_('Supprimer l\'article : %s', $article->getTitre()); if (!$this->_canModify($article->getCategorie())) { - $this->_helper->notify($this->_view->_('Vous n\'avez pas la permission "%s"', - $this->_view->titre)); + $this->_helper->notify($this->_('Vous n\'avez pas la permission "%s"', $this->_view->titre)); $this->_redirectToIndex(); return; } @@ -47,8 +44,7 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi return $this->_redirect('admin/cms'); if (!$this->_canModify($model->getCategorie())) { - $this->_helper->notify($this->_view->_('Vous n\'avez pas la permission "%s"', - $this->_view->titre)); + $this->_helper->notify($this->_('Vous n\'avez pas la permission "%s"', $this->_view->titre)); return $this->_redirectToIndex(); } @@ -58,7 +54,8 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi parent::addAction(); if ($this->_response->isRedirect()) return; - $this->_view->titre = $this->_view->_('Dupliquer l\'article : %s', $model->getTitre()); + + $this->_view->titre = $this->_('Dupliquer l\'article : %s', $model->getTitre()); $this->_view->form ->setAction($this->_view->url(['module' => 'admin', 'controller' => 'cms', @@ -87,9 +84,9 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi } if (!$this->_canModify($article->getCategorie())) { - $this->_helper->notify($this->_view->_('Vous n\'avez pas la permission "%s"', - $this->_view->_('Supprimer l\'article : %s', - $article->getTitre()))); + $this->_helper + ->notify($this->_('Vous n\'avez pas la permission "%s"', + $this->_('Supprimer l\'article : %s', $article->getTitre()))); $this->_redirectToIndex(); return; } @@ -101,7 +98,7 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi { $plugin->notifyAfterDelete($article); }); - $this->_helper->notify($this->_view->_('Article supprimé')); + $this->_helper->notify($this->_('Article supprimé')); $this->_redirect($this->_backDeleteUrl($article)); } @@ -137,6 +134,7 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi $article->setCategorie($category); if ($domaine = Class_Catalogue::findWithSamePathAs($category)) $article->setDomaineIds($domaine->getId()); + return $this; } @@ -155,7 +153,6 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi } - protected function _doBeforeSave($article) { $article->updateDateMaj(); return $this; @@ -183,10 +180,10 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi } if (!$this->_canModify($article->getCategorie())) { - $this->_helper->notify($this->_view->_('Vous n\'avez pas la permission "%s"', - $this->_view->_('Rendre %s l\'article : %s', - $visibility, - $article->getTitre()))); + $this->_helper + ->notify($this->_('Vous n\'avez pas la permission "%s"', + $this->_('Rendre %s l\'article : %s', + $visibility, $article->getTitre()))); $this->_redirectToIndex(); return; } @@ -224,8 +221,8 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi protected function _getEditActionTitle($model) { return $model->isTraduction() - ? $this->_view->_('Traduire un article: %s', $model->getLibelle()) - : $this->_view->_('Modifier un article: %s', $model->getLibelle()); + ? $this->_('Traduire un article: %s', $model->getLibelle()) + : $this->_('Modifier un article: %s', $model->getLibelle()); } @@ -273,8 +270,8 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi return 'admin/cms/index' . (!$model->isNew() ? '/id/' . $model->getId() : ''); return $this->_view->absoluteUrl(['module' => 'admin', - 'controller' => 'cms', - 'action' => 'index']); + 'controller' => 'cms', + 'action' => 'index']); } @@ -284,141 +281,9 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi ($page = $this->_getParam('page')) ? '/page/'.$page : ''); } - protected function _notifyArticleChanged($article) { - if (!Class_AdminVar::isWorkflowEnabled()) - return; - $this->_sendMailWhenUpdatedStatusToValidationPending($article); - $this->_sendMailWhenUpdatedStatusToRefused($article); - $this->_sendMailWhenUpdatedStatusToValidated($article); - } - - - protected function _sendMailWhenUpdatedStatusToRefused($article) { - if ($article->old_status != Class_Article::STATUS_REFUSED && - $article->getStatus() == Class_Article::STATUS_REFUSED) { - $this->_sendRefusedMailToAuteur($article); - } - } - - - protected function _sendMailWhenUpdatedStatusToValidated($article) { - if ($article->old_status != Class_Article::STATUS_VALIDATED && - $article->getStatus() == Class_Article::STATUS_VALIDATED) { - $this->_sendValidatedMailToAuteur($article); - } - } - - - protected function _sendMailWhenUpdatedStatusToValidationPending($article) { - if (($article->old_status != Class_Article::STATUS_VALIDATION_PENDING && - $article->getStatus() == Class_Article::STATUS_VALIDATION_PENDING) - || ($article->getStatus() > 5 - && $article->old_status != $article->getStatus())) { - $this->_sendMailToValidators($article); - } - } - - - protected function prepareMailForAuteur($article) { - $mail = new ZendAfi_Mail(); - if(!$article->getAuteur()) { - $this->_helper->notify($this->_('Mail non envoyé: article sans auteur')); - return; - } - - if(!$mail_address = $article->getAuteur()->getMail()) { - $this->_helper->notify($this->_('Mail non envoyé: %s sans mail.', $article->getNomCompletAuteur())); - return; - } - - $mail->setFrom(Class_AdminVar::getValueOrDefault('WORKFLOW_MAIL_FROM')) - ->addTo($mail_address); - return $mail; - } - - protected function prepareBodyMail($article, $message) { - $this->identity = Class_Users::getIdentity(); - $replacements = - ['TITRE_ARTICLE' => $article->getTitre(), - 'URL_ARTICLE' => $this->_view->absoluteUrl($article->getUrl(), null, true), - 'AUTHOR_ARTICLE' => $article->getNomCompletAuteur(), - 'SAVED_BY_ARTICLE' => $this->identity->getNomComplet(), - 'NEXT_STATUS_ARTICLE' => $article->getNextWorkflowStatusLabel(), - 'STATUS_ARTICLE' => $article->getStatusLabel()]; - - return - str_replace(array_keys($replacements), - array_values($replacements), - $message); - } - - - protected function _sendRefusedMailToAuteur($article) { - if(!$mail = $this->prepareMailForAuteur($article)) - return; - $body = $this->prepareBodyMail($article, $article->getRefusMessage()); - $this->sendPreparedMail($mail, - '[Bokeh] Refus de l\'article '.$article->getTitre(), - $body); - } - - - protected function sendPreparedMail($mail, $subject, $body) { - $mail->setSubject(quoted_printable_decode($subject)) - ->setBodyText($body,'utf-8',Zend_Mime::ENCODING_8BIT); - - if ($this->_sendMail($mail)) - $this->_helper->notify($this->_('Mail envoyé à : %s', $mail->getRecipients()[0])); - } - - - protected function _sendValidatedMailToAuteur($article) { - if(!$mail = $this->prepareMailForAuteur($article)) - return; - - $body = $this->prepareBodyMail($article, $article->getValideMessage()); - $this->sendPreparedMail($mail, - '[Bokeh] Validation de l\'article '.$article->getTitre(), - $body); - } - - - protected function _getValidatorsMail($article) { - return array_unique( - Class_Permission::getWorkflow($article->getNextWorkflowStatus()) - ->getUsersOnModel($article->getCategorie()) - ->collect('mail') - ->getArrayCopy()); - } - - - protected function _sendMailToValidators($article) { - if (!$mails = $this->_getValidatorsMail($article)) - return; - - $mail = new ZendAfi_Mail(); - $mail - ->setFrom('no-reply@afi-sa.fr') - ->addTo(implode(',', $mails)) - ->setSubject($this->_view->_('[Bokeh] Validation d\'article en attente: ') . $article->getTitre()) - ->setBodyText($this->prepareBodyMail($article, - Class_AdminVar::getWorkflowTextMailArticlePending())); - - if($this->_sendMail($mail)) - $this->_helper->notify($this->_view->_('Mail de validation envoyé aux validateurs.')); - } - - - protected function _sendMail($mail) { - try { - $mail->send(); - return true; - - } catch (Exception $e) { - $this->_helper->notify($this->_('Mail non envoyé: vérifier la configuration du serveur de mail.')); - return false; - } + protected function _notifyArticleChanged($article) { + Class_Article_WorkflowNotification::newFor($article, $this->_helper)->notify(); } diff --git a/tests/application/modules/admin/controllers/CmsControllerTest.php b/tests/application/modules/admin/controllers/CmsControllerTest.php index 8325644ba2a936d13a31544e041c5e04b0694ac9..65cb5d945015926cc3e32515470d72fad85e697c 100644 --- a/tests/application/modules/admin/controllers/CmsControllerTest.php +++ b/tests/application/modules/admin/controllers/CmsControllerTest.php @@ -1703,6 +1703,7 @@ class CmsControllerNewsAddActionPostWithWorkflowTest $this->postDispatch('/admin/cms/edit/id/18', $data); } + /** @test */ public function statusShouldBeUpdated() { $this->postArticleValidated(); @@ -1799,16 +1800,15 @@ class CmsControllerNewsAddActionPostWithWorkflowTest /** @test */ - public function statusDraftToValidationPendingShouldSendByNoReplayAtAfiSaDotFr() { + public function statusDraftToValidationPendingShouldSendByNoReplyAtAfiSaDotFr() { Class_Article::find(18)->setStatus(Class_Article::STATUS_DRAFT)->save(); $data = $this->_basePostDatas; $data['status'] = Class_Article::STATUS_VALIDATION_PENDING; $this->postDispatch('/admin/cms/edit/id/18', $data); - $this->assertEquals('no-reply@afi-sa.fr' ,$this->mock_transport->getSentMails()[0]->getFrom()); + $this->assertEquals('no-reply@afi-sa.fr', $this->mock_transport->getSentMails()[0]->getFrom()); } - public function postArticleAValider() { Class_Article::find(18)->setStatus(Class_Article::STATUS_DRAFT)->save(); $data = $this->_basePostDatas; @@ -1832,6 +1832,23 @@ class CmsControllerNewsAddActionPostWithWorkflowTest } + /** @test */ + public function toValidateArticleMailShouldBeSentFromDefaultNoReplyAtAfi() { + $this->postArticleAValider(); + $this->assertEquals('no-reply@afi-sa.fr', + $this->mock_transport->getSentMails()[0]->getFrom()); + } + + + /** @test */ + public function withConfiguredSenderToValidateArticleMailShouldBeSentFromUserAtServerDotOrg() { + Class_AdminVar::set('WORKFLOW_MAIL_FROM', 'user@server.org'); + $this->postArticleAValider(); + $this->assertEquals('user@server.org', + $this->mock_transport->getSentMails()[0]->getFrom()); + } + + /** @test */ public function domainesShouldBeUpdated() { $data = $this->_basePostDatas;