From 2583147a7c65b27bf3ae362c661044cf8c837587 Mon Sep 17 00:00:00 2001
From: pbarroca <pbarroca@afi-sa.fr>
Date: Fri, 25 Sep 2015 16:31:04 +0200
Subject: [PATCH] rel #29524 : add CNIL consent to cookies

---
 VERSIONS_HOTLINE/29524                        |  1 +
 .../opac/controllers/IndexController.php      |  2 +
 library/Class/Cnil.php                        | 82 ++++++++++++++++++
 library/Class/ScriptLoader.php                |  8 +-
 .../opac/controllers/IndexControllerTest.php  | 85 ++++++++++++++++++-
 5 files changed, 170 insertions(+), 8 deletions(-)
 create mode 100644 VERSIONS_HOTLINE/29524
 create mode 100644 library/Class/Cnil.php

diff --git a/VERSIONS_HOTLINE/29524 b/VERSIONS_HOTLINE/29524
new file mode 100644
index 00000000000..f9a57032f24
--- /dev/null
+++ b/VERSIONS_HOTLINE/29524
@@ -0,0 +1 @@
+ - ticket #29524 : Mise en conformité CNIL sur les cookies de traçage
\ No newline at end of file
diff --git a/application/modules/opac/controllers/IndexController.php b/application/modules/opac/controllers/IndexController.php
index f5e211b6db2..38c0a43e1b7 100644
--- a/application/modules/opac/controllers/IndexController.php
+++ b/application/modules/opac/controllers/IndexController.php
@@ -25,6 +25,8 @@ class IndexController extends Zend_Controller_Action {
     $viewRenderer = $this->getHelper('ViewRenderer');
     $viewRenderer->setLayoutScript('portail.phtml');
 
+    (new Class_Cnil())->trackConsent();
+
     if (array_keys($this->getRequest()->getParams()) == ['controller', 'action', 'module', 'current_module', 'q']) {
       $this->_redirect('recherche?'.http_build_query(['q' => $this->_getParam('q')]));
     }
diff --git a/library/Class/Cnil.php b/library/Class/Cnil.php
new file mode 100644
index 00000000000..bb1b8a4cc1e
--- /dev/null
+++ b/library/Class/Cnil.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, 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_Cnil {
+  use Trait_Translator, Trait_TimeSource;
+
+  protected static $_cookie_jar;
+
+  protected $_key, $_session;
+
+
+  /** @category testing */
+  public static function setCookieJar($cookie_jar) {
+    static::$_cookie_jar = $cookie_jar;
+  }
+
+
+  public function __construct() {
+    $this->_key = 'cnil' . md5(BASE_URL);
+    $this->_session = new Zend_Session_Namespace($this->_key);
+    if (isset($_COOKIE[$this->_key]))
+      $this->_session->seen = true;
+  }
+
+
+  public function trackConsent() {
+    if ($this->_session->seen)
+      return;
+
+    $this->_session->seen = true;
+    $this->_setConsentCookie();
+    $this->_displayMessage();
+  }
+
+
+  protected function _setConsentCookie() {
+    $this->_getCookieJar()
+         ->setcookie($this->_key,
+                     '1',
+                     $this->getTimeSource()->time() + (3600 * 24 * 30 *13));
+  }
+
+
+  protected function _displayMessage() {
+    Class_ScriptLoader::getInstance()
+      ->notify($this->_('En poursuivant votre navigation sur ce site, vous acceptez l\'utilisation de cookies.'),
+               false);
+  }
+
+
+  protected function _getCookieJar() {
+    return static::$_cookie_jar ?
+      static::$_cookie_jar : new Class_Cnil_CookieJar();
+  }
+}
+
+
+
+class Class_Cnil_CookieJar {
+  public function setcookie() {
+    call_user_func_array('setcookie', func_get_args());
+  }
+}
\ No newline at end of file
diff --git a/library/Class/ScriptLoader.php b/library/Class/ScriptLoader.php
index a1fb35f6120..d2672af2a21 100644
--- a/library/Class/ScriptLoader.php
+++ b/library/Class/ScriptLoader.php
@@ -326,14 +326,14 @@ class Class_ScriptLoader {
    * voir [[file:~/public_html/afi-opac3/library/ZendAfi/Controller/Action/Helper/Notify.php::class%20ZendAfi_Controller_Action_Helper_Notify%20extends%20Zend_Controller_Action_Helper_Abstract%20{][ZendAfi_Controller_Action_Helper_Notify]]
    * @return ScriptLoader
    */
-  public function notify($message) {
+  public function notify($message, $autoclose=true, $duration=10, $type='information') {
     return $this
       ->loadNotificationJS()
       ->addJQueryReady(sprintf('showNotification(%s)',
                                json_encode(array('message' => $message,
-                                                 'autoClose' => true,
-                                                 'duration' => 10,
-                                                 'type' => 'information'))));
+                                                 'autoClose' => $autoclose,
+                                                 'duration' => $duration,
+                                                 'type' => $type))));
   }
 
 
