diff --git a/.gitattributes b/.gitattributes
index 67959683a613c3b88436e497ab881412955236e3..4be8e222e146b82105092464021482f32a09a07a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -683,6 +683,7 @@ application/modules/opac/controllers/EtagereController.php -text
 application/modules/opac/controllers/FormulaireController.php -text
 application/modules/opac/controllers/IndexController.php -text
 application/modules/opac/controllers/JavaController.php -text
+application/modules/opac/controllers/ModulesController.php -text
 application/modules/opac/controllers/NoticeajaxController.php -text
 application/modules/opac/controllers/OaiController.php -text
 application/modules/opac/controllers/PanierController.php -text
@@ -5701,6 +5702,7 @@ tests/application/modules/opac/controllers/FormulaireControllerTest.php -text
 tests/application/modules/opac/controllers/IndexControllerTest.php -text
 tests/application/modules/opac/controllers/IndexControllerTranslationTest.php -text
 tests/application/modules/opac/controllers/JavaControllerTest.php -text
+tests/application/modules/opac/controllers/ModulesControllerTest.php -text
 tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php -text
 tests/application/modules/opac/controllers/OAI-PMH.xsd -text
 tests/application/modules/opac/controllers/OAIControllerGetRecordTest.php -text
@@ -5812,6 +5814,7 @@ tests/library/Class/Notice/simpledc20021212.xsd -text
 tests/library/Class/NoticeHtmlTest.php -text
 tests/library/Class/NoticeOAITest.php -text
 tests/library/Class/NoticeTest.php -text
+tests/library/Class/NumilogLinkTest.php -text
 tests/library/Class/OpdsCatalogTest.php -text
 tests/library/Class/PanierNoticeTest.php -text
 tests/library/Class/ProfilI18nStringExtractorTest.php -text
diff --git a/application/modules/opac/controllers/ModulesController.php b/application/modules/opac/controllers/ModulesController.php
new file mode 100644
index 0000000000000000000000000000000000000000..23e8a338de1ef310d10a1bb1cf7ce55a069420ac
--- /dev/null
+++ b/application/modules/opac/controllers/ModulesController.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 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).
+ *
+ * AFI-OPAC 2.0 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 AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+//////////////////////////////////////////////////////////////////////////////////////////
+// OPAC3 - Controleur UTILISATEURS
+//
+//@TODO@ :- faire tri sur les entetes de colonnes
+//				-	Faire une recherche par noms
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class ModulesController extends Zend_Controller_Action {
+	private $user;
+	function init() {
+				$this->user = ZendAfi_Auth::getInstance()->getIdentity();
+	}
+
+
+
+
+	public function cvsAction() {
+		$cvs = new Class_Systeme_ModulesMenu_CVS();
+		$this->_redirect($cvs->getDynamiqueUrl());
+		
+	}
+
+	public function numilogAction() {
+		$numilog = new Class_Systeme_ModulesMenu_Numilog();
+		$this->_redirect($numilog->getDynamiqueUrl());
+		
+	}
+
+	public function vodeclicAction() {
+		$vodeclic = new Class_Systeme_ModulesMenu_Vodeclic();
+		$this->_redirect($vodeclic->getDynamiqueUrl());
+		
+	}
+
+}
\ No newline at end of file
diff --git a/library/Class/CVSLink.php b/library/Class/CVSLink.php
index 5815c8950969f3a930b74fe60dd7898a7067daaf..cd52df13f2f915adbcf362653ac4b07a6b46b702 100644
--- a/library/Class/CVSLink.php
+++ b/library/Class/CVSLink.php
@@ -35,6 +35,10 @@ class Class_CVSLink extends Class_WebService_Abstract {
 		$this->_user = $user;
 	}
 
