Commit e3237c15 authored by Patrick Barroca's avatar Patrick Barroca 🐧

Merge branch 'dev#70265_2340_newsletter_je_ne_souhaite_recevoir_aucune_newsletter' into 'master'

dev#70265: newsletters : User Can disable any Newsletter [GPDR compliance]

See merge request !3567
parents dad6d950 b1135da1
Pipeline #10512 passed with stage
in 51 minutes
'70265' =>
['Label' => $this->_('RGPD : Newsletter - Possibilité de désactiver les lettres d'\'information'),
'Desc' => 'Ajout d'une possibilité pour les utilisateurs de se désabonner de toute lettre d\'information via une checkbox',
'Image' => '',
'Video' => '',
'Category' => '',
'Right' => function($feature_description, $user) {return true;},
'Wiki' => 'https://wiki.bokeh-library-portal.org/index.php?title=Lettre_d'information',
'Test' => '',
'Date' => '2020-04-29'],
\ No newline at end of file
- ticket #70265 : Newsletter Conformité RGPD je ne souhaite recevoir aucune newsletter
\ No newline at end of file
......@@ -176,6 +176,26 @@ class AbonneController extends ZendAfi_Controller_Action {
}
public function disableNewsletterAction() {
$this->_stayOnPage();
$this->_user->setDisableNewsletter(1);
$this->_user->save()
? $this->_helper->notify($this->_('Vous êtes désinscrit de toutes les listes de diffusion'))
: $this->_helper->notify($this->_('Une erreur est survenue lors de l\'enregistrement'));
}
public function enableNewsletterAction() {
$this->_stayOnPage();
$this->_user->setDisableNewsletter(0);
$this->_user->save()
? $this->_helper->notify($this->_('Vous pouvez vous inscrire à des listes de diffusion'))
: $this->_helper->notify($this->_('Une erreur est survenue lors de l\'enregistrement'));
}
public function viewavisAction(){
$this->_forward('viewauteur', 'blog', 'opac', ['id' => $this->_user->getId()]);
}
......@@ -587,7 +607,6 @@ class AbonneController extends ZendAfi_Controller_Action {
$form->addDisplayGroup(['mode_contact'], 'mode_de_contact', ['legend' => 'Recevoir mes notificactions de réservation et de rappel']);
}
/* Abonnements aux newsletters*/
$subscriptions = new Zend_Form_Element_MultiCheckbox('subscriptions');
$subscriptions->setLabel($this->_("Abonnement aux lettres d'information"));
......@@ -603,14 +622,28 @@ class AbonneController extends ZendAfi_Controller_Action {
$checked_subscriptions []= $nl->getId();
}
$subscriptions->setValue($checked_subscriptions);
$form
->addElement($subscriptions)
->addDisplayGroup(['subscriptions'],
->addElement('checkbox', 'disable_newsletter',
['label' => $this->_('Je ne veux plus recevoir du tout de lettre d\'information'),
'class' => 'important',
'value' => true,
'checked' => $user->isNewsletterDisabled()
])
->addDisplayGroup(['subscriptions','disable_newsletter'],
'newsletters',
['legend' => 'Lettres d\'information']);
}
if ($user->isNewsletterDisabled()){
$form->addSummary($this->_("Vous avez désactivé la réception des lettres d'information"));
Class_ScriptLoader::getInstance()->addJQueryReady('$("subscriptions[]").prop("disabled", true);');
}
$form
->populate($user->toArray());
......@@ -640,15 +673,18 @@ class AbonneController extends ZendAfi_Controller_Action {
$password = $this->_user->getPassword();
$fields_to_save = Class_AdminVar::getChampsFicheUtilisateur();
$attributes = [];
$fields_to_save []= 'disable_newsletter';
$attributes = [ 'disable_newsletter' => false];
foreach($fields_to_save as $field)
$attributes[$field] = $this->_request->getParam($field);
$previous_password = $this->_user->getPassword();
$this->_user
->updateAttributes($attributes)
->setPassword($password)
->setNewsletters($newsletters);
->setPassword($password);
if ($this->_user->isNewsletterEnabled())
$this->_user->setNewsletters($newsletters);
$patron = $this->_user->getEmprunteur();
$patron->updateFromUser($this->_user);
......@@ -1947,4 +1983,4 @@ class AbonneController extends ZendAfi_Controller_Action {
$this->_helper->csv('Mes-donnees-Bokeh.csv', $this->view->userDatasCsv(Class_Users::getIdentity()));
}
}
\ No newline at end of file
}
<?php
$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
try {
$adapter->query(
'ALTER TABLE `bib_admin_users` '
. 'ADD COLUMN `disable_newsletter` tinyint(1) default "0",'
. 'ADD KEY `disable_newsletter` (`disable_newsletter`)'
);
} catch(Exception $e) {}
......@@ -29,8 +29,13 @@ class Newsletter_BlacklistLoader extends Storm_Model_Loader {
public function unsubscribeByMail($mail, $newsletter_id) {
if (0 < Class_Newsletter_Blacklist::countBy(['newsletter_id' => $newsletter_id,
'mail' => $mail]))
return;
$blacklist = Class_Newsletter_Blacklist::newInstance(['mail' => $mail,
'newsletter_id' => $newsletter_id]);
return $blacklist->save();
}
......
......@@ -115,6 +115,11 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
return;
$blacklist_mails = Class_Newsletter_Blacklist::getMailsForNewsletter($this->getNewsletter()->getId());
$disabled_mails = Class_Users::getDisabledNewsletterUserMails();
$blacklist_mails = array_unique(array_merge($blacklist_mails, $disabled_mails));
$closure = function($model) use ($blacklist_mails) {
if (!$model->hasMail())
return;
......
......@@ -22,6 +22,12 @@
class UsersLoader extends Storm_Model_Loader {
use Trait_Translator;
public function getDisabledNewsletterUserMails(){
$request= 'select distinct(bib_admin_users.mail) from bib_admin_users where disable_newsletter=1 and mail<>"" and mail is not null;';
return array_map(function($element) { return $element[0]; },
Zend_Registry::get('sql')->fetchAllByColumn($request));
}
public function getNewslettersReceivers($id_newsletter, $recipient_size) {
// do not use Storm as it must be ordered by mail AND filtered by joined column
$req = 'select bib_admin_users.* from bib_admin_users join newsletters_users on bib_admin_users.id_user = newsletters_users.user_id where newsletter_id = ' . $id_newsletter . ' and newsletters_users.send is false order by bib_admin_users.mail limit ' . $recipient_size;
......@@ -500,6 +506,7 @@ class Class_Users extends Storm_Model_Abstract {
'id_panier_courant' => 0,
'settings' => '',
'statut' => 0,
'disable_newsletter' => 0,
'id_int_bib' => null];
public static function currentUserId() {
......@@ -1964,4 +1971,14 @@ class Class_Users extends Storm_Model_Abstract {
return $this->getDateFin();
return $this->addDaysToCurrentDate(30);
}
public function isNewsletterDisabled() {
return true === (boolean) $this->getDisableNewsletter();
}
public function isNewsletterEnabled() {
return !$this->isNewsletterDisabled();
}
}
......@@ -20,33 +20,49 @@
*/
class ZendAfi_View_Helper_Abonne_Newsletters extends ZendAfi_View_Helper_Abonne_Abstract {
public function abonne_newsletters($user) {
if (count(Class_Newsletter::findAll()) == 0)
if (Class_Newsletter::count() == 0)
return '';
$newsletter_info = $this->_newsletterInfo($user);
$action_url = $this->view->url(['controller' => 'abonne', 'action' => 'edit']);
return $this->tagFicheAbonne(
'<p>' . $newsletter_info . '</p>'.
$this->_newsletterInfo($user)
.
$this->view->tagAnchor($action_url,
$this->view->_('Modifier mes abonnements')),
$this->_('Modifier mes abonnements')),
'newsletter',
$action_url);
}
protected function _newsletterInfo($user) {
if (!$newsletters = $user->getNewsletters())
return $this->view->_("Vous n'êtes abonné à aucune lettre d'information");
if ($user->isNewsletterDisabled())
return
$this->_tag('p',
$this->_("Vous avez désactivé la réception des lettres d'information"))
.$this->_tag('p',
$this->view->tagAnchor($this->view->url(['controller' => 'abonne', 'action' => 'enable-newsletter']),
$this->_('Activer l\'envoi des lettres d\'information')))
;
$newsletters = $user->getNewsletters();
$titres = [];
foreach($newsletters as $newsletter) {
foreach($newsletters as $newsletter)
$titres[] = $newsletter->getTitre();
}
$newsletter_info = $this->view->_('Vous êtes abonné');
$newsletter_info .= count($titres) > 1 ? $this->view->_(" aux lettres d'information: ") : $this->view->_(" à la lettre d'information: ");
return $newsletter_info .= implode(', ', $titres);
$information_string = $this->_plural(count($titres),
"Vous n'êtes abonné à aucune lettre d'information",
"Vous êtes abonné à la lettre d'information: %s",
"Vous êtes abonné aux lettres d'information: %s",
implode(', ', $titres));
return $this->_tag('p',
$information_string)
.$this->_tag('p',
$this->view->tagAnchor($this->view->url(['controller' => 'abonne', 'action' => 'disable-newsletter'])
,
$this->_('Ne plus recevoir de lettres d\'information')));
}
}
\ No newline at end of file
......@@ -272,6 +272,7 @@ class Intonation_Library_Settings extends Intonation_System_Abstract {
'accessibility' => 'class fas fa-low-vision',
'login' => 'class fas fa-sign-in-alt',
'ban' => 'class fas fa-ban',
'previous' => 'class fas fa-chevron-left',
'next' => 'class fas fa-chevron-right',
......
......@@ -38,7 +38,13 @@ class Intonation_Library_View_Wrapper_Newsletter extends Intonation_Library_View
public function getMainLink() {
$params = $this->_model->hasRecipient(Class_Users::getIdentity())
if (!$current_user = Class_Users::getIdentity())
return '';
if ($current_user->isNewsletterDisabled())
return '';
$params = $this->_model->hasRecipient($current_user)
? ['Url' => $this->_view->url(['controller' => 'abonne',
'action' => 'unsubscribe-newsletter',
'id' => $this->_model->getId()]),
......
......@@ -38,6 +38,50 @@ class Intonation_View_RenderNewsletters extends ZendAfi_View_Helper_BaseHelper {
return
$this->_tag('h3', $this->_('Les lettres d\'information'))
. $this->_renderNewsletterAction()
. $this->view->renderTruncateList(new Storm_Collection($newsletters), $callback);
}
protected function _renderNewsletterAction() {
if (!$user = Class_Users::getIdentity())
return '';
return $user->isNewsletterEnabled()
? $this->_disableNewsletterAction()
: $this->_enableNewsletterAction();
}
protected function _disableNewsletterAction() {
$ico = Class_Template::current()->getIco($this->view, 'ban', 'utils');
return $this->_renderAction('disable-newsletter',
$this->_('Ne plus recevoir de lettre d\'information'),
'btn-warning',
$ico);
}
protected function _enableNewsletterAction() {
$ico = Class_Template::current()->getIco($this->view, 'add', 'utils');
return $this->_renderAction('enable-newsletter',
$this->_('Pouvoir recevoir des lettres d\'information'),
'btn-success',
$ico);
}
protected function _renderAction($action, $text, $class, $ico) {
$url = $this->view->url(['controller' => 'abonne',
'action' => $action]);
$attribs = ['Tag' => 'url',
'Text' => $text,
'OnlyClasses' => "card-link btn btn-sm text-light ".$class,
'Url' => $url,
'Title' => $text,
'Image' => $ico];
return $this->view->tagAction(new Intonation_Library_Link($attribs));
}
}
......@@ -71,10 +71,10 @@ abstract class AbonneControllerWithTwoNewslettersTestCase extends AbstractAbonne
'contenu' => 'du patrimoine']);
$this->marcus->subscribeNewsletter($this->concerts);
$mock = $this
->mock()
$mock = $this->mock();
$mock
->whenCalled('fetchAll')
->with('select user_id as id from user_group_memberships where user_group_id=' . $this->concerts->getDedicatedGroup()->getId())
->answers([['id' => $this->marcus->getId()]])
......@@ -83,9 +83,16 @@ abstract class AbonneControllerWithTwoNewslettersTestCase extends AbstractAbonne
->with('select user_id as id from user_group_memberships where user_group_id=' . $this->visites->getDedicatedGroup()->getId())
->answers([])
;
->whenCalled('fetchAll')
->with('select user_id as id from user_group_memberships where user_group_id=3')
->answers([])
->beStrict();
Zend_Registry::set('sql', $mock);
$this->marcus->subscribeNewsletter($this->concerts);
}
}
......@@ -158,16 +165,34 @@ class AbonneControllerNewslettersFicheActionWithOneSubscriptionTest extends Abon
}
class AbonneControllerNewsletterDisableActionTest extends AbonneControllerWithTwoNewslettersTestCase {
public function setUp() {
parent::setUp();
$this->dispatch('/opac/abonne/disable-newsletter');
}
/** @test */
public function userSettingsNewsletterDisabledShouldBeTrue() {
$this->assertTrue($this->marcus->isNewsletterDisabled());
}
}
class AbonneControllerNewslettersFicheActionWithTwoSubscriptionsTest extends AbonneControllerWithTwoNewslettersTestCase {
public function setUp() {
parent::setUp();
$this->marcus->subscribeNewsletter($this->visites);
Zend_Registry::get('sql')
->whenCalled('fetchAll')
->with('select user_id as id from user_group_memberships where user_group_id=' . $this->visites->getDedicatedGroup()->getId())
->answers([['id' => $this->marcus->getId()]])
;
->answers([['id' => $this->marcus->getId()]]);
$this->dispatch('/opac/abonne/fiche');
}
......@@ -198,6 +223,54 @@ class AbonneControllerNewslettersFicheActionWithNoSubscriptionTest extends Abonn
}
class AbonneControllerNewslettersEditWithDisabledNewsletterActionTest
extends AbonneControllerWithTwoNewslettersTestCase {
public function setUp() {
parent::setUp();
$mock = $this->mock();
$mock
->whenCalled('fetchAll')
->with('select distinct(bib_admin_users.mail) from bib_admin_users where disable_newsletter=1 and mail<>"" and mail is not null;',true,false)
->answers([])
->whenCalled('fetchAll')
->with("select user_id as id from user_group_memberships where user_group_id=2")
->answers([])
->beStrict()
;
Zend_Registry::set('sql', $mock);
$this->marcus->unsubscribeNewsletter($this->concerts);
$this->marcus->setDisableNewsletter(true)->save();
$this->dispatch('/opac/abonne/edit');
}
/** @test */
public function noNewsletterSubscriptionShouldBeDisplayed() {
$this->assertXPathContentContains('//label[@for="disable_newsletter"]',
"Je ne veux plus recevoir du tout de lettre d'information",$this->_response->getBody());
}
/** @test */
public function noNewsletterSubscriptionShouldBeChecked() {
$this->assertXPath('//input[@name="disable_newsletter"][@checked="checked"]',
$this->_response->getBody());
}
}
class AbonneControllerNewslettersEditActionTest extends AbonneControllerWithTwoNewslettersTestCase {
public function setUp() {
parent::setUp();
......
......@@ -3343,4 +3343,20 @@ class UpgradeDB_390_Test extends UpgradeDBTestCase {
public function tableDriveCheckoutShouldContainsCreatedAtColumn() {
$this->assertFieldType('drive_checkout', 'created_at', 'datetime');
}
}
\ No newline at end of file
}
class UpgradeDB_391_Test extends UpgradeDBTestCase {
public function prepare() {
$this->dropIndexedFieldFrom('bib_admin_users', 'disable_newsletter');
$this->silentQuery('ALTER TABLE bib_admin_users drop column disable_newsletter;');
}
/** @test */
public function bibAdminUsersShouldHaveNewFieldDisableNewsletterIndexed() {
$this->assertFieldType('bib_admin_users', 'disable_newsletter', 'tinyint(1)');
$this->assertIndex('bib_admin_users', 'disable_newsletter');
}
}
......@@ -19,8 +19,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
abstract class Class_NewsletterDispatchTestCase extends ModelTestCase {
abstract class Class_NewsletterDispatchFixturesTestCase extends ModelTestCase {
protected $_storm_default_to_volatile = true;
public function setUp() {
......@@ -71,7 +70,6 @@ abstract class Class_NewsletterDispatchTestCase extends ModelTestCase {
Class_UserGroup::setMemoryCleaner(function() {});
Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 15:54:25'));
Class_Newsletter::setTimeSource(new TimeSourceForTest('2016-07-21 15:54:25'));
$this->_dispatch = Class_Newsletter_Dispatch::newFrom($newsletter);
$this->mock_transport = new MockMailTransport();
Zend_Mail::setDefaultTransport($this->mock_transport);
......@@ -80,6 +78,16 @@ abstract class Class_NewsletterDispatchTestCase extends ModelTestCase {
abstract class Class_NewsletterDispatchTestCase extends Class_NewsletterDispatchFixturesTestCase {
public function setUp() {
parent::setUp();
$this->_dispatch = Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1));
}
}
class Class_NewsletterDispatchFirstRunTest extends Class_NewsletterDispatchTestCase {
public function setUp() {
parent::setUp();
......@@ -161,4 +169,76 @@ class Class_NewsletterDispatchReRunTest extends Class_NewsletterDispatchTestCase
Class_Newsletter_DispatchUser::findFirstBy(['user_id' => 22])
->getSent());
}
}
\ No newline at end of file
}
class Class_NewsletterDispatchWithMultipleSubscriptionBlacklistForOneUserTest extends Class_NewsletterDispatchFixturesTestCase {
public function setUp() {
parent::setUp();
$newsletter = Class_Newsletter::find(1);
$newsletter->unsubscribeUser(Class_Users::find(23));
$newsletter->unsubscribeUser(Class_Users::find(23));
}
/** @test */
public function countBlacklistEntryForUser23ShouldBeOne() {
$this->assertEquals(1, Class_Newsletter_Blacklist::countBy(['newsletter_id'=>1,'mail'=>"pat@server.com"]));
}
}
class Class_NewsletterDispatchWithDisabledUserForNewNewsLetterTest extends Class_NewsletterDispatchFixturesTestCase {
public function setUp() {
parent::setUp();
$mock_sql = $this->mock()
->whenCalled('fetchAllByColumn')
->with("select distinct(bib_admin_users.mail) from bib_admin_users where disable_newsletter=1 and mail<>\"\" and mail is not null;")
->answers([['pat@server.com']])
->beStrict();
Zend_Registry::set('sql', $mock_sql);
Class_Users::find(23)->setDisableNewsLetter(true);
$newsletter = $this->fixture('Class_Newsletter',
['id' => 3,
'titre' => 'Nouveautés fiction Jeunesse',
'mail_subject' => 'Nouveautés fictives',
'contenu' => 'Notre sélection du mois',
'id_catalogue' => '',
'id_panier' => '',
'expediteur' => 'papanoel@pole-nord.com',
'user_groups' => [Class_UserGroup::find(14),
Class_UserGroup::find(15)]]);
$this->_dispatch = Class_Newsletter_Dispatch::newFrom($newsletter);
$this->_dispatch->collectRecipients();
$this->_dispatch->sendBy(1);
}
/** @test */
public function mailTransportShouldHaveSent2Mails() {
$this->assertEquals(2, $this->mock_transport->count());
}
/** @test */
public function dispatchToUser23ShouldBeNull() {
$this->assertNull(Class_Newsletter_DispatchUser::findfirstBy(['user_id'=>23]));
}
/** @test */
public function dispatchToEmailPatAtServerDotComShouldBeNull() {
$this->assertNull(Class_Newsletter_DispatchUser::findfirstBy(['mail'=>'pat@server.com']));
}
}
......@@ -5067,6 +5067,19 @@ class TemplatesDispatchNewsletterWidgetTest extends TemplatesIntonationTestCase
$this->dispatch('/admin/widget/edit-widget/id/25/id_profil/72', true);
$this->assertXpath('//input[@name="titre"][@type="text"]');
}
/** @test */
public function buttonDisableNewsletterShouldBePresent() {
$this->fixture('Class_Newsletter',
['id' => 3,
'titre' => 'Jeunesse',
'mail_subject' => 'Jeunesse',
'contenu' => 'les nouveautés jeunesse']);
$this->dispatch('/opac/abonne/fiche', true);
$this->assertXPathContentContains('//a[@href="/abonne/disable-newsletter"]', 'Ne plus recevoir de lettre d\'information', $this->_response->getBody());
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment