diff --git a/VERSIONS_HOTLINE/29524 b/VERSIONS_HOTLINE/29524
new file mode 100644
index 0000000000000000000000000000000000000000..8095394619296c5a4fc4e788d388cb30d629979c
--- /dev/null
+++ b/VERSIONS_HOTLINE/29524
@@ -0,0 +1 @@
+ - ticket #29524 : Mise en conformité CNIL sur les cookies de traçage (Ajout du message de consentement et de la page d'information sur les cookies).
\ No newline at end of file
diff --git a/application/modules/opac/controllers/HelpController.php b/application/modules/opac/controllers/HelpController.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d5819dd78cf0ec8c51cdbab65dad0f4b886a52b
--- /dev/null
+++ b/application/modules/opac/controllers/HelpController.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Copyright (c) 2012, 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 HelpController extends ZendAfi_Controller_Action {
+  public function cookiesAction() {
+    $this->view->ga_warning = "";
+    if (Class_AdminVar::getGoogleAnalyticsId()) {
+      $this->view->ga_warning = $this->view->help_GoogleAnalyticsWarning();
+    }
+  }
+}
diff --git a/application/modules/opac/views/scripts/help/cookies.phtml b/application/modules/opac/views/scripts/help/cookies.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..81fe0d8f1f4199c2d82ca8c25a5b8a59daf740cb
--- /dev/null
+++ b/application/modules/opac/views/scripts/help/cookies.phtml
@@ -0,0 +1,15 @@
+<h3>Qu'est-ce qu'un cookie ?</h3>
+
+<p>Un cookie est un fichier texte déposé, sous réserve de vos choix, sur votre ordinateur lors de la visite d'un site ou de la consultation d'une publicité. Il a pour but de collecter des informations relatives à votre navigation et de vous adresser des services adaptés à votre terminal (ordinateur, mobile ou tablette).
+
+Les cookies sont gérés par votre navigateur internet et seul l’émetteur d’un cookie est susceptible de lire ou de modifier les informations qui y sont contenues.</p>
+
+<h3>A quoi servent les cookies émis sur notre site ?</h3>
+
+<p>Nous utilisons uniquement des cookies visant à faciliter votre navigation. Il s'agit notamment des cookies suivants:
+<ul>
+ <li>les cookies de session utilisés pour le maintient de l'authentification.</li>
+ <li>les cookies permettent d'identifier les services et rubriques que l'utilisateur a visités.</li>
+</ul>
+
+<?php echo $this->ga_warning; ?>
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 87eeba675d6be62636ff5fd0abbcfa07fe512957..07d8641570a40cc7b3135a705dfae5bc29642933 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -162,7 +162,8 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
 
                     'TEXT_REPLACEMENTS' => Class_AdminVar_Meta::newRawText($this->_('Remplacement de textes à la volée. <br/>Ex:<br/>Panier;Sélection<br/>Vous avez %%d paniers;Vous avez %%d sélections')),
                     'URL_COSMOGRAMME' => Class_AdminVar_Meta::newDefault('')->bePrivate(),
-                    'PACK_MOBILE' => Class_AdminVar_Meta::newOnOff($this->_('Activation des fonctions avancées du téléphone'))->bePrivate()],
+                    'PACK_MOBILE' => Class_AdminVar_Meta::newOnOff($this->_('Activation des fonctions avancées du téléphone'))->bePrivate(),
+                    'CNIL_CONSENT_ENABLE' => Class_AdminVar_Meta::newOnOff('Affiche la demande de consentement avant l\'insertion de cookies ou autres traceurs')],
        'catalogue' => [
                        'OAI_SERVER' => Class_AdminVar_Meta::newOnOff($this->_('Activation du serveur OAI: permet le moissonnage des domaines par d\'autres logiciels via OAI'))],
        'newsletter' => [
diff --git a/library/Class/Cnil.php b/library/Class/Cnil.php
new file mode 100644
index 0000000000000000000000000000000000000000..01f3324dbf6d0c3a889d0b9fcfd9d68dff4d58ec
--- /dev/null
+++ b/library/Class/Cnil.php
@@ -0,0 +1,84 @@
+<?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() {
+    $read_more = Class_Url::absolute(['controller' => 'help',
+                                      'action' => 'cookies']);
+    Class_ScriptLoader::getInstance()
+      ->notify($this->_('En poursuivant votre navigation sur ce site, vous acceptez l\'utilisation de cookies. <a href="'.$read_more.'">En savoir plus</a>'),
+               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 a1fb35f6120797054dae23fd5d859b786c0b10e4..d2672af2a21f4dc5d0b537e6fdc33b13ac0f385e 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/library/ZendAfi/Controller/Plugin/CnilConsent.php b/library/ZendAfi/Controller/Plugin/CnilConsent.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf0b78107ad59710d9a4948c5e9cb4ed50b41ab9
--- /dev/null
+++ b/library/ZendAfi/Controller/Plugin/CnilConsent.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Copyright (c) 2012, 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
+ */
+//////////////////////////////////////////////////////////////////////////////////////////
+// OPAC3 : Conformité CNIL (consentement avant l'insertion de cookies)
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class ZendAfi_Controller_Plugin_CnilConsent extends Zend_Controller_Plugin_Abstract {
+  function preDispatch(Zend_Controller_Request_Abstract $request) {
+    if (Class_AdminVar::get('CNIL_CONSENT_ENABLE'))
+      (new Class_Cnil())->trackConsent();
+  }
+}
diff --git a/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php b/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cedc0e9dc0d213ffa4eb4d6409fa7f16b5ab6ad
--- /dev/null
+++ b/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (c) 2012, 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 ZendAfi_View_Helper_Help_GoogleAnalyticsWarning extends Zend_View_Helper_HtmlElement {
+  public function help_googleAnalyticsWarning() {
+    return $this->view->tag('p', $this->view->_('Pour les mesures de trafic dites de “Webanalytics”, 
+notre site utilise les services de Google Analytics. Pour bloquer l’utilisation des données par Google Analytics : https://tools.google.com/dlpage/gaoptout'));
+  }
+}
diff --git a/library/startup.php b/library/startup.php
index 13a6b864a550a66b324dfce7e9c9b9f7b007d07f..73107fe5798a8c9c91f54160a63f28404c1d0aac 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -276,6 +276,7 @@ function newFrontController() {
     ->registerPlugin(new ZendAfi_Controller_Plugin_CustomFields())
     ->registerPlugin(new ZendAfi_Controller_Plugin_Lectura())
     ->registerPlugin(new ZendAfi_Controller_Plugin_InspectorGadget())
+    ->registerPlugin(new ZendAfi_Controller_Plugin_CnilConsent())
     ->setParam('useDefaultControllerAlways', false);
 }
 
diff --git a/tests/application/modules/AbstractControllerTestCase.php b/tests/application/modules/AbstractControllerTestCase.php
index f4405305b0c16056ac5c337001d5e62d7d975b98..19a5457fa8b09265fa6a7d92233e92f72dad8a8a 100644
--- a/tests/application/modules/AbstractControllerTestCase.php
+++ b/tests/application/modules/AbstractControllerTestCase.php
@@ -144,6 +144,10 @@ abstract class AbstractControllerTestCase extends Zend_Test_PHPUnit_ControllerTe
       ->newInstanceWithId('PACK_MOBILE')
       ->setValeur(1);
 
+    $admin_var_loader
+      ->newInstanceWithId('CNIL_CONSENT_ENABLE')
+      ->setValeur(false);
+
     Class_AdminVar::newInstanceWithId('JS_STAT', ['valeur' => '']);
     ZendAfi_Controller_Action_Helper_TrackEvent::setDefaultWebAnalyticsClient(null);
     Storm_Cache::setDefaultZendCache(null);
diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
index 8749bc6df5f13876f7ab287cb273470a3ff50a45..2e7562cccee60c7479e4e034b7e8fc25fab766f7 100644
--- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
+++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
@@ -234,6 +234,12 @@ class AdminIndexControllerAdminVarActionAsAdminPortailTest extends Admin_Abstrac
   public function unexistingVarInDbShouldBeDisplayed() {
     $this->assertXpathContentContains('//td', 'AFFICHER_DISPONIBILITE_SUR_RECHERCHE');
   }
+
+
+  /** @test */
+  public function cnilVarShouldBeDisplay() {
+    $this->assertXPathContentContains('//td', 'CNIL_CONSENT_ENABLE');
+  }
 }
 
 
diff --git a/tests/application/modules/admin/controllers/UsersControllerTest.php b/tests/application/modules/admin/controllers/UsersControllerTest.php
index 8851f4524a09d45cb93fc5d40d9fa0f1bcd8f741..0c892f060deb7f8358e8c62f6fcde8ab0309d38c 100644
--- a/tests/application/modules/admin/controllers/UsersControllerTest.php
+++ b/tests/application/modules/admin/controllers/UsersControllerTest.php
@@ -1,4 +1,4 @@
- <?php
+<?php
 /**
  * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
  *
@@ -16,7 +16,7 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 require_once 'AbstractControllerTestCase.php';
 
@@ -68,7 +68,7 @@ abstract class UsersControllerWithMarcusTestCase extends AbstractControllerTestC
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_UserGroup')
       ->whenCalled('findAllBy')
       ->answers([]);
-    
+
     $this->assertTrue($this->marcus->isValid());
     $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
 
@@ -115,7 +115,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->dispatch('/admin/users/edit/id/10', true);
   }
 
-  
+
   /** @test **/
   public function roleLevelShouldBeSIGBSubscriber() {
     $this->assertXpathContentContains('//tr/td','abonné identifié SIGB');
@@ -127,7 +127,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertXPath("//input[@name='username'][@value='mmiller']");
   }
 
-  
+
   /** @test **/
   public function testPasswordIsMysecret() {
     $this->assertXPath("//input[@name='password'][@value='mysecret']");
@@ -145,7 +145,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertXPath("//input[@name='prenom'][@value='Marcus']");
   }
 
-  
+
   /** @test **/
   public function testGroupesAreMultimediaAndReferent() {
     $this->assertXPath("//input[@name='id_items'][@value='20-22']",$this->_response->getBody());
@@ -163,7 +163,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertNotXPath("//select[@name='role']");
   }
 
-  
+
   /** @test **/
   public function testSelectedBibIsIdOne() {
     $this->assertXPath("//input[@name='bib'][@value='1']", $this->_response->getBody());
@@ -385,7 +385,7 @@ class UsersControllerPostMarcusDataTest extends UsersControllerWithMarcusTestCas
   public function testOrdreabonIsTwo() {
     $this->assertEquals(2, $this->marcus->getOrdreabon());
   }
-  
+
   public function testPseudoIsDave() {
     $this->assertEquals('Dave', $this->marcus->getPseudo());
   }
@@ -419,7 +419,7 @@ class UsersControllerPostMarcusInvalidDataTest extends UsersControllerWithMarcus
     $this->assertAction('edit');
     $this->assertQueryContentContains('span#abonne_erreur', "Vous devez compléter le champ 'Identifiant'");
     $this->assertQueryContentContains('span#abonne_erreur', "Vous devez compléter le champ 'Mot de passe'");
-    $this->assertQueryContentContains('span#abonne_erreur', 
+    $this->assertQueryContentContains('span#abonne_erreur',
                                       "La bibliothèque est obligatoire pour le rôle : administrateur bibliothèque");
   }
 
@@ -503,7 +503,7 @@ class UsersControllerPostValidDataWithCommOpsysTest extends UsersControllerWithM
     $this->_postData();
     $this->assertRedirectTo('/admin/users');
 
-    $this->assertEquals($this->marcus, 
+    $this->assertEquals($this->marcus,
                         $this->user_loader->getFirstAttributeForLastCallOn('save'));
   }
 
@@ -563,13 +563,13 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
 
   public function setUp() {
     parent::setUp();
-    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');    
+    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
     $this->user_loader->whenCalled('getIdentity')
       ->answers($user=Class_Users::newInstanceWithId(2)
                 ->setLogin('referent')
                 ->setRoleLevel(ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
                 ->setPseudo('referent'));
-    
+
     $this->addUserToRightsReferent($user);
 
     $this->dispatch('/admin/users', true);
@@ -594,7 +594,7 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
     $this->assertNotXPathContentContains("//td",
                                          'Ajouter un utilisateur',$this->_response->getBody());
   }
-  
+
 
 
   /** @test **/
@@ -602,7 +602,7 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
     $this->assertNotXPath("//a[contains(@href, '/users/edit')]",
                           $this->_response->getBody());
   }
-  
+
 
 }
 
@@ -614,7 +614,7 @@ class UsersControllerAddPostTest extends UsersControllerWithMarcusTestCase {
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Newsletter')
       ->whenCalled('findAllBy')
       ->answers([]);
-    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');    
+    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
   }
 
 
@@ -666,7 +666,7 @@ class UsersControllerAddPostTest extends UsersControllerWithMarcusTestCase {
       ->with($this->marcus)
       ->never()
       ->getWrapper()
-      
+
       ->whenCalled('getIdentity')
       ->answers(Class_Users::getLoader()->newInstanceWithId(2)
                 ->setLogin('sysadm')
diff --git a/tests/application/modules/opac/controllers/HelpControllerTest.php b/tests/application/modules/opac/controllers/HelpControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..98b8746cf23f535c0a584d786dd5b2c04a128763
--- /dev/null
+++ b/tests/application/modules/opac/controllers/HelpControllerTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright (c) 2012, 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 HelpControlleriCnilComplianceTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->dispatch('/help/cookies', true);
+  }
+
+
+  /** @test */
+  public function pageShouldContainTitleQuEstCeQuUnCookie() {
+    $this->assertXPathContentContains('//h3', "Qu'est-ce qu'un cookie ?");
+  }
+
+
+  /** @test */
+  public function pageShouldExplainQuEstCeQuUnCookie() {
+    $this->assertXPathContentContains('//p', 'Un cookie est un fichier texte déposé, sous réserve de vos choix');
+  }
+
+
+  /** @test */
+  public function pageShouldContainTitleAQuoiServentLesCookiesEmisSurNotreSite() {
+    $this->assertXPathContentContains('//h3', "A quoi servent les cookies émis sur notre site ?");
+  }
+
+
+  /** @test */
+  public function pageShouldNotWarnAboutGoogleAnalytic() {
+    $this->assertNotXPathContentContains('//p', 'utilise les services de Google Analytics');
+  }
+}
+
+
+
+class HelpControlleriCnilComplianceWithGoogleAnalyticsTest extends AbstractControllerTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' =>'JS_STAT',
+                                      'valeur' => "<script> var _gaq = _gaq ||
+                                                  []; _gaq.push(['_setAccount',
+                                                  'UA-41754005-1']);
+                                                  _gaq.push(['_trackPageview']);
+                                                  (function() { var ga =
+                                                  document.createElement('script');
+                                                  ga.type = 'text/javascript'; ga.async =
+                                                  true; ga.src = ('https:' ==
+                                                  document.location.protocol ?
+                                                  'https://ssl' : 'http://www') +
+                                                  '.google-analytics.com/ga.js'; var
+                                                  s =
+                                                  document.getElementsByTagName('script')[
+                                                  0]; s.parentNode.insertBefore(ga, s);
+                                                  })(); </script>"]);
+    $this->dispatch('/help/cookies', true);
+  }
+
+
+  /** @test */
+  public function pageShouldNotWarnAboutGoogleAnalytic() {
+    $this->assertXPathContentContains('//p', 'utilise les services de Google Analytics');
+  }
+}
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/IndexControllerTest.php b/tests/application/modules/opac/controllers/IndexControllerTest.php
index 6ad8cab0275a27a9316aadd0e6c7e11bb916cc7d..36d8996037f46cded3cf868eac7faeac445609d6 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,119 @@ 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';
+  protected $_cookie_jar;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' => 'CNIL_CONSENT_ENABLE',
+                                      'valeur' => true]);
+
+    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('cnil' . md5(BASE_URL),
+                                    1,
+                                    1476884754)
+                             ->answers(null));
+  }
+
+
+  public function tearDown() {
+    Class_Cnil::setCookieJar(null);
+    Class_Cnil::setTimeSource(null);
+    parent::tearDown();
+  }
+}
+
+
+
+class IndexControllerCnilTrackingFirstVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldDisplayCnilMessage() {
+    $this->assertXPath('//script[contains(text(), "En poursuivant votre navigation")][contains(text(), "\/help\/cookies")][contains(text(), "En savoir plus")]', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function shouldSetCnilCookieFor13Months() {
+    $this->assertTrue($this->_cookie_jar->methodHasBeenCalled('setcookie'));
+  }
+}
+
+
+
+class IndexControllerCnilTrackingFirstVisitWithAdminVarDisabledTest
+  extends IndexControllerCnilTrackingTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' => 'CNIL_CONSENT_ENABLE',
+                                      'valeur' => false]);
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldDisplayCnilMessage() {
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
+}
+
+
+
+class IndexControllerCnilTrackingSecondVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/', true);
+    $this->_response->setBody('');
+    Class_ScriptLoader::resetInstance();
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldNotDisplayCnilMessageOf() {
+    $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
diff --git a/tests/library/Trait/UserGroupFixtures.php b/tests/library/Trait/UserGroupFixtures.php
index f5cd82eeed890ca12e5c4cdfd10065bdf5709f21..0d86dcb06056ec77c5efccddfc837ef0723e45d6 100644
--- a/tests/library/Trait/UserGroupFixtures.php
+++ b/tests/library/Trait/UserGroupFixtures.php
@@ -16,7 +16,7 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 trait Trait_UserGroupFixtures {
@@ -58,15 +58,10 @@ trait Trait_UserGroupFixtures {
                   ->newInstanceWithId(223)
                   ->setUserId($user->getId())
                   ->setUserGroupId(2222)]);
-    
+
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users')
       ->whenCalled('getIdentity')
       ->answers($user);
 
   }
-
-}
-
-?>
-
-
+}
\ No newline at end of file