+	public static function staticLink() {
+		return BASE_URL.'/modules/cvs';
+	}
+
 
 	public function baseUrl() {
 		return 'http://stream.cvs-mediatheques.com/api/partners.php';
diff --git a/library/Class/NumilogLink.php b/library/Class/NumilogLink.php
index 298b477608087b528ae1b05450b7edbd3ecae23a..5d099b932ab229c31a9b1da97aca44cab0759061 100644
--- a/library/Class/NumilogLink.php
+++ b/library/Class/NumilogLink.php
@@ -36,6 +36,11 @@ class Class_NumilogLink extends Class_WebService_Abstract {
 	}
 
 
+	public static function staticLink() {
+		return 'opac/modules/numilog';
+	}
+
+
 	public function getBaseUrl() {
 		return Class_AdminVar::get('NUMILOG_URL');
 	}
diff --git a/library/Class/Systeme/ModulesMenu/CVS.php b/library/Class/Systeme/ModulesMenu/CVS.php
index 1ad7e377da7c936772ac6fda49549d09dc942723..970da04537d23f25e518229e4631d62cac34642d 100644
--- a/library/Class/Systeme/ModulesMenu/CVS.php
+++ b/library/Class/Systeme/ModulesMenu/CVS.php
@@ -38,11 +38,16 @@ class Class_Systeme_ModulesMenu_CVS extends Class_Systeme_ModulesMenu_Null {
 
 
 	public function getUrl($preferences=[]) {
+		return Class_CVSLink::staticLink();
+	}
+
+
+	public function getDynamiqueUrl() {
 		return ($user = Class_Users::getIdentity())
 		   ? $this->getCVSUrlForUser($user)
 		   : BASE_URL.'/auth/login';
-	}
 
+	}
 
 	public function shouldOpenInNewWindow($preferences) {
 		return null != Class_Users::getIdentity();
diff --git a/library/Class/Systeme/ModulesMenu/Numilog.php b/library/Class/Systeme/ModulesMenu/Numilog.php
index 64e31b8d8a99d8b7c8cf310b7460601c8c38d164..ba52605fcb0f7409a7a9a0a20670c541e8a1ddbd 100644
--- a/library/Class/Systeme/ModulesMenu/Numilog.php
+++ b/library/Class/Systeme/ModulesMenu/Numilog.php
@@ -36,12 +36,15 @@ class Class_Systeme_ModulesMenu_Numilog extends Class_Systeme_ModulesMenu_Null {
 	}
 
 	public function getUrl($preferences=[]) {
-		return ($user = Class_Users::getIdentity())
+		return BASE_URL.'/modules/numilog';
+	}
+
+	public function getDynamiqueUrl() {
+	return ($user = Class_Users::getIdentity())
 			? $this->getNumilogUrlForUser($user)
 			: Class_AdminVar::get('NUMILOG_URL');
 	}
 
-
 	public function shouldOpenInNewWindow($preferences) {
 		return null != Class_Users::getIdentity();
 	}
diff --git a/library/Class/Systeme/ModulesMenu/Vodeclic.php b/library/Class/Systeme/ModulesMenu/Vodeclic.php
index f5448679e216265fe28841633ec52fd03c6f59e7..ae6d21d7e65ccc93e8640b0c1bcba60561b163fa 100644
--- a/library/Class/Systeme/ModulesMenu/Vodeclic.php
+++ b/library/Class/Systeme/ModulesMenu/Vodeclic.php
@@ -37,12 +37,15 @@ class Class_Systeme_ModulesMenu_Vodeclic extends Class_Systeme_ModulesMenu_Null
 	}
 
 
-	public function getUrl($preferences=[]) {
+	public function getDynamiqueUrl() {
 		return ($user = Class_Users::getIdentity())
 		   ? $this->getVodeclicUrlForUser($user)
 		   : BASE_URL . '/auth/login';
 	}
-
+	
+	public function getUrl($preferences=[]) {
+		return Class_VodeclicLink::staticUrl();
+	}
 
 	public function shouldOpenInNewWindow($preferences) {
 		return null != Class_Users::getIdentity();
diff --git a/library/Class/VodeclicLink.php b/library/Class/VodeclicLink.php
index 1ab424af55b269175efe7f4c52cb50d197a3691b..b4d7300cfbe1f75578bd48976f57888c94c9a7aa 100644
--- a/library/Class/VodeclicLink.php
+++ b/library/Class/VodeclicLink.php
@@ -36,6 +36,10 @@ class Class_VodeclicLink {
 		return "https://biblio.vodeclic.com/auth/biblio/sso";
 	}
 
+	public static function staticUrl() {
+		return BASE_URL.'/modules/vodeclic';
+	}
+
 
 	public function url() {
 		$hash = Class_Hash::sha256WithKey(Class_AdminVar::get('VODECLIC_KEY'));
diff --git a/tests/application/modules/opac/controllers/ModulesControllerTest.php b/tests/application/modules/opac/controllers/ModulesControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c2f140acc7103341658092959a703d141df239f
--- /dev/null
+++ b/tests/application/modules/opac/controllers/ModulesControllerTest.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 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).
+ *
+ * AFI-OPAC 2.0 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 AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+require_once 'AbstractControllerTestCase.php';
+
+class ModulesControllerCVSTest extends AbstractControllerTestCase {
+	public function setUp() {
+		parent::setUp();
+
+
+		Class_AdminVar::newInstanceWithId('VODECLIC_KEY', ['valeur' => 1234]);
+		Class_AdminVar::newInstanceWithId('VODECLIC_ID', ['valeur' => 'afi']);
+		Class_AdminVar::newInstanceWithId('VODECLIC_BIB_ID', ['valeur' => '12']);
+		Class_AdminVar::newInstanceWithId('MULTIMEDIA_KEY', ['valeur' => 'zork']);
+		Class_AdminVar::newInstanceWithId('CVS_BMKEY',['valeur' => '22222']);
+		Class_AdminVar::newInstanceWithId('CVS_BMID',['valeur' => '22222']);
+		Class_AdminVar::newInstanceWithId('CVS_SOURCENAME',['valeur' => '22222']);
+		Class_AdminVar::newInstanceWithId('CVS_SOURCEID',['valeur' => '22222']);
+		Class_AdminVar::newInstanceWithId('CVS_SOURCEKEY',['valeur' => '22222']);
+		Class_AdminVar::newInstanceWithId('CVS_SOURCEPASSWORD',['valeur' => '22222']);
+	}
+
+
+
+
+	/** @test */
+	public function cvsUrlWithoutUserShouldBeLoginPage() {
+		ZendAfi_Auth::getInstance()->clearIdentity();
+		$this->dispatch('/opac/modules/cvs');
+		$this->assertRedirectTo(BASE_URL.'/auth/login');
+	
+	}
+
+
+}
+
+
+
+class ModulesControllerVodeclicTest extends AbstractControllerTestCase {
+	public function setUp() {
+		parent::setUp();
+		Class_AdminVar::newInstanceWithId('VODECLIC_KEY', ['valeur' => 1234]);
+		Class_AdminVar::newInstanceWithId('VODECLIC_ID', ['valeur' => 'afi']);
+		Class_AdminVar::newInstanceWithId('VODECLIC_BIB_ID', ['valeur' => '12']);
+	}
+
+
+
+
+	/** @test */
+	public function vodeclicUrlWithoutUserShouldBeLoginPage() {
+		ZendAfi_Auth::getInstance()->clearIdentity();
+		$this->dispatch('/opac/modules/vodeclic');
+		$this->assertRedirectTo(BASE_URL.'/auth/login');
+	
+	}
+
+
+}
+
+
+class ModulesControllerNumilogTest extends AbstractControllerTestCase {
+	public function setUp() {
+		parent::setUp();
+
+		Class_AdminVar::newInstanceWithId('NUMILOG_URL',['valeur' => 'http://numilogurl/action']);
+	}
+
+
+	/** @test */
+	public function numilogUrlWithoutUserShouldBeLoginPage() {
+		ZendAfi_Auth::getInstance()->clearIdentity();
+		$this->dispatch('/opac/modules/numilog');
+		$this->assertRedirectTo('http://numilogurl/action');
+	
+	}
+
+
+
+}
\ No newline at end of file
diff --git a/tests/library/Class/NumilogLinkTest.php b/tests/library/Class/NumilogLinkTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1065c25948c95553c74757cad8715be73e825bf6
--- /dev/null
+++ b/tests/library/Class/NumilogLinkTest.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 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).
+ *
+ * AFI-OPAC 2.0 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 AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+
+abstract class NumilogLinkTestCase extends Storm_Test_ModelTestCase {
+	public function setUp() {
+		parent::setUp();
+		Class_AdminVar::newInstanceWithId('NUMILOG_URL',['valeur' => 'http://urlnumilog/action']);
+
+	}
+}
+
+
+
+ class NumilogLinkWithAbonTest extends NumilogLinkTestCase {
+	protected 
+		$_http_client,
+		$_jerry;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->_jerry = Class_Users::getLoader()
+			->newInstanceWithId(4)
+			->setIdabon(34)
+			->setLogin(34)
+			->setPrenom('Jerry')
+			->setNom('Khan')
+			->setMail('feu@essence.fr')
+			->setDateFin('2023-09-02');
+
+		$this->_numilog = Class_NumilogLink::forUser($this->_jerry);
+
+		$this->_http_client = Storm_Test_ObjectWrapper::mock();
+
+		Class_NumilogLink::setDefaultHttpClient($this->_http_client);
+
+	}
+
+
+	/** @test */
+	public function urlForJerryShouldBeNumilogSSO() {
+		$this->assertEquals('http://urlnumilog/action',
+												$this->_numilog->getBaseUrl());
+	}
+
+  /** @test */
+	public function numilogUrlShouldReturnUrlWithTicket() {
+		$this->assertEquals('http://urlnumilog/action?ticket='.md5( Zend_Session::getId()), 
+												$this->_numilog->url());
+	}
+
+}
+?>
\ No newline at end of file
diff --git a/tests/library/Class/Systeme/ModulesMenuTest.php b/tests/library/Class/Systeme/ModulesMenuTest.php
index 65dfbf99e7b61b8dca014b27f421c897716d96aa..f24aa0e6778b79785a6fba73424b929f61971480 100644
--- a/tests/library/Class/Systeme/ModulesMenuTest.php
+++ b/tests/library/Class/Systeme/ModulesMenuTest.php
@@ -33,7 +33,7 @@ class ModulesMenuTest extends Storm_Test_ModelTestCase {
 		Class_AdminVar::newInstanceWithId('CVS_SOURCEID',['valeur' => '22222']);
 		Class_AdminVar::newInstanceWithId('CVS_SOURCEKEY',['valeur' => '22222']);
 		Class_AdminVar::newInstanceWithId('CVS_SOURCEPASSWORD',['valeur' => '22222']);
-
+		Class_AdminVar::newInstanceWithId('NUMILOG_URL',['valeur' => 'http://numilogurl/action']);
 
 		$this->module_menu = new Class_Systeme_ModulesMenu();
 	}
@@ -56,19 +56,21 @@ class ModulesMenuTest extends Storm_Test_ModelTestCase {
 	}
 
 
+
 	/** @test */
-	public function vodeclicUrlWithoutUserShouldBeLoginPage() {
+	public function cvsUrlShouldBeStaticPage() {
 		ZendAfi_Auth::getInstance()->clearIdentity();
-		$this->assertEquals(array('url' => BASE_URL.'/auth/login', 'target' => ''), 
-												$this->module_menu->getUrl('VODECLIC', array()));
+		$this->assertEquals(['url' => BASE_URL.'/modules/cvs', 'target' => ''], 
+												$this->module_menu->getUrl('CVS', array()));
 	}
 
 
+
 	/** @test */
-	public function cvsUrlWithoutUserShouldBeLoginPage() {
+	public function numilogUrlShouldBeStaticPage() {
 		ZendAfi_Auth::getInstance()->clearIdentity();
-		$this->assertEquals(array('url' => BASE_URL.'/auth/login', 'target' => ''), 
-												$this->module_menu->getUrl('CVS', array()));
+		$this->assertEquals(['url' => BASE_URL.'/modules/numilog', 'target' => ''], 
+												$this->module_menu->getUrl('NUMILOG', array()));
 	}
 
 
@@ -95,48 +97,66 @@ class ModulesMenuTest extends Storm_Test_ModelTestCase {
 			->beAbonneSIGB()
 			->setDateDebut('1999-02-10')
 			->setDateFin('2025-09-12');
+		$vodeclic= new Class_Systeme_ModulesMenu_Vodeclic();
 		
-		$menu_url = $this->module_menu->getUrl('VODECLIC', array());
-		$this->assertContains('vodeclic', $menu_url['url']);
-		$this->assertEquals('_blank', $menu_url['target']);
+		$url = $vodeclic->getDynamiqueUrl();
+		$this->assertContains('vodeclic', $url);
+
 	}
 
 
 	/** @test */
 	public function vodeclicUrlWithAbonnementInvalidShouldBeJSAlertAbonnementInvalid() {
 		$this->_logUserGaston();
+		$vodeclic= new Class_Systeme_ModulesMenu_Vodeclic();
+		
+		$url = $vodeclic->getDynamiqueUrl();
+
+		$this->assertContains('javascript:alert(\\\'Votre abonnement est terminé\\\')', $url);
+
+	}
+
+
+
+ 
+/** @test */
+	public function cvsUrlWithUserNotValidShouldReturnJsAlert() {
+		$this->_logUserGaston()
+			->beAbonneSIGB()
+			->setDateDebut('1999-02-10')
+			->setDateFin('2025-09-12');
+		$cvs = new Class_Systeme_ModulesMenu_CVS();
 
-		$menu_url = $this->module_menu->getUrl('VODECLIC', array());
-		$this->assertContains('javascript:alert(\\\'Votre abonnement est terminé\\\')', $menu_url['url']);
-		$this->assertEquals('_blank', $menu_url['target']);
+
+		$menu_url = $cvs->getDynamiqueUrl();
+		$this->assertContains('javascript:alert("Merci de contacter la médiathèque pour obtenir un accès.")', $menu_url);
 	}
 
 
 	/** @test */
 	public function cvsUrlWithAbonnementInvalidShouldBeJSAlertAbonnementInvalid() {
 		$this->_logUserGaston();
+		$cvs = new Class_Systeme_ModulesMenu_CVS();
+		$menu_url = $cvs->getDynamiqueUrl();
+		$this->assertContains('javascript:alert("Votre abonnement est terminé")', $menu_url);
 
-		$menu_url = $this->module_menu->getUrl('CVS', array());
-		$this->assertContains('javascript:alert("Votre abonnement est terminé")', $menu_url['url']);
-		$this->assertEquals('_blank', $menu_url['target']);
 	}
 
 
 
 	/** @test */
-	public function cvsUrlWithUserNotValidShouldReturnJsAlert() {
-		$this->_logUserGaston()
-			->beAbonneSIGB()
-			->setDateDebut('1999-02-10')
-			->setDateFin('2025-09-12');
-		
-		$menu_url = $this->module_menu->getUrl('CVS', array());
-		$this->assertContains('javascript:alert("Merci de contacter la médiathèque pour obtenir un accès.")', $menu_url['url']);
+	public function numilogUrlWithAbonnementInvalidShouldReturnDefaultNumilogUrl() {
+		$this->_logUserGaston();
+		$numilog = new Class_Systeme_ModulesMenu_Numilog();
+		$url = $numilog->getDynamiqueUrl();
+		$this->assertContains('http://numilogurl/action', $url);
+
 	}
 
 
 
 
+
 	/** @test */
 	public function reserverPosteUrlShouldBeAbonneReservations() {
 		$this->assertEquals(['url' => BASE_URL.'/abonne/multimedia-hold-location', 'target' => ''],