diff --git a/tests/application/modules/opac/controllers/IndexControllerTest.php b/tests/application/modules/opac/controllers/IndexControllerTest.php
index 6ad8cab0275..6638f456d36 100644
--- a/tests/application/modules/opac/controllers/IndexControllerTest.php
+++ b/tests/application/modules/opac/controllers/IndexControllerTest.php
@@ -18,9 +18,9 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
 
 
+class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
     Class_Adminvar::beVolatile();
@@ -57,8 +57,6 @@ class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
 
 
 class IndexControllerAsInviteTest extends AbstractControllerTestCase {
-
-
   protected function _loginHook($account) {
     $account->ROLE_LEVEL = ZendAfi_Acl_AdminControllerRoles::INVITE;
   }
@@ -126,7 +124,6 @@ class IndexControllerAsAdminWithCssEditorAndNoHeaderCss extends IndexControllerA
 
 
 
-
 class IndexControllerAsAdminWithCSSEditorTest extends IndexControllerAsAdminTestCase {
   public function setUp() {
     parent::setUp();
@@ -149,6 +146,7 @@ class IndexControllerAsAdminWithCSSEditorTest extends IndexControllerAsAdminTest
 }
 
 
+
 class IndexControllerWithInvitedLevelRestrictionForProfilTest extends AbstractControllerTestCase {
   public function setup() {
     parent::setup();
@@ -313,4 +311,83 @@ class IndexControllerRewriteUrlTest extends AbstractControllerTestCase {
     $this->assertController('auth');
     $this->assertAction('login');
   }
+}
+
+
+
+abstract class IndexControllerCnilTrackingTestCase extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+  protected $_expected_message = 'showNotification({"message":"En poursuivant votre navigation sur ce site, vous acceptez l\'utilisation de cookies.","autoClose":false';
+
+  public function tearDown() {
+    Class_Cnil::setCookieJar(null);
+    Class_Cnil::setTimeSource(null);
+    parent::tearDown();
+  }
+}
+
+
+
+class IndexControllerCnilTrackingFirstVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+  protected $_cookie_jar;
+
+  public function setUp() {
+    parent::setUp();
+
+    Class_Cnil::setTimeSource($this->mock()
+                              ->whenCalled('time')
+                              ->answers(strtotime('2015-09-25 15:45:54')));
+
+    Class_Cnil::setCookieJar($this->_cookie_jar = $this->mock()
+                             ->whenCalled('setcookie')
+                             ->with('cnil8801b6c24c4d369a55a96252ed121d5c',
+                                    1,
+                                    1476884754)
+                             ->answers(null));
+
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldDisplayCnilMessage() {
+    $this->assertXPathContentContains('//script', $this->_expected_message);
+  }
+
+
+  /** @test */
+  public function shouldSetCnilCookieFor13Months() {
+    $this->assertTrue($this->_cookie_jar->methodHasBeenCalled('setcookie'));
+  }
+}
+
+
+
+class IndexControllerCnilTrackingSecondVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+  /** @test */
+  public function shouldNotDisplayCnilMessage() {
+    $this->dispatch('/', true);
+    $this->_response->setBody('');
+    Class_ScriptLoader::resetInstance();
+
+    $this->dispatch('/', true);
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
+}
+
+
+
+class IndexControllerCnilTrackingExistingCookieVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+  /** @test */
+  public function shouldNotDisplayCnilMessage() {
+    $_COOKIE['cnil' . md5(BASE_URL)] = 1;
+    $this->dispatch('/', true);
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
 }
\ No newline at end of file
-- 
GitLab