diff --git a/application/modules/opac/controllers/ModulesController.php b/application/modules/opac/controllers/ModulesController.php
index 78b81b07cd5c403dbc23310bc65f9efa9f1b94c2..64d8d3f33f55ab92d0a29d6742c2ed46eef27d9d 100644
--- a/application/modules/opac/controllers/ModulesController.php
+++ b/application/modules/opac/controllers/ModulesController.php
@@ -47,11 +47,13 @@ class ModulesController extends Zend_Controller_Action {
 		$this->_redirect($numilog->getDynamiqueUrl());
 	}
 
+
   public function numeriquepremiumAction() {
 		$url = $this->_getParam('url');
     $this->checkNotifyMessage(NULL, $url);
   }
 
+
 	public function vodeclicAction() {
 		$vodeclic = new Class_Systeme_ModulesMenu_Vodeclic();
 		$vodeclic->afterLoginRedirectTo($this->_request->getServer('HTTP_REFERER'));
@@ -87,6 +89,13 @@ class ModulesController extends Zend_Controller_Action {
 	}
 
 
+	public function kidilanguesAction() {
+		$kidilangues = new Class_Systeme_ModulesMenu_Kidilangues();
+		$kidilangues->afterLoginRedirectTo($this->_request->getServer('HTTP_REFERER'));
+		$this->checkNotifyMessage($kidilangues, $kidilangues->getDynamiqueUrl());
+	}
+
+
 	protected function checkNotifyMessage($ressource_link, $url) {
 		if ('' == $url) {
 			$this->_helper->notify($ressource_link->getMessage());
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 400523050e3fcd08c2c3f2a619cbe4413067b978..59f69c551b25e3c808f74b473784cb95370d354b 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -205,6 +205,14 @@ class Class_AdminVar extends Storm_Model_Abstract {
 	 }
 
 
+	 public static function isKidilanguesEnabled() {
+		 return (('' != self::get('KIDILANGUES_ID'))
+						 && ('' != self::get('KIDILANGUES_KEY'))
+						 && ('' != self::get('KIDILANGUES_LOGIN'))
+						 && ('' != self::get('KIDILANGUES_PWD')));
+	 }
+
+
 	 /**
 		* @return bool
 		*/
@@ -605,7 +613,20 @@ class Class_AdminVar extends Storm_Model_Abstract {
 														'ORPHEA_USERNAME' => ['description' => 'Nom de l\'utilisateur Orphea'],
 														'ORPHEA_PWD' => ['description' => 'Mot de passe de l\'utilisateur Orphea'],
 														'ORPHEA_LANGUAGE' => ['description' => 'Paramètre permettant de choisir la langue des des ressources. La liste des codes disponibles se trouve à cette adresse http://help.orphea.com/43/gateway/#language_identifiers'],
-			];
+														'KIDILANGUES_KEY' => [
+																						'description' => 'Clé d\'encodage Kidilangues pour le portail. Cette clé doit être fournie par Kidilangues.',
+														],
+														'KIDILANGUES_ID' => [
+																						'description' => 'Clé d\'identification Kidilangues pour le portail. Cette clé doit être fournie par Kidilangues.',
+														],
+														'KIDILANGUES_LOGIN' => [
+																						'description' => 'Login du portail fourni par Kidilangues.',
+														],
+														'KIDILANGUES_PWD' => [
+																						'description' => 'Paswword du portail fourni par Kidilangues.',
+														],
+
+];
 
 		return self::$_knownVars;
 	}
diff --git a/library/Class/KidilanguesLink.php b/library/Class/KidilanguesLink.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6391e9181eb150599968a0ae13f9b6235b82fbe
--- /dev/null
+++ b/library/Class/KidilanguesLink.php
@@ -0,0 +1,67 @@
+<?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 Class_KidilanguesLink {
+	protected $_user,
+		$_home_page = 'cfcd208495d565ef66e7dff9f98764da';
+
+	public static function forUser($user) {
+		return new self($user);
+	}
+
+
+	public function __construct($user) {
+		$this->_user = $user;
+	}
+
+
+	public function baseUrl() {
+		return 'http://www.kidilangues.fr/directaccess/biblio';
+	}
+
+	public static function staticUrl() {
+		return Class_Url::assemble(['controller' => 'modules',
+																'action' => 'kidilangues']);
+	}
+
+
+	public function url() {
+		$params = [];
+		$params[] = Class_AdminVar::get('KIDILANGUES_ID');
+		$params[] = md5(Class_AdminVar::get('KIDILANGUES_LOGIN'));
+		$params[] = $this->kidilanguesEncode(Class_AdminVar::get('KIDILANGUES_PWD'));
+		$params[] = $this->_home_page;
+
+		return $this->baseUrl() . '/' . implode('/', $params);
+	}
+
+
+	public function kidilanguesEncode($pwd) {
+		return rtrim(strtr(base64_encode($this->kidilanguesEncrypt($pwd)), '+/', '-_'), '=');
+	}
+
+
+	public function kidilanguesEncrypt($data_to_encrypt) {
+		$key = Class_AdminVar::get('KIDILANGUES_KEY');
+		return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data_to_encrypt, MCRYPT_MODE_CBC, md5($key));
+	}
+}
+?>
\ No newline at end of file
diff --git a/library/Class/Systeme/ModulesMenu.php b/library/Class/Systeme/ModulesMenu.php
index ae1804ea538326f99e29f5b735c1dcfc9d162ce2..b8a2c7fde81f27181aba17c2eca1e08b6ba2cb37 100644
--- a/library/Class/Systeme/ModulesMenu.php
+++ b/library/Class/Systeme/ModulesMenu.php
@@ -122,6 +122,7 @@ class Class_Systeme_ModulesMenu extends Class_Systeme_ModulesAbstract {
 												 "LEKIOSK" => new Class_Systeme_ModulesMenu_LeKiosk(),
 												 "MUSICME" => new Class_Systeme_ModulesMenu_MusicMe(),
 												 "MYCOW" => new Class_Systeme_ModulesMenu_MyCow(),
+												 "KIDLILANGUES" => new Class_Systeme_ModulesMenu_Kidilangues(),
 												 "RESERVER_POSTE" => new Class_Systeme_ModulesMenu_ReserverPoste(),
 												 'SUGGESTION_ACHAT' => new Class_Systeme_ModulesMenu_SuggestionAchat(),
 												 'WEBKIOSK_RESERVATION' => new Class_Systeme_ModulesMenu_WebkioskReservation()
diff --git a/library/Class/Systeme/ModulesMenu/Kidilangues.php b/library/Class/Systeme/ModulesMenu/Kidilangues.php
new file mode 100644
index 0000000000000000000000000000000000000000..053c107d01777bb5fc18609e996311126a51e5a1
--- /dev/null
+++ b/library/Class/Systeme/ModulesMenu/Kidilangues.php
@@ -0,0 +1,56 @@
+<?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_Systeme_ModulesMenu_Kidilangues extends Class_Systeme_ModulesMenu_SSOAbstract {
+	/** @var string */
+	protected $_group = Class_Systeme_ModulesMenu::GROUP_MENU_ABONNES;
+
+	/** @var string */
+	protected $_type_module = 'KIDILANGUES';
+
+	/** @var string */
+	protected $_libelle = 'Lien vers Kidilangues';
+
+
+	public function urlForUser($user) {
+		if ($user && $user->hasRightAccesKidilangues() && Class_AdminVar::isKidilanguesEnabled()) {
+			return Class_KidilanguesLink::forUser($user)->url();
+		}
+		$this->setMessage('Vous n\'avez pas accès à cette ressource.');
+		return '';
+	}
+
+
+	public function getUrl($preferences=[]) {
+		return Class_KidilanguesLink::staticUrl();
+	}
+
+	public function shouldOpenInNewWindow($preferences) {
+		return null != Class_Users::getIdentity();
+	}
+
+
+	public function isVisibleForProfil($profil) {
+		return Class_AdminVar::isKidilanguesEnabled();
+	}
+}
+?>
\ No newline at end of file
diff --git a/library/Class/UserGroup.php b/library/Class/UserGroup.php
index 5b79c7e15ba37ff0ec0b95b1cc564edde7edcad7..1191687e2987fdab8b6c6737f7e6245eafe05d5f 100644
--- a/library/Class/UserGroup.php
+++ b/library/Class/UserGroup.php
@@ -54,6 +54,7 @@ class Class_UserGroup extends Storm_Model_Abstract {
 	const RIGHT_ACCES_LEKIOSK = 21;
 	const RIGHT_ACCES_CYBERLIBRIS = 22;
 	const RIGHT_ACCES_MYCOW = 24;
+	const RIGHT_ACCES_KIDILANGUES = 25;
 
 	// droits moderateurs
 	const RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT = 8;
@@ -91,6 +92,7 @@ class Class_UserGroup extends Storm_Model_Abstract {
 		self::RIGHT_ACCES_MUSICME => 'Bibliothèque numérique: accéder à Music Me',
 		self::RIGHT_ACCES_LEKIOSK => 'Bibliothèque numérique: accéder à Lekiosk.com',
 		self::RIGHT_ACCES_MYCOW => 'Bibliothèque numérique: accéder à MyCOW.EU',
+		self::RIGHT_ACCES_KIDILANGUES => 'Bibliothèque numérique: accéder à Kidilangues',
 		self::RIGHT_USER_DOMAINES_SUPPRESSION_LIMIT => 'Domaines: accès, modification et suppression limitée au créateur',
 		self::RIGHT_USER_DOMAINES_TOTAL_ACCESS => 'Domaines: accès total en modification et suppression',
 		self::RIGHT_USER_FILE_ACCESS => 'Articles: accès sur les répertoires images et file',
@@ -138,7 +140,8 @@ class Class_UserGroup extends Storm_Model_Abstract {
 								'ToutApprendre' => [Class_UserGroup::RIGHT_ACCES_TOUTAPPRENDRE],
 								'MusicMe' => [Class_UserGroup::RIGHT_ACCES_MUSICME],
 								'NumeriquePremium' => [Class_UserGroup::RIGHT_ACCES_NUMERIQUEPREMIUM],
-								'MyCow' => [Class_UserGroup::RIGHT_ACCES_MYCOW]
+								'MyCow' => [Class_UserGroup::RIGHT_ACCES_MYCOW],
+								'Kidilangues' => [Class_UserGroup::RIGHT_ACCES_KIDILANGUES],
 		];
 
 		foreach($mapping as $type => $tokens) {
diff --git a/library/Class/Users.php b/library/Class/Users.php
index bacfb7debed2f21d6618a9957ea9b80178e78cca..ca1ddc9ed891a4cebdf5bacba63d30b09518ee43 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -759,6 +759,11 @@ class Class_Users extends Storm_Model_Abstract {
 	}
 
 
+	public function hasRightAccesKidilangues() {
+		return $this->isAbonneAndHasRightToAccess(Class_UserGroup::RIGHT_ACCES_KIDILANGUES);
+	}
+
+
 	public function getFirstAvisByIdNotice($id_notice) {
 		$notice = Class_Notice::getLoader()->find($id_notice);
 		$avis = $notice->getAvisByUser($this);
diff --git a/tests/TestSpeedTrap.php b/tests/TestSpeedTrap.php
index 8f712ce99352faa584b7711cd8391675ef034680..db39c54f9af3bd07735ec18d297c6f2e71721a9b 100644
--- a/tests/TestSpeedTrap.php
+++ b/tests/TestSpeedTrap.php
@@ -16,29 +16,23 @@
  *
  * 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
  */
 
 /**
  * @package Tests
  */
 class TestSpeedTrap extends PHPUnit_Util_Printer  implements PHPUnit_Framework_TestListener {
-	protected static $_speed_trapped_test = [],
-		$_speed_limit = 0.1;
 
-	protected $_trapped_test = [];
+	protected static $_speed_trapped_test = [],
+		$_speed_limit = 0.01;
 
 
 	public static function printSpeedTrappedTests() {
-		if(!$_trapped = self::sortTrapped())
-			return;
-
-		$_high_score_in_seconds = self::getHighScore($_trapped);
-		$_high_score_speed_test = self::getHighSpeedTest($_trapped);
-
-		self::renderScore($_high_score_in_seconds, $_high_score_speed_test);
+		self::sortTrapped();
+		self::renderScore();
 	}
-  
+
 	/**
 	 * called when test is ended - determines if it was long and prints
 	 * @param PHUnit_Framework_Test $test
@@ -47,9 +41,9 @@ class TestSpeedTrap extends PHPUnit_Util_Printer  implements PHPUnit_Framework_T
 	public function endTest(PHPUnit_Framework_Test $test, $length)
 	{
 		if ($length > self::$_speed_limit)
-			$this->collect(['name' => $test->getName(), 'speed' => $length]);
+			$this->collect($test->getName(), round($length, 4));
 	}
-    
+
 	/**
 	 * Used to print error in error colors
 	 * @param string $error
@@ -58,10 +52,10 @@ class TestSpeedTrap extends PHPUnit_Util_Printer  implements PHPUnit_Framework_T
 	{
 		print  <<<END
 
-[ $error ] ⤞  $speed seconds
+ [ $error ] ⤞  $speed seconds
 END;
 	}
-    
+
 	/**
 	 * Required for Interface
 	 * (non-PHPdoc)
@@ -96,14 +90,14 @@ END;
 	 * @see PHPUnit_Framework_TestListener::addSkippedTest()
 	 */
 	public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
-    
+
 	/**
 	 * Required for Interface
 	 * (non-PHPdoc)
 	 * @see PHPUnit_Framework_TestListener::startTestSuite()
 	 */
 	public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
-    
+
 	/**
 	 * Required for Interface
 	 * (non-PHPdoc)
@@ -114,43 +108,59 @@ END;
 	public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
 
 
-	protected function collect($trapped) {
-		// disable: crash gitlab-ci runner 
-		// self::$_speed_trapped_test[floor($trapped['speed'])][$trapped['name']] = round($trapped['speed'], 2);
+	protected function collect($name, $speed) {
+		$count = count(self::$_speed_trapped_test);
+		if($count < 10 ) {
+			return self::$_speed_trapped_test[$name] = $speed;
+		}
+
+		$min_speed = min(self::$_speed_trapped_test);
+		$min_name = array_search($min_speed, self::$_speed_trapped_test);
+
+		if($speed > $min_speed) {
+			unset(self::$_speed_trapped_test[$min_name]);
+			self::$_speed_trapped_test[$name] = $speed;
+		}
 	}
 
-	
+
 	protected static function sortTrapped() {
 		if(!count(self::$_speed_trapped_test))
 			return false;
-		
-		krsort(self::$_speed_trapped_test);
+
+		arsort(self::$_speed_trapped_test);
 		reset(self::$_speed_trapped_test);
 		return self::$_speed_trapped_test;
 	}
 
-	
-	protected static function getHighScore($trapped_tests) {
-		return key($trapped_tests);
-	}
 
+	protected static function renderScore() {
+		$speed_limit= self::$_speed_limit;
 
-	protected static function getHighSpeedTest($trapped_tests) {
-		$high_score_tests = current($trapped_tests);
-		arsort($high_score_tests);
-		return $high_score_tests;
-	}
+		if(1 > ($count = count(self::$_speed_trapped_test))) {
+			print <<<END
+
+ :D  http://ljdchost.com/zLWnDY0.gif
+END;
+
+			return;
+		}
 
-	
-	protected static function renderScore($high_score_in_second, $high_score_tests) {
-		$emot = count($high_score_tests) > 1 ? '😣' : '😄';
-		$header = $emot.' SpeedTrap score: ' . $high_score_in_second . ' seconds.';
 		print <<<END
 
-$header
+ :(  http://ljdchost.com/UlaUCrU.gif
+
 END;
-		foreach($high_score_tests as $test_name => $speed )
+
+		foreach(self::$_speed_trapped_test as $test_name => $speed )
 			self::printError($test_name, $speed);
+
+		print <<<END
+
+
+Speed trap set to : $speed_limit second / test running
+
+END;
 	}
 }
 ?>
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/ModulesControllerKidilanguesTest.php b/tests/application/modules/opac/controllers/ModulesControllerKidilanguesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc67950214fddba6a35561e969f65f7a5420e4c5
--- /dev/null
+++ b/tests/application/modules/opac/controllers/ModulesControllerKidilanguesTest.php
@@ -0,0 +1,131 @@
+<?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
+ */
+include_once('tests/fixtures/RessourcesNumeriquesFixtures.php');
+
+
+abstract class ModulesControllerKidilanguesActivatedTestCase extends AbstractControllerTestCase {
+	public function setUp() {
+		parent::setUp();
+		Storm_Model_Loader::defaultToVolatile();
+		RessourcesNumeriquesFixtures::activateKidilangues();
+		ZendAfi_Auth::getInstance()->clearIdentity();
+	}
+
+
+	public function tearDown() {
+		Storm_Model_Loader::defaultToDb();
+		RessourcesNumeriquesFixtures::deactivateKidilangues();
+		parent::tearDown();
+	}
+}
+
+
+
+class ModulesControllerKidilanguesNoAccessRightTest extends ModulesControllerKidilanguesActivatedTestCase {
+  public function setUp() {
+    parent::setUp();
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+
+    $this->dispatch('/opac/modules/kidilangues', true);
+	}
+
+
+	/** @test */
+	public function shouldHaveAccessForbiddenMessage() {
+		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
+	}
+}
+
+
+
+class ModulesControllerKidilanguesLoggedTest extends ModulesControllerKidilanguesActivatedTestCase {
+  public function setUp() {
+    parent::setUp();
+
+		$group = $this->fixture('Class_UserGroup', ['id' => 1])
+									->addRight(Class_UserGroup::RIGHT_ACCES_KIDILANGUES);
+		$group->save();
+
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+		$user = Class_Users::getIdentity();
+		$user->setUserGroups([$group])
+				 ->beAbonneSIGB()
+				 ->save();
+    $this->dispatch('/opac/modules/kidilangues', true);
+	}
+
+
+	/** @test */
+	public function shouldContainsScriptToRedirectToKidilangueSSOUrl() {
+		$this->assertXPathContentContains('//script', 'window.location="http://www.kidilangues.fr/directaccess/biblio/dcc509a6f7a4238a0b95849bc4c23820/fc2f6178abeec3a91654adc3f22419fd/Ud4x5rY2DddxHOL9AuC3pNGbnlKua8e4HK2SiO8JwWc/cfcd208495d565ef66e7dff9f98764da";', $this->_response->getBody());
+	}
+}
+
+
+
+class ModulesControllerKidilanguesNotLoggedTest extends ModulesControllerKidilanguesActivatedTestCase {
+  public function setUp() {
+    parent::setUp();
+		$_SERVER['HTTP_REFERER'] = '/opac/modules/kidilangues';
+    $this->dispatch('/opac/modules/kidilangues', true);
+	}
+
+
+	/** @test */
+	public function shouldRedirectToLogin() {
+		$this->assertContains('/auth/login?redirect='.urlencode('/opac/modules/kidilangues'),
+													$this->_response->getBody());
+	}
+}
+
+
+
+class ModulesControllerKidilanguesDeactivateAndUserLoggedTest extends ModulesControllerKidilanguesActivatedTestCase {
+  public function setUp() {
+    parent::setUp();
+
+		RessourcesNumeriquesFixtures::deactivateKidilangues();
+
+		$_SERVER['HTTP_REFERER'] = '/index';
+		$group = $this->fixture('Class_UserGroup', ['id' => 1])
+									->addRight(Class_UserGroup::RIGHT_ACCES_KIDILANGUES);
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+		$user = Class_Users::getIdentity();
+		$user->setUserGroups([$group])->save();
+
+    $this->dispatch('/opac/modules/kidilangues', true);
+	}
+
+
+	/** @test */
+	public function shouldHaveAccessForbiddenMessage() {
+		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
+	}
+}
diff --git a/tests/application/modules/opac/controllers/ModulesControllerMyCowTest.php b/tests/application/modules/opac/controllers/ModulesControllerMyCowTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..afbb44232164375275e92811e0c75923f76a97fd
--- /dev/null
+++ b/tests/application/modules/opac/controllers/ModulesControllerMyCowTest.php
@@ -0,0 +1,139 @@
+<?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
+ */
+include_once('tests/fixtures/RessourcesNumeriquesFixtures.php');
+
+
+class ModulesControllerMyCowSsoTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/opac/modules/mycow-sso/eid/afi/uid/tom', true);
+  }
+
+
+	/** @test */
+	public function postActionShouldBeMyCowConnectSSO() {
+		$this->assertXPath('//form[contains(@action, "mycow.eu/connectSSO.php")]');
+	}
+
+
+	/** @test */
+	public function postEidValueShouldBeAfi() {
+		$this->assertXPath('//form/input[@name="eid"][@value="afi"]');
+	}
+
+
+	/** @test */
+	public function postUidShouldBeTom() {
+		$this->assertXPath('//form//input[@name="uid"][@value="tom"]');
+	}
+
+
+	/** @test */
+	public function mycowShouldContainsPostJs() {
+		$this->assertXPath('//script', "('#mycow_form').submit()");
+	}
+}
+
+
+
+class ModulesControllerMyCowNoAccessRightTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/opac/modules/mycow', true);
+	}
+
+
+	/** @test */
+	public function shouldHaveAccessForbiddenMessage() {
+		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
+	}
+}
+
+
+
+class ModulesControllerMyCowLoggedTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+
+		RessourcesNumeriquesFixtures::activateMyCow();
+
+		$group = $this->fixture('Class_UserGroup', ['id' => 1])
+									->addRight(Class_UserGroup::RIGHT_ACCES_MYCOW);
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+		$user = Class_Users::getIdentity();
+		$user->setUserGroups([$group])->save();
+    $this->dispatch('/opac/modules/mycow', true);
+	}
+
+
+	/** @test */
+	public function shouldHaveAccessForbiddenMessage() {
+		$this->assertContains('/modules/mycow-sso/eid/afi-sa-test/uid/admin', $this->_response->getBody());
+	}
+}
+
+
+
+class ModulesControllerMyCowNotLoggedTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+		ZendAfi_Auth::getInstance()->clearIdentity();
+		RessourcesNumeriquesFixtures::activateMyCow();
+		$_SERVER['HTTP_REFERER'] = '/opac/modules/mycow';
+    $this->dispatch('/opac/modules/mycow', true);
+	}
+
+
+	/** @test */
+	public function shouldRedirectToLogin() {
+		$this->assertContains('/auth/login?redirect='.urlencode('/opac/modules/mycow'),
+													$this->_response->getBody());
+	}
+}
+
+
+
+class ModulesControllerMyCowDeactivateAndUserLoggedTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+		RessourcesNumeriquesFixtures::deactivateMyCow();
+		$_SERVER['HTTP_REFERER'] = '/index';
+		$group = $this->fixture('Class_UserGroup', ['id' => 1])
+									->addRight(Class_UserGroup::RIGHT_ACCES_MYCOW);
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+		$user = Class_Users::getIdentity();
+		$user->setUserGroups([$group])->save();
+
+    $this->dispatch('/opac/modules/mycow', true);
+	}
+
+
+	/** @test */
+	public function shouldHaveAccessForbiddenMessage() {
+		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
+	}
+}
diff --git a/tests/application/modules/opac/controllers/ModulesControllerTest.php b/tests/application/modules/opac/controllers/ModulesControllerTest.php
index b75caf6d1b07ec998586402e3cac0b8c51466384..698a86bb576b6b54beb710c10dc32c90d372da4b 100644
--- a/tests/application/modules/opac/controllers/ModulesControllerTest.php
+++ b/tests/application/modules/opac/controllers/ModulesControllerTest.php
@@ -19,7 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 require_once 'AbstractControllerTestCase.php';
-
+include_once('tests/fixtures/RessourcesNumeriquesFixtures.php');
 
 class ModulesControllerLoginRequiredTest extends AbstractControllerTestCase {
 	public function datas() {
@@ -148,123 +148,4 @@ class ModulesControllerNumeriquepremiumTest extends AbstractControllerTestCase {
   public function numeriquepremiumShouldContainScript() {
     $this->assertXPathContentContains('//script', 'http://foo/', $this->_response->getBody());
   }
-}
-
-
-
-class ModulesControllerMyCowSsoTest extends AbstractControllerTestCase {
-  public function setUp() {
-    parent::setUp();
-    $this->dispatch('/opac/modules/mycow-sso/eid/afi/uid/tom', true);
-  }
-
-
-	/** @test */
-	public function postActionShouldBeMyCowConnectSSO() {
-		$this->assertXPath('//form[contains(@action, "mycow.eu/connectSSO.php")]');
-	}
-
-
-	/** @test */
-	public function postEidValueShouldBeAfi() {
-		$this->assertXPath('//form/input[@name="eid"][@value="afi"]');
-	}
-
-
-	/** @test */
-	public function postUidShouldBeTom() {
-		$this->assertXPath('//form//input[@name="uid"][@value="tom"]');
-	}
-
-
-	/** @test */
-	public function mycowShouldContainsPostJs() {
-		$this->assertXPath('//script', "('#mycow_form').submit()");
-	}
-}
-
-
-
-class ModulesControllerMyCowNoAccessRightTest extends AbstractControllerTestCase {
-  public function setUp() {
-    parent::setUp();
-    $this->dispatch('/opac/modules/mycow', true);
-	}
-
-
-	/** @test */
-	public function shouldHaveAccessForbiddenMessage() {
-		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
-	}
-}
-
-
-
-class ModulesControllerMyCowLoggedTest extends AbstractControllerTestCase {
-  public function setUp() {
-    parent::setUp();
-
-		RessourcesNumeriquesFixtures::activateMyCow();
-
-		$group = $this->fixture('Class_UserGroup', ['id' => 1])
-									->addRight(Class_UserGroup::RIGHT_ACCES_MYCOW);
-		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
-																												['id' => 1,
-																												 'login' => 'admin',
-																												 'password' => 'pwd']));
-		$user = Class_Users::getIdentity();
-		$user->setUserGroups([$group])->save();
-    $this->dispatch('/opac/modules/mycow', true);
-	}
-
-
-	/** @test */
-	public function shouldHaveAccessForbiddenMessage() {
-		$this->assertContains('/modules/mycow-sso/eid/afi-sa-test/uid/admin', $this->_response->getBody());
-	}
-}
-
-
-class ModulesControllerMyCowNotLoggedTest extends AbstractControllerTestCase {
-  public function setUp() {
-    parent::setUp();
-		ZendAfi_Auth::getInstance()->clearIdentity();
-		RessourcesNumeriquesFixtures::activateMyCow();
-		$_SERVER['HTTP_REFERER'] = '/opac/modules/mycow';
-    $this->dispatch('/opac/modules/mycow', true);
-	}
-
-
-	/** @test */
-	public function shouldRedirectToLogin() {
-		$this->assertContains('/auth/login?redirect='.urlencode('/opac/modules/mycow'),
-													$this->_response->getBody());
-	}
-}
-
-
-
-class ModulesControllerMyCowDeactivateAndUserLoggedTest extends AbstractControllerTestCase {
-  public function setUp() {
-    parent::setUp();
-		RessourcesNumeriquesFixtures::deactivateMyCow();
-		$_SERVER['HTTP_REFERER'] = '/index';
-		$group = $this->fixture('Class_UserGroup', ['id' => 1])
-									->addRight(Class_UserGroup::RIGHT_ACCES_MYCOW);
-		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
-																												['id' => 1,
-																												 'login' => 'admin',
-																												 'password' => 'pwd']));
-		$user = Class_Users::getIdentity();
-		$user->setUserGroups([$group])->save();
-
-    $this->dispatch('/opac/modules/mycow', true);
-	}
-
-
-	/** @test */
-	public function shouldHaveAccessForbiddenMessage() {
-		$this->assertFlashMessengerContentContains('Vous n\'avez pas accès à cette ressource.');
-	}
-}
-?>
+}
\ No newline at end of file
diff --git a/tests/fixtures/RessourcesNumeriquesFixtures.php b/tests/fixtures/RessourcesNumeriquesFixtures.php
index dd5ea62080a1c8d4211caf507843c93c00f83ad7..2236d3ea7f5f93c6696191669ad563b3ae5b4433 100644
--- a/tests/fixtures/RessourcesNumeriquesFixtures.php
+++ b/tests/fixtures/RessourcesNumeriquesFixtures.php
@@ -32,6 +32,7 @@ class RessourcesNumeriquesFixtures {
 		self::deactivateLekiosk();
 		self::deactivateMusicMe();
     self::deactivate1Dtouch();
+		self::deactivateKidilangues();
 	}
 
 
@@ -48,6 +49,7 @@ class RessourcesNumeriquesFixtures {
 		self::activateMusicMe();
     self::activate1Dtouch();
 		self::activateOrphea();
+		self::activateKidilangues();
 	}
 
 
@@ -209,5 +211,24 @@ class RessourcesNumeriquesFixtures {
 		Class_AdminVar::newInstanceWithId('ORPHEA_PWD', ['valeur' => '']);
 		Class_AdminVar::newInstanceWithId('ORPHEA_LANGUAGE', ['valeur' => '']);
 	}
+
+
+	public static function activateKidilangues() {
+		$key = '44f8d1e03fa8281584f0d2b13efcb4bdd0b2ef587001f34954a5d204ffc636a2';
+		$key = pack('H*', $key);
+
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_KEY', ['valeur' => $key]);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_ID', ['valeur' => 'dcc509a6f7a4238a0b95849bc4c23820']);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_LOGIN', ['valeur' => 'afi']);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_PWD', ['valeur' => 'password']);
+	}
+
+
+	public static function deactivateKidilangues() {
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_KEY', ['valeur' => '']);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_ID', ['valeur' => '']);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_LOGIN', ['valeur' => '']);
+		Class_AdminVar::newInstanceWithId('KIDILANGUES_PWD', ['valeur' => '']);
+	}
 }
 ?>
diff --git a/tests/library/Class/KidilanguesLinkTest.php b/tests/library/Class/KidilanguesLinkTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..464042f5437c8b0e2a61b24184912c109634965f
--- /dev/null
+++ b/tests/library/Class/KidilanguesLinkTest.php
@@ -0,0 +1,76 @@
+<?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
+ */
+
+include_once('tests/fixtures/RessourcesNumeriquesFixtures.php');
+
+
+abstract class KidilanguesLinkTestCase extends Storm_Test_ModelTestCase {
+	public function setUp() {
+		parent::setUp();
+		Storm_Model_Loader::defaultToVolatile();
+		RessourcesNumeriquesFixtures::activateKidilangues();
+		$group = $this->fixture('Class_UserGroup', ['id' => 1])
+									->addRight(Class_UserGroup::RIGHT_ACCES_KIDILANGUES);
+		$group->save();
+
+		ZendAfi_Auth::getInstance()->logUser($this->fixture('Class_Users',
+																												['id' => 1,
+																												 'login' => 'admin',
+																												 'password' => 'pwd']));
+		$user = Class_Users::getIdentity();
+		$user->setUserGroups([$group])
+				 ->beAbonneSIGB()
+				 ->save();
+	}
+
+
+	public function tearDown() {
+		Storm_Model_Loader::defaultToDb();
+		RessourcesNumeriquesFixtures::deactivateKidilangues();
+		ZendAfi_Auth::getInstance()->clearIdentity();
+		parent::tearDown();
+	}
+}
+
+
+
+class KidilanguesLinkUrlForUserTest extends KidilanguesLinkTestCase {
+	protected $_link;
+
+	public function setUp() {
+		parent::setUp();
+		$this->_link = $link = Class_KidilanguesLink::forUser(Class_Users::find(1));
+	}
+
+
+	/** @test */
+	public function encryptedPasswordShouldBeHasExpected() {
+		$this->assertEquals('Ud4x5rY2DddxHOL9AuC3pNGbnlKua8e4HK2SiO8JwWc=', base64_encode($this->_link->kidilanguesEncrypt(Class_AdminVar::get('KIDILANGUES_PWD'))));
+	}
+
+
+	/** @test */
+	public function base64EncryptedPasswordShouldBeHasExpected() {
+		$encrypted_pwd = $this->_link->kidilanguesEncrypt(Class_AdminVar::get('KIDILANGUES_PWD'));
+		$this->assertEquals('WWqQeIopvisfbLvlYNuHjUop7JHZXizJ35aRYOu_eOQ', $this->_link->kidilanguesEncode($encrypted_pwd));
+	}
+
+}
\ No newline at end of file
diff --git a/tests/library/Class/Systeme/ModulesMenuTest.php b/tests/library/Class/Systeme/ModulesMenuTest.php
index 3593eceb376b7ab1421d9d433288e6cef8b51d8a..555e6470574f39a5cf2f39e18cee0f448502d7b4 100644
--- a/tests/library/Class/Systeme/ModulesMenuTest.php
+++ b/tests/library/Class/Systeme/ModulesMenuTest.php
@@ -18,6 +18,7 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
+include_once('tests/fixtures/RessourcesNumeriquesFixtures.php');
 
 
 abstract class ModulesMenuBibNumeriqueTestCase extends Storm_Test_ModelTestCase {
@@ -30,7 +31,8 @@ abstract class ModulesMenuBibNumeriqueTestCase extends Storm_Test_ModelTestCase
 						['NUMILOG', 'Numilog'],
 						['ARTEVOD', 'ArteVod'],
 						['CVS', 'CVS'],
-						['MYCOW', 'MyCow']
+						['MYCOW', 'MyCOW.EU'],
+						['KIDILANGUES', 'Kidilangues'],
 		];
 	}
 }