diff --git a/VERSIONS b/VERSIONS
index a4e51397c53acec33d98768d6ba021dc8b3c9cbf..580f157871c2078d80836ff543ae33ee504a8c58 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,3 +1,20 @@
+27/11/2015 - v7.3.32
+
+ - ticket #33756 : Cosmogramme : correction de la prise en charge de webservices en https
+
+ - ticket #33429 : Cosmogramme : correction de la génération de vignettes des pseudo-notices dans le contexte de l'intégration automatique
+
+ - ticket #33773 : VSmart: correction de l'impossibilité de se connecter au compte lecteur lorsqu'une consultation est reservée
+
+
+26/11/2015 - v7.3.31
+
+ - ticket #33501 : Cosmogramme: amélioration des intégrations automatiques
+
+ - ticket #33472 : prise en charge de la non répétition du répertoire dans les urls de vignettes des domaines
+
+
+
 23/11/2015 - v7.3.30
 
  - ticket #5524 : Correction compatibilité php < 5.5
diff --git a/VERSIONS_WIP/10430 b/VERSIONS_WIP/10430
new file mode 100644
index 0000000000000000000000000000000000000000..3d2f86c121b408db204bc4919c9c89e73f8c87ae
--- /dev/null
+++ b/VERSIONS_WIP/10430
@@ -0,0 +1 @@
+ - ticket #10430 : Agenda : Possibilité de spécifier les jours de la semaines applicables aux évènements récurrents
\ No newline at end of file
diff --git a/application/modules/admin/controllers/CmsController.php b/application/modules/admin/controllers/CmsController.php
index f4bee15637ec260da98fb0ef795584b293a965ba..e73556d374a3d696752b4e06c26a4a2ba98d1d0b 100644
--- a/application/modules/admin/controllers/CmsController.php
+++ b/application/modules/admin/controllers/CmsController.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
  */
+
 class Admin_CmsController extends ZendAfi_Controller_Action {
   /** @var Class_Bib */
   private $_bib;
@@ -514,6 +515,8 @@ class Admin_CmsController extends ZendAfi_Controller_Action {
     $attributes=parent::_getFormValues($model);
     foreach(['description', 'contenu'] as $content_field)
       $attributes[$content_field] = Class_CmsUrlTransformer::forEditing($attributes[$content_field]);
+
+    $attributes['pick_day'] = $model->getPickDayAsArray();
     return $attributes;
   }
 
diff --git a/application/modules/opac/controllers/CmsController.php b/application/modules/opac/controllers/CmsController.php
index cb1dcf37597122b38a2add249cae0ffcbb14af57..781ac43f7fab46d7fc54dbb0a9e74608a170275b 100644
--- a/application/modules/opac/controllers/CmsController.php
+++ b/application/modules/opac/controllers/CmsController.php
@@ -61,11 +61,13 @@ class CmsController extends ZendAfi_Controller_Action {
 
     if($preferences['display_mode']=='Summary')
       $preferences['summary_content']='Summary';
+
     if($preferences['display_mode']!='Title')
-      $this->_viewArticlesByPreferences($preferences);
+      return $this->_viewArticlesByPreferences($preferences);
 
     $articles = Class_Article::getArticlesByPreferences($preferences);
     $articles = Class_Article::filterByLocaleAndWorkflow($articles);
+    $articles = Class_Article::filterByDay(strtotime($this->_getParam('d')), $articles);
     $articles = Class_Article::groupByBibId($articles);
 
     if ((count($articles) == 1) && isset($articles[0]))
@@ -291,6 +293,7 @@ class CmsController extends ZendAfi_Controller_Action {
                                ->getArticlesByPreferences($preferences);
 
      $articles = Class_Article::getLoader()->filterByLocaleAndWorkflow($articles);
+     $articles = Class_Article::filterByDay(strtotime($this->_getParam('d')), $articles);
 
      $this->view->articles = $articles;
 
@@ -309,6 +312,5 @@ class CmsController extends ZendAfi_Controller_Action {
     }
 
     $this->renderScript('cms/articlesview.phtml');
-
   }
 }
\ No newline at end of file
diff --git a/application/modules/opac/views/scripts/cms/articleview.phtml b/application/modules/opac/views/scripts/cms/articleview.phtml
index 1295bcf60af8ed0511163d55d2ad51d755ceaf1a..aa8f8c7dfcc278343b1b67440a081b81cb276737 100644
--- a/application/modules/opac/views/scripts/cms/articleview.phtml
+++ b/application/modules/opac/views/scripts/cms/articleview.phtml
@@ -1,6 +1,6 @@
 <a name="Top"></a>
 <?php
-   echo $this->partial('cms/article_partial.phtml', array('article' => $this->article));
+   echo $this->partial('cms/article_partial.phtml', ['article' => $this->article]);
 ?>
 
 <table style="width:100%">
@@ -8,4 +8,4 @@
     <td style="text-align:left"><a href="javascript:history.back()"><?php echo $this->_('Retour') ?></a></td>
     <td style="text-align:right"><a href="#Top"><?php echo $this->_('Haut') ?></a></td>
   </tr>
-</table>
\ No newline at end of file
+</table>
diff --git a/cosmogramme/php/integration/pseudo_notices.php b/cosmogramme/php/integration/pseudo_notices.php
index 93fce93607d2c60d42fbc5ced0906c10528fb950..ab67cab00684c6bdbc3c6a2cff5624ddb46b43d4 100644
--- a/cosmogramme/php/integration/pseudo_notices.php
+++ b/cosmogramme/php/integration/pseudo_notices.php
@@ -19,9 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-
-startIntegrationPhase('Batchs');
 startIntegrationPhase('PseudoRecordCms');
 startIntegrationPhase('PseudoRecordRss');
 startIntegrationPhase('PseudoRecordSitotheque');
-startIntegrationPhase('PseudoRecordAlbum');
+startIntegrationPhase('PseudoRecordAlbum');
\ No newline at end of file
diff --git a/cosmogramme/php/integre_traite_main.php b/cosmogramme/php/integre_traite_main.php
index 54fbc4f4a5bf03e039a429375dbf57f2de405610..6ab0c085d6cbd84e9473026bb62580d8981522d2 100644
--- a/cosmogramme/php/integre_traite_main.php
+++ b/cosmogramme/php/integre_traite_main.php
@@ -1,4 +1,4 @@
-<?PHP
+<?php
 /**
  * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
  *
@@ -75,6 +75,7 @@ $timeStart = time();
 $chrono->start();
 $phase = '0';
 $phase_data = [];
+$should_skip_records = false;
 
 // ----------------------------------------------------------------
 // Début du traitement
@@ -108,7 +109,6 @@ if ($_REQUEST['reprise'] == 'oui') {
 		$log->ecrire('Type d\'import : ' . $txt[$cron_a_traiter] . BR);
 	}
 
-
 	try {
 		startIntegrationPhase('PrepareIntegrations');
 	} catch (Class_Cosmogramme_Integration_PhasePrepareIntegrationsException $e) {
@@ -127,28 +127,31 @@ if ($_REQUEST['reprise'] == 'oui') {
 		$controle = fetchOne("select count(*) from integrations,profil_donnees
 				Where integrations.profil=profil_donnees.id_profil
 				and traite='non'
-				and type_operation=2
-				and type_fichier=0");
+				and type_operation = 2
+				and type_fichier = 0");
+
+    $logger = function($message, $log) {
+      $log->ecrire(BR . BR
+									 . '<span class="rouge">' . $message . ' : traitement des notices désactivé !</span>'
+                   . BR);
+    };
 
 		if ($cron_a_traiter == 'inc' and $controle > 0) {
-			$erreur = 'Il y a un import total à traiter et le cron demande de ne traiter que les imports incrementiels';
-			$log->ecrire(BR . BR
-									 . '<span class="rouge">' . $erreur . ' : Abandon du traitement !</span><br>');
-			exit;
+			$logger('Il y a un import total à traiter et le cron demande de ne traiter que les imports incrementiels',
+              $log);
+      $should_skip_records = true;
 		}
 
 		if ($cron_a_traiter == 'tot' and $controle == 0) {
-			$erreur = 'Il n\'y aucun import total à traiter et le cron demande de ne traiter que les imports totaux';
-			$log->ecrire(BR . BR
-									 . '<span class="rouge">' . $erreur . ' : Abandon du traitement !</span><br>');
-			exit;
+      $logger('Il n\'y aucun import total à traiter et le cron demande de ne traiter que les imports totaux',
+              $log);
+      $should_skip_records = true;
 		}
 	}
 
 	// ----------------------------------------------------------------
 	// Test blocage de la base
 	// ----------------------------------------------------------------
-	$log->ecrire('<h4>Traitement des notices</h4>');
 	if (getVariable('clef_traitements') == '1') {
 		$log->ecrire('<span class="rouge">La base est bloquée (clef_traitements) : Abandon du traitement !</span><br>');
 		$log->close();
@@ -158,328 +161,340 @@ if ($_REQUEST['reprise'] == 'oui') {
 	setVariable('clef_traitements', '1');
 }
 
-// ----------------------------------------------------------------
-// Integration des notices (PHASE 0)
-// ----------------------------------------------------------------
-setVariable('traitement_phase', 'Intégration des notices');
-startIntegrationPhase('notice');
-
-// ----------------------------------------------------------------
-// PSEUDO-NOTICES - cms rss sitotheque et albums (phase 0.1 a 0.6)
-// ----------------------------------------------------------------
-if ($phase >= 0 and $phase < 1) {
-	include('integration/pseudo_notices.php');
-	$phase = 1;
-}
-
 
-if ($phase == 1 || $phase == 1.1) {
-	startIntegrationPhase('DeleteItem');
-	$phase = 2;
+if (!$should_skip_records) {
+  // ----------------------------------------------------------------
+  // Integration des notices (PHASE 0)
+  // ----------------------------------------------------------------
+  $log->ecrire('<h4>Traitement des notices</h4>');
+  setVariable('traitement_phase', 'Intégration des notices');
+  startIntegrationPhase('notice');
 }
 
-// ----------------------------------------------------------------
-// Suppression des notices sans exemplaires (PHASE 2)
-// ----------------------------------------------------------------
-$log->ecrire('Suppression des notices sans exemplaire<br>');
-setVariable("traitement_phase", "Suppression des notices sans exemplaire");
-startIntegrationPhase('DeleteRecordWithoutItem');
-
-if ($phase == 2) $phase = "PERIODIQUES_0";
-
-// ----------------------------------------------------------------
-// INDEXATION DES ARTICLES DE PERIODIQUES
-// ----------------------------------------------------------------
-if (substr($phase, 0, 11) == "PERIODIQUES")
-{
-	include("integration/periodiques.php");
-	$phase = 2;
-}
-
-
-// ----------------------------------------------------------------
-// Traiter les notices succintes (PHASE 3)
-// ----------------------------------------------------------------
-require_once("classe_communication.php");
-setVariable("traitement_phase", "Intégration des notices succintes");
-$frequence = getVariable("succintes_frequence");
-$date_homogene = getVariable("succintes_date");
-$Z3950_retry_level = getVariable("Z3950_retry_level");
-$ecart = ecartDates($date, $date_homogene);
-if ($phase == 2)
-{
-	$log->ecrire("<h4>Intégration des notices succintes</h4>");
-	$phase = 3;
-	unset($phase_data);
-	$phase_data["nombre"] = 0;
-	$phase_data["timeStart"] = time();
-	$chrono100notices->start();
-	$phase_data["nb_homogene"] = 0;
-	$phase_data["id"] = 0;
-	if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
-	elseif (!$mode_cron) sauveContexte();
-}
-if ($phase == 3 and $ecart >= $frequence)
-{
-	if (!$mode_cron) print("<h4>Intégration des notices succintes</h4>");
-	$chrono->start();
-	if (!$phase_data["nombre"]) $log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR . BR);
-	$result = $sql->prepareListe("select * from notices_succintes where id > '" . $phase_data["id"] . "' and z3950_retry <= $Z3950_retry_level order by id");
-	while ($data = $sql->fetchNext($result))
-	{
-		if (!$mode_cron and $chrono->tempsPasse() > $timeout) sauveContexte();
-		$ret = $notice->traiteSuccinte($data);
-		$phase_data["id"] = $data["id"];
-		$phase_data["nombre"]++;
-		traceSuccinte($ret);
-	}
-	// Recap
-	$log->ecrire(BR . '<span class="vert">' . ($phase_data["nombre"]) . ' notices traitées</span>' . BR);
-	$chrono->timeStart = $phase_data["timeStart"];
-	$log->ecrire('<span class="vert">Temps de traitement : ' . $chrono->end() . " (" . $chrono->moyenne($phase_data["nombre"], "notices") . ")</span>" . BR);
-
-	for ($i = 0; $i < 5; $i++) if (!$phase_data[$i]) $phase_data[$i] = "0";
-	$log->ecrire('<table class="blank" cellspacing="0" cellpadding="5px" style="margin-left:25px;margin-bottom:10px;margin-top:10px">');
-	$log->ecrire('<tr><td class="blank">Notices trouvées dans la base</td><td class="blank" align="right">' . $phase_data[1] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Notices trouvées sur serveurs z39.50</td><td class="blank" align="right">' . $phase_data[2] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Notices non trouvées</td><td class="blank" align="right">' . $phase_data[3] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Echecs de connexions</td><td class="blank" align="right">' . $phase_data[4] . '</td></tr>');
-	$log->ecrire('</table>');
-	setVariable("succintes_date", $date);
-}
-
-// ----------------------------------------------------------------
-// Traiter l'homogénéisation des notices par ISBN (PHASE 4)
-// ----------------------------------------------------------------
-setVariable("traitement_phase", "Homogénéisation par l'ISBN");
-$homogene_actif = getVariable("homogene");
-$homogene_cache_only = getVariable("Z3950_cache_only");
-$frequence = getVariable("homogene_frequence");
-$date_homogene = getVariable("homogene_date");
-$Z3950_retry_level = getVariable("Z3950_retry_level");
-$Z3950_max_reconnect = getVariable("Z3950_max_reconnect");
-$qualite_homogene = getVariable("homogene_code_qualite");
-$ecart = ecartDates($date, $date_homogene);
-if ($phase == 3)
-{
-	$log->ecrire("<h4>Homogénéisation Z39.50 des notices par l'ISBN</h4>");
-	unset($phase_data);
-	$phase_data["nombre"] = 0;
-	$phase_data["timeStart"] = time();
-	$chrono100notices->start();
-	$phase = 4;
-	if ($homogene_actif == 0)
-	{
-		$log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
-	} else
-	{
-		if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
-		else if (!$mode_cron) sauveContexte();
-	}
-}
-if ($phase == 4 and $ecart >= $frequence and $homogene_actif == 1)
-{
-	if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par l'ISBN</h4>");
-	$chrono->start();
-	$ret["timeout"] = 10;
-	if (!$phase_data["nombre"])
-	{
-		$log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
-		$log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . $Z3950_max_reconnect . '</span>' . BR);
-		$log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
-	}
-	$result = $sql->prepareListe("select id_notice,isbn from notices where isbn > '" . $phase_data["isbn"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by isbn");
-	try {
-		while ($data = $sql->fetchNext($result))
-			{
-				while (true)
-					{
-						if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
-						$ret = $notice->traiteHomogene($data["id_notice"], $data["isbn"], "", "", $homogene_cache_only);
-						traceHomogene($data["id_notice"], $data["isbn"], "", "");
-						if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
-							{
-								$phase_data["nb_reconnexions"] = 0;
-								break;
-							}
-
-						// Tentatives de reconnexion
-						$phase_data["nb_reconnexions"]++;
-						if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
-							{
-								$log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
-								$fin = true;
-								break;
-							}
-						sleep(5);
-					}
-				if ($fin == true) break;
-				$phase_data["isbn"] = $data["isbn"];
-				$phase_data["nombre"]++;
-			}
-	} catch (PDOException $e) {
-			$log->ecrire('PDOException'.$e);
-			$cosmo_path = new CosmoPaths();
-			$site = '/' . $cosmo_path->getSite() . '/';
-			$cfgfile = $cosmo_path->getConfigPath();
-			$cfg=lireConfig($cfgfile);
-			$sql = new sql($cfg["integration_server"],$cfg["integration_user"],$cfg["integration_pwd"],$cfg["integration_base"]);
 
-		}
-	afficherRecapHomogene($phase_data, $chrono);
-}
 // ----------------------------------------------------------------
-// Traiter l'homogénéisation des notices par EAN (PHASE 5)
+// Batch
 // ----------------------------------------------------------------
-setVariable("traitement_phase", "Homogénéisation par l'EAN");
-$fin = false;
-if ($phase == 4)
-{
-	$log->ecrire("<h4>Homogénéisation Z39.50 des notices par l'EAN</h4>");
-	unset($phase_data);
-	$phase_data["nombre"] = 0;
-	$phase_data["timeStart"] = time();
-	$phase_data["ean"] = "";
-	$chrono100notices->start();
-	$phase = 5;
-	if ($homogene_actif == 0)
-	{
-		$log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
-	} else
-	{
-		if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
-		else if (!$mode_cron) sauveContexte();
-	}
+if ($phase >= 0 and $phase < 0.2) {
+  startIntegrationPhase('Batchs');
+  $phase = 0.2;
 }
-if ($phase == 5 and $ecart >= $frequence and $homogene_actif == 1)
-{
-	if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par l'EAN</h4>");
-	$chrono->start();
-	$ret["timeout"] = 10;
-	if (!$phase_data["nombre"])
-	{
-		$log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
-		$log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . ($Z3950_max_reconnect) . '</span>' . BR);
-		$log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
-	}
-	$result = $sql->prepareListe("select id_notice,ean from notices where ean > '" . $phase_data["ean"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by ean");
-	while ($data = $sql->fetchNext($result))
-	{
-		while (true)
-		{
-			if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
-			$ret = $notice->traiteHomogene($data["id_notice"], "", $data["ean"], "", $homogene_cache_only);
-			traceHomogene($data["id_notice"], "", $data["ean"], "");
-			if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
-			{
-				$phase_data["nb_reconnexions"] = 0;
-				break;
-			}
 
-			// Tentatives de reconnexion
-			$phase_data["nb_reconnexions"]++;
-			if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
-			{
-				$log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
-				$fin = true;
-				break;
-			}
-			sleep(5);
-		}
-		if ($fin == true) break;
-		$phase_data["ean"] = $data["ean"];
-		$phase_data["nombre"]++;
-	}
-	afficherRecapHomogene($phase_data, $chrono);
-}
 
-// --------------------------------------------------------------------
-// Traiter l'homogénéisation des notices par ID_COMMERCIALE (PHASE 6)
-// --------------------------------------------------------------------
-setVariable("traitement_phase", "Homogénéisation par le numéro commercial");
-$fin = false;
-if ($phase == 5)
-{
-	$log->ecrire("<h4>Homogénéisation Z39.50 des notices par le numéro commercial</h4>");
-	unset($phase_data);
-	$phase_data["nombre"] = 0;
-	$phase_data["timeStart"] = time();
-	$phase_data["ean"] = "";
-	$chrono100notices->start();
-	$phase = 6;
-	if ($homogene_actif == 0)
-	{
-		$log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
-	} else
-	{
-		if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
-		else if (!$mode_cron) sauveContexte();
-	}
+if (!$should_skip_records) {
+  // ----------------------------------------------------------------
+  // PSEUDO-NOTICES - cms rss sitotheque et albums (phase 0.1 a 0.6)
+  // ----------------------------------------------------------------
+  if ($phase > 0.1 and $phase < 1) {
+    include('integration/pseudo_notices.php');
+    $phase = 1;
+  }
+
+
+  if ($phase == 1 || $phase == 1.1) {
+    startIntegrationPhase('DeleteItem');
+    $phase = 2;
+  }
+
+  // ----------------------------------------------------------------
+  // Suppression des notices sans exemplaires (PHASE 2)
+  // ----------------------------------------------------------------
+  $log->ecrire('Suppression des notices sans exemplaire<br>');
+  setVariable("traitement_phase", "Suppression des notices sans exemplaire");
+  startIntegrationPhase('DeleteRecordWithoutItem');
+
+  if ($phase == 2) $phase = "PERIODIQUES_0";
+
+  // ----------------------------------------------------------------
+  // INDEXATION DES ARTICLES DE PERIODIQUES
+  // ----------------------------------------------------------------
+  if (substr($phase, 0, 11) == "PERIODIQUES")
+    {
+      include("integration/periodiques.php");
+      $phase = 2;
+    }
+
+
+  // ----------------------------------------------------------------
+  // Traiter les notices succintes (PHASE 3)
+  // ----------------------------------------------------------------
+  require_once("classe_communication.php");
+  setVariable("traitement_phase", "Intégration des notices succintes");
+  $frequence = getVariable("succintes_frequence");
+  $date_homogene = getVariable("succintes_date");
+  $Z3950_retry_level = getVariable("Z3950_retry_level");
+  $ecart = ecartDates($date, $date_homogene);
+  if ($phase == 2)
+    {
+      $log->ecrire("<h4>Intégration des notices succintes</h4>");
+      $phase = 3;
+      unset($phase_data);
+      $phase_data["nombre"] = 0;
+      $phase_data["timeStart"] = time();
+      $chrono100notices->start();
+      $phase_data["nb_homogene"] = 0;
+      $phase_data["id"] = 0;
+      if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
+      elseif (!$mode_cron) sauveContexte();
+    }
+  if ($phase == 3 and $ecart >= $frequence)
+    {
+      if (!$mode_cron) print("<h4>Intégration des notices succintes</h4>");
+      $chrono->start();
+      if (!$phase_data["nombre"]) $log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR . BR);
+      $result = $sql->prepareListe("select * from notices_succintes where id > '" . $phase_data["id"] . "' and z3950_retry <= $Z3950_retry_level order by id");
+      while ($data = $sql->fetchNext($result))
+        {
+          if (!$mode_cron and $chrono->tempsPasse() > $timeout) sauveContexte();
+          $ret = $notice->traiteSuccinte($data);
+          $phase_data["id"] = $data["id"];
+          $phase_data["nombre"]++;
+          traceSuccinte($ret);
+        }
+      // Recap
+      $log->ecrire(BR . '<span class="vert">' . ($phase_data["nombre"]) . ' notices traitées</span>' . BR);
+      $chrono->timeStart = $phase_data["timeStart"];
+      $log->ecrire('<span class="vert">Temps de traitement : ' . $chrono->end() . " (" . $chrono->moyenne($phase_data["nombre"], "notices") . ")</span>" . BR);
+
+      for ($i = 0; $i < 5; $i++) if (!$phase_data[$i]) $phase_data[$i] = "0";
+      $log->ecrire('<table class="blank" cellspacing="0" cellpadding="5px" style="margin-left:25px;margin-bottom:10px;margin-top:10px">');
+      $log->ecrire('<tr><td class="blank">Notices trouvées dans la base</td><td class="blank" align="right">' . $phase_data[1] . '</td></tr>');
+      $log->ecrire('<tr><td class="blank">Notices trouvées sur serveurs z39.50</td><td class="blank" align="right">' . $phase_data[2] . '</td></tr>');
+      $log->ecrire('<tr><td class="blank">Notices non trouvées</td><td class="blank" align="right">' . $phase_data[3] . '</td></tr>');
+      $log->ecrire('<tr><td class="blank">Echecs de connexions</td><td class="blank" align="right">' . $phase_data[4] . '</td></tr>');
+      $log->ecrire('</table>');
+      setVariable("succintes_date", $date);
+    }
+
+  // ----------------------------------------------------------------
+  // Traiter l'homogénéisation des notices par ISBN (PHASE 4)
+  // ----------------------------------------------------------------
+  setVariable("traitement_phase", "Homogénéisation par l'ISBN");
+  $homogene_actif = getVariable("homogene");
+  $homogene_cache_only = getVariable("Z3950_cache_only");
+  $frequence = getVariable("homogene_frequence");
+  $date_homogene = getVariable("homogene_date");
+  $Z3950_retry_level = getVariable("Z3950_retry_level");
+  $Z3950_max_reconnect = getVariable("Z3950_max_reconnect");
+  $qualite_homogene = getVariable("homogene_code_qualite");
+  $ecart = ecartDates($date, $date_homogene);
+  if ($phase == 3)
+    {
+      $log->ecrire("<h4>Homogénéisation Z39.50 des notices par l'ISBN</h4>");
+      unset($phase_data);
+      $phase_data["nombre"] = 0;
+      $phase_data["timeStart"] = time();
+      $chrono100notices->start();
+      $phase = 4;
+      if ($homogene_actif == 0)
+        {
+          $log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
+        } else
+        {
+          if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
+          else if (!$mode_cron) sauveContexte();
+        }
+    }
+  if ($phase == 4 and $ecart >= $frequence and $homogene_actif == 1)
+    {
+      if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par l'ISBN</h4>");
+      $chrono->start();
+      $ret["timeout"] = 10;
+      if (!$phase_data["nombre"])
+        {
+          $log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
+          $log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . $Z3950_max_reconnect . '</span>' . BR);
+          $log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
+        }
+      $result = $sql->prepareListe("select id_notice,isbn from notices where isbn > '" . $phase_data["isbn"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by isbn");
+      try {
+        while ($data = $sql->fetchNext($result))
+          {
+            while (true)
+              {
+                if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
+                $ret = $notice->traiteHomogene($data["id_notice"], $data["isbn"], "", "", $homogene_cache_only);
+                traceHomogene($data["id_notice"], $data["isbn"], "", "");
+                if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
+                  {
+                    $phase_data["nb_reconnexions"] = 0;
+                    break;
+                  }
+
+                // Tentatives de reconnexion
+                $phase_data["nb_reconnexions"]++;
+                if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
+                  {
+                    $log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
+                    $fin = true;
+                    break;
+                  }
+                sleep(5);
+              }
+            if ($fin == true) break;
+            $phase_data["isbn"] = $data["isbn"];
+            $phase_data["nombre"]++;
+          }
+      } catch (PDOException $e) {
+        $log->ecrire('PDOException'.$e);
+        $cosmo_path = new CosmoPaths();
+        $site = '/' . $cosmo_path->getSite() . '/';
+        $cfgfile = $cosmo_path->getConfigPath();
+        $cfg=lireConfig($cfgfile);
+        $sql = new sql($cfg["integration_server"],$cfg["integration_user"],$cfg["integration_pwd"],$cfg["integration_base"]);
+
+      }
+      afficherRecapHomogene($phase_data, $chrono);
+    }
+  // ----------------------------------------------------------------
+  // Traiter l'homogénéisation des notices par EAN (PHASE 5)
+  // ----------------------------------------------------------------
+  setVariable("traitement_phase", "Homogénéisation par l'EAN");
+  $fin = false;
+  if ($phase == 4)
+    {
+      $log->ecrire("<h4>Homogénéisation Z39.50 des notices par l'EAN</h4>");
+      unset($phase_data);
+      $phase_data["nombre"] = 0;
+      $phase_data["timeStart"] = time();
+      $phase_data["ean"] = "";
+      $chrono100notices->start();
+      $phase = 5;
+      if ($homogene_actif == 0)
+        {
+          $log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
+        } else
+        {
+          if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
+          else if (!$mode_cron) sauveContexte();
+        }
+    }
+  if ($phase == 5 and $ecart >= $frequence and $homogene_actif == 1)
+    {
+      if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par l'EAN</h4>");
+      $chrono->start();
+      $ret["timeout"] = 10;
+      if (!$phase_data["nombre"])
+        {
+          $log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
+          $log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . ($Z3950_max_reconnect) . '</span>' . BR);
+          $log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
+        }
+      $result = $sql->prepareListe("select id_notice,ean from notices where ean > '" . $phase_data["ean"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by ean");
+      while ($data = $sql->fetchNext($result))
+        {
+          while (true)
+            {
+              if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
+              $ret = $notice->traiteHomogene($data["id_notice"], "", $data["ean"], "", $homogene_cache_only);
+              traceHomogene($data["id_notice"], "", $data["ean"], "");
+              if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
+                {
+                  $phase_data["nb_reconnexions"] = 0;
+                  break;
+                }
+
+              // Tentatives de reconnexion
+              $phase_data["nb_reconnexions"]++;
+              if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
+                {
+                  $log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
+                  $fin = true;
+                  break;
+                }
+              sleep(5);
+            }
+          if ($fin == true) break;
+          $phase_data["ean"] = $data["ean"];
+          $phase_data["nombre"]++;
+        }
+      afficherRecapHomogene($phase_data, $chrono);
+    }
+
+  // --------------------------------------------------------------------
+  // Traiter l'homogénéisation des notices par ID_COMMERCIALE (PHASE 6)
+  // --------------------------------------------------------------------
+  setVariable("traitement_phase", "Homogénéisation par le numéro commercial");
+  $fin = false;
+  if ($phase == 5)
+    {
+      $log->ecrire("<h4>Homogénéisation Z39.50 des notices par le numéro commercial</h4>");
+      unset($phase_data);
+      $phase_data["nombre"] = 0;
+      $phase_data["timeStart"] = time();
+      $phase_data["ean"] = "";
+      $chrono100notices->start();
+      $phase = 6;
+      if ($homogene_actif == 0)
+        {
+          $log->ecrire('<span class="rouge">Le processus d\'homogénéisation des notices est désactivé</span>' . BR);
+        } else
+        {
+          if ($ecart < $frequence) $log->ecrire('<span class="vert">Sera fait dans ' . ($frequence - $ecart) . ' jour(s)</span>');
+          else if (!$mode_cron) sauveContexte();
+        }
+    }
+  if ($phase == 6 and $ecart >= $frequence and $homogene_actif == 1)
+    {
+      if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par le numéro commercial</h4>");
+      $chrono->start();
+      $ret["timeout"] = 10;
+      if (!$phase_data["nombre"])
+        {
+          $log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
+          $log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . ($Z3950_max_reconnect) . '</span>' . BR);
+          $log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
+        }
+      $result = $sql->prepareListe("select id_notice,id_commerciale from notices where id_commerciale > '" . $phase_data["id_commerciale"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by id_commerciale");
+      while ($data = $sql->fetchNext($result))
+        {
+          while (true)
+            {
+              if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
+              $ret = $notice->traiteHomogene($data["id_notice"], "", "", $data["id_commerciale"], $homogene_cache_only);
+              traceHomogene($data["id_notice"], "", "", $data["id_commerciale"]);
+              if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
+                {
+                  $phase_data["nb_reconnexions"] = 0;
+                  break;
+                }
+
+              // Tentatives de reconnexion
+              $phase_data["nb_reconnexions"]++;
+              if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
+                {
+                  $log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
+                  $fin = true;
+                  break;
+                }
+              sleep(5);
+            }
+          if ($fin == true) break;
+          $phase_data["id_commerciale"] = $data["id_commerciale"];
+          $phase_data["nombre"]++;
+        }
+      afficherRecapHomogene($phase_data, $chrono);
+      setVariable("homogene_date", $date);
+    };
+
+  // ----------------------------------------------------------------
+  // Recalcul des facettes bibliothèque (phases 7 et 7.1)
+  // ----------------------------------------------------------------
+  if (!$mode_cron and $chrono->tempsPasse() > 5)
+    sauveContexte();
+
+  startIntegrationPhase('ItemFacets');
 }
-if ($phase == 6 and $ecart >= $frequence and $homogene_actif == 1)
-{
-	if (!$mode_cron) print("<h4>Homogénéisation Z39.50 des notices par le numéro commercial</h4>");
-	$chrono->start();
-	$ret["timeout"] = 10;
-	if (!$phase_data["nombre"])
-	{
-		$log->ecrire('<span class="vert">Niveau de tentatives d\'homogénéisation : ' . $Z3950_retry_level . '</span>' . BR);
-		$log->ecrire('<span class="vert">Nombre maxi de tentatives de reconnexions : ' . ($Z3950_max_reconnect) . '</span>' . BR);
-		$log->ecrire('<span class="vert">Mode de recherche sur les serveurs z39.50 : ' . getLibCodifVariable("Z3950_cache_only", $homogene_cache_only) . '</span>' . BR);
-	}
-	$result = $sql->prepareListe("select id_notice,id_commerciale from notices where id_commerciale > '" . $phase_data["id_commerciale"] . "' and qualite < $qualite_homogene and z3950_retry <= $Z3950_retry_level order by id_commerciale");
-	while ($data = $sql->fetchNext($result))
-	{
-		while (true)
-		{
-			if (!$mode_cron and ($chrono->tempsPasse() + $ret["timeout"]) > $timeout) sauveContexte();
-			$ret = $notice->traiteHomogene($data["id_notice"], "", "", $data["id_commerciale"], $homogene_cache_only);
-			traceHomogene($data["id_notice"], "", "", $data["id_commerciale"]);
-			if ($ret["statut"] != "erreur" and $ret["statut_z3950"] > 0)
-			{
-				$phase_data["nb_reconnexions"] = 0;
-				break;
-			}
-
-			// Tentatives de reconnexion
-			$phase_data["nb_reconnexions"]++;
-			if ($phase_data["nb_reconnexions"] >= $Z3950_max_reconnect)
-			{
-				$log->ecrire(BR . '<span class="rouge">Abandon du traitement : maximum de tentatives de reconnexions atteint</span>' . BR);
-				$fin = true;
-				break;
-			}
-			sleep(5);
-		}
-		if ($fin == true) break;
-		$phase_data["id_commerciale"] = $data["id_commerciale"];
-		$phase_data["nombre"]++;
-	}
-	afficherRecapHomogene($phase_data, $chrono);
-	setVariable("homogene_date", $date);
-};
-
-// ----------------------------------------------------------------
-// Recalcul des facettes bibliothèque (phases 7 et 7.1)
-// ----------------------------------------------------------------
-if (!$mode_cron and $chrono->tempsPasse() > 5)
-	sauveContexte();
-
-startIntegrationPhase('ItemFacets');
 $phase = 7.5;
 
 // ----------------------------------------------------------------
 // Reattach Reviews
 // ----------------------------------------------------------------
-
 if ($phase == 7.5) {
 	startIntegrationPhase('Reviews');
 	$phase = 8;
 }
 
-
-
-
 // ----------------------------------------------------------------
 // Integration des abonnés
 // ----------------------------------------------------------------
@@ -489,6 +504,7 @@ if ($phase == 8 or $phase == 9)
 	$phase = 10;
 }
 
+
 // ----------------------------------------------------------------
 // Integration des prets et des reservations
 // ----------------------------------------------------------------
@@ -512,6 +528,7 @@ if ($phase==16 or $phase==17) {
 	include("integration/domaines.php");
 	$phase=20;
 }
+
 // ----------------------------------------------------------------
 // Envoi de mails aux bibs qui ont du retard
 // ----------------------------------------------------------------
@@ -807,10 +824,11 @@ function sauveContexte()
 	global $timeStart, $chrono_fichier, $chrono100notices;
 	global $nb_notices, $compteur;
 	global $phase, $phase_data;
+  global $should_skip_records;
 
 	$timeStart_fichier = $chrono_fichier->timeStart;
 	$timeStart_100notices = $chrono100notices->timeStart;
-	$data = compact("cron_a_traiter","nb_notices", "compteur", "timeStart", "timeStart_fichier", "timeStart_100notices", "phase", "phase_data");
+	$data = compact("cron_a_traiter","nb_notices", "compteur", "timeStart", "timeStart_fichier", "timeStart_100notices", "phase", "phase_data", "should_skip_records");
 	$_SESSION["reprise"] = $data;
 	redirection("integre_traite_main.php?reprise=oui");
 }
@@ -821,11 +839,10 @@ function restaureContext()
 	global $timeStart, $chrono_fichier, $chrono100notices;
 	global $nb_notices, $compteur;
 	global $phase, $phase_data;
+  global $should_skip_records;
 
 	extract($_SESSION["reprise"]);
 	$chrono_fichier->timeStart = $timeStart_fichier;
 	$chrono100notices->timeStart = $timeStart_100notices;
 	unset($_SESSION["reprise"]);
-}
-
-?>
+}
\ No newline at end of file
diff --git a/cosmogramme/sql/patch/patch_278.php b/cosmogramme/sql/patch/patch_278.php
index 8b33a4ae5a4ca824fc2062961fd2e096b1412fef..981329c38c3f99f7483e1a1a934f4204841de1ad 100644
--- a/cosmogramme/sql/patch/patch_278.php
+++ b/cosmogramme/sql/patch/patch_278.php
@@ -8,4 +8,4 @@ try {
 try {
   $adapter->query("update variables set liste='0:aucun\r\n1:pergame\r\n2:web-service Opsys\r\n3:serveur Z39.50\r\n4:web-service V-Smart\r\n5:web-service Koha\r\n6:web-service Carthame\r\n7:web-service AFI-Nanook\r\n8:web-service Orphée\r\n9:web-service Microbib\r\n10:web-service BiblixNet\r\n11:web-service Dynix-Symphony\r\n12:Cd-Script\r\n' where clef='comm_sigb';");
 } catch (Exception $e) {}
-?>
+?>
\ No newline at end of file
diff --git a/cosmogramme/sql/patch/patch_280.php b/cosmogramme/sql/patch/patch_280.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f7cbba65faf69a557b6a18d98ed98d4dfc274f2
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_280.php
@@ -0,0 +1,9 @@
+<?php
+$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+
+try {
+  $adapter->query('select pick_day from cms_article limit 1');
+} catch (Exception $e) {
+  $adapter->query('ALTER TABLE `cms_article` ADD COLUMN `pick_day` VARCHAR(255) NULL DEFAULT NULL;');
+}
+?>
\ No newline at end of file
diff --git a/library/Class/Article.php b/library/Class/Article.php
index 12b30b0b88ad3c1c715e0ec4c22bb8d0bb9e903a..2d00582223520fdfc6662cd91e0bfa5a9bda600e 100644
--- a/library/Class/Article.php
+++ b/library/Class/Article.php
@@ -436,9 +436,18 @@ class ArticleLoader extends Storm_Model_Loader {
       $page++;
     }
   }
-}
 
 
+  public function filterByDay($day, $articles) {
+    $day_num = gmdate("w", $day);
+    return  array_filter($articles,
+                         function($event) {
+                                             if(empty($event->getPickDayAsArray()) || in_array($day_num, $event->getPickDayAsArray()))
+                                               return $event;
+                                           });
+  }
+}
+
 
 
 class Class_Article extends Storm_Model_Abstract {
@@ -513,28 +522,29 @@ class Class_Article extends Storm_Model_Abstract {
                                       'refus_message'];
 
   protected $_default_attribute_values = [
-    'id_notice' => null,
-    'titre' => '',
-    'description' => '',
-    'contenu' => '',
-    'debut' => null,
-    'fin' => null,
-    'avis' => false,
-    'tags' => '',
-    'events_debut' => null,
-    'events_fin' => null,
-    'indexation' => 1,
-    'cacher_titre' => 0,
-    'date_maj' => '',
-    'date_creation' => '',
-    'status' => self::STATUS_DRAFT,
-    'id_lieu' => 0,
-    'id_cat' => 0,
-    'domaine_ids' => '',
-    'id_origine' => 0,
-    'refus_message' => '',
-    'destination_email' => '',
-    'all_day' => FALSE];
+                                          'id_notice' => null,
+                                          'titre' => '',
+                                          'description' => '',
+                                          'contenu' => '',
+                                          'debut' => null,
+                                          'fin' => null,
+                                          'avis' => false,
+                                          'tags' => '',
+                                          'events_debut' => null,
+                                          'events_fin' => null,
+                                          'indexation' => 1,
+                                          'cacher_titre' => 0,
+                                          'date_maj' => '',
+                                          'date_creation' => '',
+                                          'status' => self::STATUS_DRAFT,
+                                          'id_lieu' => 0,
+                                          'id_cat' => 0,
+                                          'domaine_ids' => '',
+                                          'id_origine' => 0,
+                                          'refus_message' => '',
+                                          'destination_email' => '',
+                                          'all_day' => FALSE,
+                                          'pick_day' => null];
 
 
   public function describeAssociationsOn($associations) {
@@ -1277,6 +1287,9 @@ class Class_Article extends Storm_Model_Abstract {
         $attributes[$content_field] = Class_CmsUrlTransformer::forSaving($attributes[$content_field]);
     }
 
+    if(isset($attributes['pick_day']) && is_array($attributes['pick_day']))
+      $attributes['pick_day'] = implode(',', $attributes['pick_day']);
+
     if (array_key_exists('status', $attributes))
       $this->old_status = $this->getStatus();
 
@@ -1320,5 +1333,17 @@ class Class_Article extends Storm_Model_Abstract {
       return '';
     return $author->getLoginOrFullName();
   }
+
+
+  public function getPickDayAsArray() {
+    $param = parent::_get('pick_day');
+    if($param == '')
+      return [];
+
+    if(empty($result = explode(',', $param)))
+      return [$param];
+
+    return $result;
+  }
 }
 ?>
\ No newline at end of file
diff --git a/library/Class/Calendar.php b/library/Class/Calendar.php
index 8a7d8132c50821750828d19d8e7c62d67605ae46..ea743e17dfce21b850040dd13cdb44c24566ea44 100644
--- a/library/Class/Calendar.php
+++ b/library/Class/Calendar.php
@@ -18,11 +18,10 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//   OPAC- 3                                        Class_Calendar
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
 class Class_Calendar {
   use Trait_TimeSource;
+
   var $PREFIX         = "calendar_";
   var $URL_PARAMETER  = "date";
   var $PRESERVE_URL   = true;
diff --git a/library/Class/Import/Typo3.php b/library/Class/Import/Typo3.php
index a52fdc6a149169f0b6edb361f753aaeef09f92f7..b8ff9d80566f17a08882d75119ab7dd5fb01ecfb 100644
--- a/library/Class/Import/Typo3.php
+++ b/library/Class/Import/Typo3.php
@@ -236,7 +236,7 @@ class Class_Import_Typo3 {
 
 
   protected function _getSite($uid) {
-    return Class_Sitotheque::findFirstByCustomFieldValue(SELF::UID_TYPO3_CF, $uid);
+    return Class_Sitotheque::findFirstByCustomFieldValue(self::UID_TYPO3_CF, $uid);
   }
 
 
diff --git a/library/Class/Profil.php b/library/Class/Profil.php
index 2d5d9a3853e85bf212f1e97ae497a7324af43d8b..8495ddf1c0d53b7e2361ffa71fdcda4923b10e59 100644
--- a/library/Class/Profil.php
+++ b/library/Class/Profil.php
@@ -137,12 +137,14 @@ class Class_Profil extends Storm_Model_Abstract {
    * @return Class_Profil
    */
   public static function getCurrentProfil() {
-    if (!isset(self::$_current_profil)) {
-      if (!$id_profil = Zend_Registry::get('session')->id_profil)
-        $id_profil = 1;
-      self::$_current_profil = self::getLoader()->find($id_profil);
-    }
-    return self::$_current_profil;
+    if (isset(self::$_current_profil))
+      return self::$_current_profil;
+
+    if (!Zend_Registry::isRegistered('session')
+        || (!$id_profil = Zend_Registry::get('session')->id_profil))
+      $id_profil = 1;
+
+    return self::$_current_profil = self::getLoader()->find($id_profil);
   }
 
 
diff --git a/library/Class/WebService/SIGB/AbstractRESTService.php b/library/Class/WebService/SIGB/AbstractRESTService.php
index 1303ebb6ebac571764193cc3bbe0ba4201af0624..857648b26603573f365e4efc3bc6b9bfe319fac9 100644
--- a/library/Class/WebService/SIGB/AbstractRESTService.php
+++ b/library/Class/WebService/SIGB/AbstractRESTService.php
@@ -37,10 +37,18 @@ abstract class Class_WebService_SIGB_AbstractRESTService extends Class_WebServic
    * @return Class_WebService_SIGB_AbstractRESTService
    */
   public function setServerRoot($server_root) {
-    $this->_server_root = 'http://' . str_replace('http://', '', $server_root);
+    $this->_server_root = $this->_withProtocol($server_root);
     return $this;
   }
 
+
+  protected function _withProtocol($url) {
+    return ('http://' == substr($url, 0, 7) || 'https://' == substr($url, 0, 8)
+            ? '' : 'http://')
+      . $url;
+  }
+
+
   /**
    * @return string
    */
diff --git a/library/Class/WebService/SIGB/VSmart/BorrowerReader.php b/library/Class/WebService/SIGB/VSmart/BorrowerReader.php
index fe701a3c069281472a3d4c2207347cceedcd2c9d..77d0a507194ae430cdec729224a9ef99006a7c93 100644
--- a/library/Class/WebService/SIGB/VSmart/BorrowerReader.php
+++ b/library/Class/WebService/SIGB/VSmart/BorrowerReader.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
  */
 
 class Class_WebService_SIGB_VSmart_BorrowerReader {
@@ -27,7 +27,7 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
   protected $_sigb_service;
 
   protected $_xml_parser;
-
+  protected $_current_exemplaire_operation;
 
   /**
    *
@@ -110,11 +110,17 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
   }
 
 
+  public function endReservation() {
+    $this->_current_exemplaire_operation = null;
+  }
+
+
   /**
    * @param string $data
    */
   public function endLoan($data) {
     $this->_emprunteur->empruntsAdd($this->_current_exemplaire_operation);
+    $this->_current_exemplaire_operation = null;
   }
 
 
@@ -130,7 +136,8 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
    * @param string $data
    */
   public function endTitle($data) {
-    $this->_current_exemplaire_operation->getExemplaire()->setTitre($data);
+    if ($this->_current_exemplaire_operation)
+      $this->_current_exemplaire_operation->getExemplaire()->setTitre($data);
   }
 
 
@@ -138,7 +145,8 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
    * @param string $data
    */
   public function endDueDate($data) {
-    $this->_current_exemplaire_operation->setDateRetour($data);
+    if ($this->_current_exemplaire_operation)
+      $this->_current_exemplaire_operation->setDateRetour($data);
   }
 
 
@@ -146,10 +154,11 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
    * @param string $data
    */
   public function endItemBarcode($data) {
-    $this->_current_exemplaire_operation->setId($data)
-      ->getExemplaire()
-      ->setId($data)
-      ->setCodeBarre($data);
+    if ($this->_current_exemplaire_operation)
+      $this->_current_exemplaire_operation->setId($data)
+                                          ->getExemplaire()
+                                          ->setId($data)
+                                          ->setCodeBarre($data);
   }
 
 
@@ -157,7 +166,8 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
    * @param string $data
    */
   public function endPlaceInQueue($data) {
-    $this->_current_exemplaire_operation->setRang($data);
+    if ($this->_current_exemplaire_operation)
+      $this->_current_exemplaire_operation->setRang($data);
   }
 
 
@@ -165,7 +175,8 @@ class Class_WebService_SIGB_VSmart_BorrowerReader {
    * @param string $data
    */
   public function endReservationNumber($data) {
-    $this->_current_exemplaire_operation->setId($data);
+    if ($this->_current_exemplaire_operation)
+      $this->_current_exemplaire_operation->setId($data);
   }
 
 
diff --git a/library/ZendAfi/Form/Admin/News.php b/library/ZendAfi/Form/Admin/News.php
index b09da25e79c6c20c9748c138b4e437f1098dc242..78811ffbd29d3a4239825f91ced5c20fcbc88fc1 100644
--- a/library/ZendAfi/Form/Admin/News.php
+++ b/library/ZendAfi/Form/Admin/News.php
@@ -83,9 +83,22 @@ class ZendAfi_Form_Admin_News extends ZendAfi_Form {
                    ['label' => $this->_("L'évenement dure toute la journée"),
                     'order' => 11])
 
+      ->addElement('multiCheckbox',
+                   'pick_day',
+                   ['label' => $this->_('Tous les'),
+                    'multiOptions' => ['1' => $this->_('lundi'),
+                                       '2' => $this->_('mardi'),
+                                       '3' => $this->_('mercredi'),
+                                       '4' => $this->_('jeudi'),
+                                       '5' => $this->_('vendredi'),
+                                       '6' => $this->_('samedi'),
+                                       '0' => $this->_('dimanche')],
+                    'order' => 12 ,
+                    'separator' => ' '])
+
       ->addElement('select', 'id_lieu', ['label' => $this->_('Lieu'),
                                          'multiOptions' => $this->getLocations(),
-                                         'order' => 12])
+                                         'order' => 19])
       ->addElement('ckeditor', 'contenu')
       ->addElement('ckeditor', 'description')
       ->addElement('textarea', 'tags',
@@ -104,14 +117,15 @@ class ZendAfi_Form_Admin_News extends ZendAfi_Form {
                          'id_cat',
                          'publication_range',
                          'event_range',
+                         'pick_day',
                          'all_day',
-                         'id_lieu'
-                         ],
+                         'id_lieu' ],
                         'publication',
                         ['legend' => $this->_('Publication')])
       ->addDisplayGroup(['contenu'],
                         'article',
                         ['legend' => $this->_('Article')])
+
       ->addDisplayGroup(['description'],
                         'article_for_widget',
                         ['legend' => $this->_('Résumé pour l\'affichage dans les boîtes')])
diff --git a/library/ZendAfi/View/Helper/Accueil/Calendar.php b/library/ZendAfi/View/Helper/Accueil/Calendar.php
index 67f8fa93b8a6bd8d03a6615fc497a59b6fe94e83..77715bd1fe8e0e77b67cd071fb443cf1c8dbe3dc 100644
--- a/library/ZendAfi/View/Helper/Accueil/Calendar.php
+++ b/library/ZendAfi/View/Helper/Accueil/Calendar.php
@@ -20,8 +20,6 @@
  */
 
 class ZendAfi_View_Helper_Accueil_Calendar extends ZendAfi_View_Helper_Accueil_Base {
-
-
   protected $class_calendar;
 
   protected function _renderHeadScriptsOn($script_loader) {
diff --git a/library/ZendAfi/View/Helper/Calendar/Table.php b/library/ZendAfi/View/Helper/Calendar/Table.php
index f4dc2d86a4007ae550227e5f3df2e2e1f1d5e13e..b62a2c93d45224bc7cf19ed9d2bd07119be085ee 100644
--- a/library/ZendAfi/View/Helper/Calendar/Table.php
+++ b/library/ZendAfi/View/Helper/Calendar/Table.php
@@ -18,8 +18,8 @@
  * 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_Calendar_Table extends Zend_View_Helper_HtmlElement {
-  use Trait_Translator, Trait_TimeSource;
+class ZendAfi_View_Helper_Calendar_Table extends ZendAfi_View_Helper_BaseHelper {
+  use Trait_TimeSource;
 
   protected
     $param,
@@ -32,93 +32,97 @@ class ZendAfi_View_Helper_Calendar_Table extends Zend_View_Helper_HtmlElement {
     $this->year = $year;
     $this->param = $param;
     $this->id_module = $param['ID_MODULE'];
-    $this->today = date("dmY", $this->getTimeSource()->time());
-
-    $this->WEEK_DAYS = array($this->_("dim"),
-                             $this->_("lun"),
-                             $this->_("mar"),
-                             $this->_("mer"),
-                             $this->_("jeu"),
-                             $this->_("ven"),
-                             $this->_("sam"));
-
-    $this->MONTHS = array($this->_('janvier'),
-                          $this->_('février'),
-                          $this->_('mars'),
-                          $this->_('avril'),
-                          $this->_('mai'),
-                          $this->_('juin'),
-                          $this->_('juillet'),
-                          $this->_('août'),
-                          $this->_('septembre'),
-                          $this->_('octobre'),
-                          $this->_('novembre'),
-                          $this->_('décembre'));
+    $this->today = date('dmY', $this->getTimeSource()->time());
+
+    $this->WEEK_DAYS = [$this->_('dim'),
+                        $this->_('lun'),
+                        $this->_('mar'),
+                        $this->_('mer'),
+                        $this->_('jeu'),
+                        $this->_('ven'),
+                        $this->_('sam')];
+
+    $this->MONTHS = [$this->_('janvier'),
+                     $this->_('février'),
+                     $this->_('mars'),
+                     $this->_('avril'),
+                     $this->_('mai'),
+                     $this->_('juin'),
+                     $this->_('juillet'),
+                     $this->_('août'),
+                     $this->_('septembre'),
+                     $this->_('octobre'),
+                     $this->_('novembre'),
+                     $this->_('décembre')];
 
     return $this->getTableCalendar($articles);
 
   }
 
 
-  function getURL($type,$jour = "") {
-    switch($type) {
-      case "LAST_MONTH" :
-        $url = $this->buildUrl(['controller' => 'cms',
-                'action' => 'calendar',
-                                'date' => $this->getLastMonth($this->month, $this->year)]);
-        break;
-
-      case "MONTH" :
-        if(strlen($this->month) == 1) $mois='0'.$this->month; else $mois = $this->month;
-        $url = $this->buildUrl(['controller' => 'cms',
-                'action' => 'articleviewbydate',
-                                'd' => $this->year.'-'.$mois]);
-        break;
-
-      case "NEXT_MONTH" :
-        $url = $this->buildUrl(['controller' => 'cms',
-                'action' => 'calendar',
-                                'date' => $this->getNextMonth($this->month, $this->year)]);
-        break;
-
-      case "EVENTS" :
-        if(strlen($this->month) == 1) $mois='0'.$this->month; else $mois = $this->month;
-        if(strlen($jour) == 1) $day='0'.$jour; else $day = $jour;
-        $url = $this->buildUrl(['controller' => 'cms',
-                'action' => 'articleviewbydate',
-                                'd' => $this->year.'-'.$mois.'-'.$day]);
-        break;
-    }
+  protected function getUrl($type, $day='', $month='') {
+    $mapping =
+      ['LAST_MONTH' => (new Class_Entity)->setMethod('_lastMonthUrl'),
+       'MONTH' => (new Class_Entity)->setMethod('_currentMonthUrl'),
+       'NEXT_MONTH' => (new Class_Entity)->setMethod('_nextMonthUrl'),
+       'EVENTS' => (new Class_Entity)->setMethod('_dayUrl')->setParams([$day, $month])];
+
+    if (!array_key_exists($type, $mapping))
+      return '';
 
-    return $this->view->url($url + ['id_module' => $this->id_module,
-                                    'id_profil' => Class_Profil::getCurrentProfil()->getId(),
-                                    'select_id_categorie' => $this->param["SELECT_ID_CAT"]]);
+    $proxy = $mapping[$type];
+    $params = call_user_func_array([$this, $proxy->getMethod()],
+                                   ($params = $proxy->getParams()) ? $params : []);
+
+    return $this->view->url($this->buildUrl($params)
+                            + ['id_module' => $this->id_module,
+                               'id_profil' => Class_Profil::getCurrentProfil()->getId(),
+                               'select_id_categorie' => $this->param["SELECT_ID_CAT"]]);
   }
 
 
-  function getLastMonth($month, $year) {
-    if ($month == 1) {
-      $new_month = "12";
-      $new_year  = $year - 1;
-    } else {
-      $new_month = (($month > 10)?"":"0").($month - 1);
-      $new_year  = $year;
-    }
+  protected function _lastMonthUrl() {
+    return ['controller' => 'cms',
+            'action' => 'calendar',
+            'date' => $this->getLastMonth($this->month, $this->year)];
+  }
+
 
-    return $new_year.'-'.$new_month;
+  protected function _currentMonthUrl() {
+    return ['controller' => 'cms',
+            'action' => 'articleviewbydate',
+            'd' => $this->year . '-' . str_pad($this->month, 2, '0', STR_PAD_LEFT)];
   }
 
 
-  function getNextMonth($month, $year) {
-    if ($month == 12) {
-      $new_month = "01";
-      $new_year  = $year + 1;
-    } else {
-      $new_month = (($month < 9)?"0":"").($month + 1);
-      $new_year  = $year;
-    }
+  protected function _nextMonthUrl() {
+    return ['controller' => 'cms',
+            'action' => 'calendar',
+            'date' => $this->getNextMonth($this->month, $this->year)];
+  }
+
+
+  protected function _dayUrl($day, $month) {
+    $month = str_pad(($month ? $month : $this->month), 2, '0', STR_PAD_LEFT);
+    $day = '' == $day ? '' : str_pad($day, 2, '0', STR_PAD_LEFT);
+
+    return ['controller' => 'cms',
+            'action' => 'articleviewbydate',
+            'd' => $this->year . '-' . $month . '-' . $day];
+  }
+
+
+  protected function getLastMonth($month, $year) {
+    return $month == 1
+      ? (($year - 1) . '-12')
+      : ($year . '-' . str_pad($month - 1, 2, '0', STR_PAD_LEFT));
+  }
 
-    return $new_year.'-'.$new_month;
+
+  protected function getNextMonth($month, $year) {
+    return $month == 12
+      ? (($year + 1) . '-01')
+      : ($year . '-' . str_pad($month + 1, 2, '0', STR_PAD_LEFT));
   }
 
 
@@ -127,8 +131,9 @@ class ZendAfi_View_Helper_Calendar_Table extends Zend_View_Helper_HtmlElement {
     // jour -> [articles *], ça simplifie pas mal de code.
     // Pour construire le calendrier, il suffira de parcourir
     // le dictionnaire et virer tout ce code bizarre.
-    $day = (int)gmdate("j", $date);
-    $month = (int)gmdate("m", $date);
+    $day = (int)date('j', $date);
+    $month = (int)date('m', $date);
+    $day_num = (int)date('w', $date);
 
     foreach($events as $event) {
       $event_debut = strtotime($event->getEventsDebut());
@@ -139,7 +144,10 @@ class ZendAfi_View_Helper_Calendar_Table extends Zend_View_Helper_HtmlElement {
       $jour_fin = (int)date('j', $event_fin);
       $mois_fin = (int)date('m', $event_fin);
 
-      // Jour clickable
+      if(!empty($event->getPickDayAsArray())
+         && !in_array($day_num, $event->getPickDayAsArray()))
+        continue;
+
       if($mois_debut == $month && $mois_fin == $month) {
         if($day >= $jour_debut && $day <= $jour_fin)
           return true;
@@ -160,119 +168,120 @@ class ZendAfi_View_Helper_Calendar_Table extends Zend_View_Helper_HtmlElement {
           return true;
       }
     }
+
     return false;
   }
 
 
   protected function anEventStartThisDay($date, $events) {
-    $day = (int)gmdate("j", $date);
-    $month = (int)gmdate("m", $date);
+    $day = (int)date('j', $date);
+    $month = (int)date('m', $date);
+
     foreach($events as $event) {
       $event_debut = strtotime($event->getEventsDebut());
-      if (((int)date('m', $event_debut) == $month) && ((int)date('j', $event_debut) == $day))
+      if (((int)date('m', $event_debut) == $month)
+          && ((int)date('j', $event_debut) == $day))
         return true;
     }
+
     return false;
   }
 
 
   protected function getTableCalendar($articles) {
-    return $this->view->tag('div',$this->getTableCalendarContent($articles));
+    return $this->_tag('div', $this->getTableCalendarContent($articles));
   }
 
 
   protected function getTableCalendarContent($articles) {
-    return $this->view->tag('table',
-                            $this->getCaption($this->view->_('Calendrier'))
-                            . $this->getTableCalendarHeader()
-                            . $this->getTableCalendarTable($articles),
-                            ['class' => 'calendar_main']);
+    return $this->_tag('table',
+                       $this->getCaption($this->_('Calendrier'))
+                       . $this->getTableCalendarHeader()
+                       . $this->getTableCalendarTable($articles),
+                       ['class' => 'calendar_main']);
   }
 
 
   protected function getCaption($summary) {
-    return $this->view->tag('caption',
-                            $this->view->tag('details',
-                                             $this->view->tag('summary', $summary)), ['style' => 'display: none']);
+    return $this->_tag('caption',
+                       $this->_tag('details',
+                                   $this->_tag('summary', $summary)),
+                       ['style' => 'display: none']);
   }
 
 
   protected function getTableCalendarHeader() {
-    $anchor_class=  ['class' => 'calendar_title_month_clickable'];
-
-    $anchor_class['class'].= $this->param['DISPLAY_FULL_PAGE'] ? '' : ' calendar_ajax_ready';
-
-    $previews_month =
-      $this->view->tagAnchor($this->getURL('LAST_MONTH'),
-                             '&laquo;&nbsp;',
-                             $anchor_class);
-    $current_month =
-      $this->view->tagAnchor($this->getURL('MONTH'),
-                             $this->MONTHS[$this->month-1].strftime(" %Y", mktime(5,0,0, $this->month, 1, $this->year)),
-                             $anchor_class);
-    $next_month =
-      $this->view->tagAnchor($this->getURL('NEXT_MONTH'),
-                             '&nbsp;&raquo;',
-                             $anchor_class);
-
-    $tds_content = $this->view->tag('td', '', ['class' => 'calendar_title_left_arrow']);
-    $tds_content .= $this->view->tag('td', $previews_month . $current_month . $next_month, ['class' => 'calendar_title_month']);
-    $tds_content .= $this->view->tag('td', '', ['class' => 'calendar_title_right_arrow']);
-
-    return $this->view->tag('tr', $tds_content, ['class' => 'calendar_title']);
+    $anchor_class = ['class' => 'calendar_title_month_clickable'];
+    $anchor_class['class'] .= $this->param['DISPLAY_FULL_PAGE']  ? '' : ' calendar_ajax_ready';
+
+    $previous = $this->view->tagAnchor($this->getUrl('LAST_MONTH'), '&laquo;&nbsp;', $anchor_class);
+
+    $current = $this->view
+      ->tagAnchor($this->getUrl('MONTH'),
+                  $this->MONTHS[$this->month-1] . strftime(' %Y', mktime(5,0,0, $this->month, 1, $this->year)),
+                  $anchor_class);
+
+    $next = $this->view->tagAnchor($this->getUrl('NEXT_MONTH'), '&nbsp;&raquo;', $anchor_class);
+
+    return $this->_tag('tr',
+                       $this->_tag('td', '', ['class' => 'calendar_title_left_arrow'])
+                       . $this->_tag('td', $previous . $current . $next,
+                                     ['class' => 'calendar_title_month'])
+                       .  $this->_tag('td', '', ['class' => 'calendar_title_right_arrow']),
+                       ['class' => 'calendar_title']);
   }
 
 
   protected function buildUrl($url = []) {
-    if (!$this->param['DISPLAY_FULL_PAGE']) {
-      $url['action'] = 'calendar';
-
-      if(isset($url['d'])){
-        $url['date'] = $url['d'];
-        unset($url['d']);
-      }
+    if ($this->param['DISPLAY_FULL_PAGE'])
+      return $url;
 
-      $url = $url + ['render' => 'ajax'];
+    $url['action'] = 'calendar';
+    if(isset($url['d'])) {
+      $url['date'] = $url['d'];
+      unset($url['d']);
     }
+    $url['render'] = 'ajax';
+
     return $url;
   }
 
 
   protected function getTableCalendarTable($articles) {
-    return $this->view->tag('tr',
-                            $this->view->tag('td',
-                                             $this->calendarTable($articles),
-                                             ['colspan' => 3]));
+    return $this->_tag('tr', $this->_tag('td',
+                                         $this->calendarTable($articles),
+                                         ['colspan' => 3]));
   }
 
 
   protected function calendarTable($articles) {
-    return $this->view->tag('table',
-                            $this->getCaption($this->view->_('Calendrier en jours du mois'))
-                            . $this->getCalendarTableHeaders()
-                            . $this->getCalendarTableDays($articles),
-                            ['class' => 'calendar_table']);
+    return $this->_tag('table',
+                       $this->getCaption($this->view->_('Calendrier en jours du mois'))
+                       . $this->getCalendarTableHeaders()
+                       . $this->getCalendarTableDays($articles),
+                       ['class' => 'calendar_table']);
   }
 
 
   protected function getCalendarTableHeaders() {
-    $headers='';
-    for ($counter = 0; $counter < 7; $counter++) {
-      $headers.= $this->view->tag('th', $this->WEEK_DAYS[(1 + $counter) % 7], ['scope' => 'col']);
-    }
-    return $this->view->tag('tr', $headers);
+    $headers = '';
+    for ($counter = 0; $counter < 7; $counter++)
+      $headers .= $this->_tag('th', $this->WEEK_DAYS[(1 + $counter) % 7],
+                              ['scope' => 'col']);
+
+    return $this->_tag('tr', $headers);
   }
 
 
   protected function getCalendarTableDays($articles) {
-    $html = '';
-    $first_month_day = gmmktime(0, 0, 0, $this->month, 1, $this->year);
-    $offset = (7 - (1 % 7 - gmdate("w", $first_month_day))) % 7;
+    $first_month_day = mktime(0, 0, 0, $this->month, 1, $this->year);
+    $offset = (7 - (1 % 7 - date('w', $first_month_day))) % 7;
     $current_day = $first_month_day - 3600 * 24 * $offset;
-    $row_number = ceil((gmdate("t", $first_month_day) + $offset) / 7);
+    $row_number = ceil((date('t', $first_month_day) + $offset) / 7);
 
+    $html = '';
     for ($row = 1; $row <= $row_number; $row++)
-      $html.= $this->view->tag('tr', $this->getCalendarTableCols($articles, $current_day));
+      $html .= $this->_tag('tr', $this->getCalendarTableCols($articles, $current_day));
 
     return $html;
   }
@@ -280,60 +289,81 @@ class ZendAfi_View_Helper_Calendar_Table extends Zend_View_Helper_HtmlElement {
 
   protected function getCalendarTableCols($articles, &$current_day) {
     $html = '';
-    for ($column = 1; $column <= 7; $column++) {
-      $html.= $this->getCalendarTableColsDays($articles, $current_day);
+    foreach (range(1, 7) as $i) {
+      $html .= $this->getCalendarTableColsDays($articles, $current_day);
       $current_day += 3600 * 24 + 1;
     }
+
     return $html;
   }
 
 
-  protected function getCalendarTableColsDays($articles, $current_day) {
-    $html = '';
-    // Day currently displayed
-    $day = gmdate("j", $current_day);
+  protected function getCalendarTableColsDays($events, $time) {
+    $options = $this->_isTimestampWeekend($time)
+      ? ['class' => 'calendar_weekend'] : [];
 
-    // If it is saturday or sunday, we use the "weekend" style
-    if (gmdate("w", $current_day) == 6 || gmdate("w", $current_day) == 0) {
-      $table_cell = "         <td class=\"calendar_weekend\">";
-    }
-    else {
-      $table_cell = "         <td>";
-    }
+    return $this
+      ->_tag('td',
+             $this->_renderCellContent($time, $events),
+             $options);
+  }
 
-    // We display the current day
-    $day_classes = [];
-
-    if (gmdate("dmY", $current_day) == $this->today) {
-      $day_classes []= "calendar_today_clickable";
-      $today_click = $this->view->tag('b', $day);
-    } else {
-      $today_click=$day;
-      if (gmdate("n", $current_day) != $this->month) {
-        $day_classes []= "calendar_other_month";
-      } else {
-        $day_classes []= "calendar_day_non_clickable";
-      }
-    }
 
-    if ($this->dayHasEvents($current_day, $articles))
-      $day_classes[]= "day_clickable";
+  protected function _renderCellContent($time, $events) {
+    $day = date('j', $time);
+    $month = date('n', $time);
 
-    if ($this->anEventStartThisDay($current_day, $articles))
-      $day_classes[]= "calendar_day_event_start";
+    $classes = array_merge($this->_getCellBasicClasses($time),
+                           $this->_getCellEventsClasses($time, $events));
 
-    $cell_classes = implode(' ', array_unique($day_classes));
-    if (in_array('day_clickable', $day_classes)) {
-      $options = ['class' => $cell_classes, 'target' => '_parent'];
-      $options['class'].= $this->param['DISPLAY_FULL_PAGE'] ? '' : ' calendar_ajax_ready';
-      $table_cell.= $this->view->tagAnchor($this->getURL('EVENTS',$day), $today_click, $options);
-    } else {
-      $table_cell .= $this->view->tag('span', $day,['class' => $cell_classes]);
-    }
+    if (!in_array('day_clickable', $classes))
+      return $this->_tag('span', $day,
+                         ['class' => implode(' ', $classes)]);
+
+    if ($this->param['DISPLAY_FULL_PAGE'])
+      $classes[] = 'calendar_ajax_ready';
+
+    return $this->view
+      ->tagAnchor($this->getUrl('EVENTS', $day, $month),
+                  $this->_isTimestampToday($time) ? $this->_tag('b', $day) : $day,
+                  ['class' => implode(' ', $classes),
+                   'target' => '_parent']);
+  }
+
+
+  protected function _isTimestampToday($time) {
+    return date('dmY', $time) == $this->today;
+  }
+
+
+  protected function _isTimestampWeekend($time) {
+    return in_array(date('w', $time), [0, 6]);
+  }
+
+
+  protected function _isTimestampThisMonth($time) {
+    return date('n', $time) == $this->month;
+  }
+
+
+  protected function _getCellBasicClasses($time) {
+    if ($this->_isTimestampToday($time))
+      return ['calendar_today_clickable'];
+
+    return [(!$this->_isTimestampThisMonth($time))
+            ? 'calendar_other_month' : 'calendar_day_non_clickable'];
+  }
+
+
+  protected function _getCellEventsClasses($time, $events) {
+    $classes = [];
+
+    if ($this->dayHasEvents($time, $events))
+      $classes[] = 'day_clickable';
+
+    if ($this->anEventStartThisDay($time, $events))
+      $classes[] = 'calendar_day_event_start';
 
-    // End of day cell
-    return $html.=$table_cell."</td>";
-    // Next day
+    return $classes;
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/FormLabel.php b/library/ZendAfi/View/Helper/FormLabel.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ac92c25164d9c6e3743452f82bbb8a878498dcd
--- /dev/null
+++ b/library/ZendAfi/View/Helper/FormLabel.php
@@ -0,0 +1,48 @@
+<?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 ZendAfi_View_Helper_FormLabel extends Zend_View_Helper_FormElement
+{
+
+  protected function displayFor($name,$id) {
+    if (strpos($name, '[]'))
+      return '';
+    return ' for="' . $this->view->escape($id) . '"';
+  }
+
+
+  public function formLabel($name, $value = null, array $attribs = array())
+  {
+    $info = $this->_getInfo($name, $value, $attribs);
+
+    if ($info['disable'])
+      return '';
+
+    $value = ($info['escape']) ? $this->view->escape($info['value']) : $info['value'];
+    $xhtml = '<label'
+      . $this->displayFor($name,$info['id'])
+      . $this->_htmlAttribs($info['attribs'])
+      . '>' . $value . '</label>';
+
+    return $xhtml;
+  }
+}
diff --git a/library/ZendAfi/View/Helper/TagArticleEvent.php b/library/ZendAfi/View/Helper/TagArticleEvent.php
index 3fcec82355d32f675c03080148753a2460d28cd5..743e28e054b5ea44cc4819250d35726cf653b237 100644
--- a/library/ZendAfi/View/Helper/TagArticleEvent.php
+++ b/library/ZendAfi/View/Helper/TagArticleEvent.php
@@ -70,9 +70,38 @@ class ZendAfi_View_Helper_TagArticleEvent extends Zend_View_Helper_HtmlElement {
     if ($year_start == $year_end)
       $year_start = '';
 
-    return $this->view->_('Du %s au %s',
-        trim(strftime('%A %d', $date_start) . ' ' . $month_start . ' ' . $year_start),
-        trim(strftime('%A %d', $date_end) . ' ' . $month_end . ' ' . $year_end));
+    $picked_days = $article->getPickDayAsArray();
+
+    if(empty($article->getPickDayAsArray()) || 7 == count($picked_days))
+       return $this->view->_('Du %s au %s',
+                             trim(strftime('%A %d', $date_start) . ' ' . $month_start . ' ' . $year_start),
+                             trim(strftime('%A %d', $date_end) . ' ' . $month_end . ' ' . $year_end));
+
+    $open_days = $this->getTextualDays($picked_days);
+
+    return $this->view->_('Les ' . $open_days . 's du %s au %s',
+                             trim(strftime('%e', $date_start) . ' ' . $month_start . ' ' . $year_start),
+                             trim(strftime('%e', $date_end) . ' ' . $month_end . ' ' . $year_end));
+  }
+
+
+  protected function getTextualDays($days) {
+    if(1 == count($days))
+      return  $this->numericDayToTextual($days[0]);
+
+    $last = array_pop($days);
+    $last = $this->numericDayToTextual($last);
+
+    $textual_days = '';
+    foreach($days as $day)
+      $textual_days.= $this->numericDayToTextual($day) . ', ';
+
+    return substr($textual_days, 0, -2) . $this->view->_('s et ') . $last;
+  }
+
+
+  protected function numericDayToTextual($nb) {
+    return strftime('%A', strtotime('Sunday +' . $nb . ' days'));
   }
 }
 ?>
diff --git a/library/startup.php b/library/startup.php
index 88dd7a5da7b58cae7ed3799372a91c9e7273b734..4c441d2789b529950563319cc9fb4348a77a097f 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -64,8 +64,7 @@ function defineConstant($name, $value) {
 
 function setupConstants() {
   defineConstant('BOKEH_MAJOR_VERSION','7.3');
-
-  defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.30');
+  defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.32');
 
   defineConstant('BOKEH_REMOTE_FILES', 'http://git.afi-sa.fr/afi/opacce/');
 
diff --git a/scripts/clean_domains_url.php b/scripts/clean_domains_url.php
new file mode 100644
index 0000000000000000000000000000000000000000..b70a4e1d987c25cb86403e3626294854d30e52f2
--- /dev/null
+++ b/scripts/clean_domains_url.php
@@ -0,0 +1,38 @@
+<?php
+if (!$argv[2]) {
+  echo 'Missing arguments : hostname basedir';
+  exit(1);
+}
+
+$hostname = $argv[1];
+$basedir = $argv[2];
+
+chdir($basedir);
+
+require('console.php');
+
+echo BASE_URL . "\n";
+
+$loader = function() use($basedir) {
+  return Class_Catalogue::findAllBy(['where' => 'url_img like "%' . $basedir . '%"']);
+};
+
+$items = $loader();
+echo count($items) . " domains to process found\n";
+
+$occurences = 0;
+foreach ($items as $item) {
+  echo $item->getId() . ' : ' . $item->getUrlImg() . " -> ";
+  $item
+    ->setUrlImg(Class_CmsUrlTransformer::removeBaseDir($hostname, $basedir, $item->getUrlImg()))
+    ->save();
+  echo $item->getUrlImg() . "\n";
+  $occurences++;
+}
+
+if ($items = $loader()) {
+  $ids = array_map(function($a) {return $a->getId();}, $items);
+  echo "check domains : " . implode(', ', $ids) . "\n";
+}
+
+echo $occurences . " occurence(s) replaced.\n";
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/CmsControllerTest.php b/tests/application/modules/admin/controllers/CmsControllerTest.php
index a6028e76b5d82efedd6f560fb46bdf90e0ab44e3..20ba7a8896acd63d12d8d68329800a97a4af64f5 100644
--- a/tests/application/modules/admin/controllers/CmsControllerTest.php
+++ b/tests/application/modules/admin/controllers/CmsControllerTest.php
@@ -202,6 +202,7 @@ abstract class CmsControllerTestCase extends Admin_AbstractControllerTestCase {
                                      'date_creation' => '2010-12-25 10:23:23',
                                      'date_maj' => '2010-12-26  11:12:34',
                                      'domaine_ids' => [11,12],
+                                     'pick_day' => 'monday',
                                      'avis_users' => []]);
 
     $this->cat_evenements->setArticles([$this->concert]);
@@ -2839,4 +2840,10 @@ class CmsControllerEditArticleWithDate30December2014Test extends CmsControllerte
   public function inputFinShouldContains30_12_2014() {
     $this->assertXPath('//input[@name="fin"][@value="30/12/2014"]');
   }
+
+
+  /** @test */
+  public function mondayShouldBePicked() {
+    $this->assertXPath('//input[@checked="checked"][@value="1"]');
+  }
 }
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
index 2edb7010273ce43be18b4d26e29fa3c1e136a513..6d7e40922d942a44aa214f8ab2bd2c62f5a3cf99 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
@@ -1853,7 +1853,7 @@ class NoticeAjaxControllerBiographieNonTrouveeTest extends NoticeAjaxControllerB
 
 
 abstract class NoticeAjaxControllerLocalisationTestCase extends AbstractControllerTestCase {
-  protected $_json;
+  protected $_json, $_storm_default_to_volatile = true;
 
 
   public function setUp() {
diff --git a/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php b/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php
index 6003d2c856d8580efb3871abdac46f77ec867b76..36620c78a3190c19d8c17f935eb6930b0304182e 100644
--- a/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php
+++ b/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php
@@ -844,6 +844,7 @@ class ProfilOptionsControllerProfilAdulteAsAdminPostConfigKiosqueStyleReloadTest
 
 
 abstract class ProfilOptionsControllerProfilJeunesseWithPagesJeuxMusiqueTestCase extends ProfilOptionsControllerWithProfilAdulteTestCase {
+
   protected function _loginHook($account) {
     $account->ROLE = "";
     $account->ROLE_LEVEL = 0;
@@ -998,7 +999,6 @@ class ProfilOptionsControllerPopupLoginProfilAdulteTest extends ProfilOptionsCon
     $_json,
     $_xpath;
 
-
   public function setUp() {
     parent::setUp();
 
@@ -1021,8 +1021,9 @@ class ProfilOptionsControllerPopupLoginProfilAdulteTest extends ProfilOptionsCon
 
 
 class ProfilOptionsControllerProfilJeunesseAndJeuxTest extends ProfilOptionsControllerProfilJeunesseWithPagesJeuxMusiqueTestCase {
+
   /** @test */
-  function boiteActionForModuleIdOneShouldDisplayBoiteRecherche() {
+  public function boiteActionForModuleIdOneShouldDisplayBoiteRecherche() {
     $this->dispatch('/opac/index/embed_module/id_profil/230/id_module/1', true);
     $this->assertXPath('//div[@class="recherche_avancee"]//a[contains(@href, "avancee")]');
   }
@@ -1209,7 +1210,6 @@ class ProfilOptionsControllerProfilBreadcrumbHomePageTest extends ProfilOptionsC
 
 class ProfilOptionsControllerProfilJeunesseViewPageJeuxTest extends ProfilOptionsControllerProfilJeunesseWithPagesJeuxMusiqueTestCase {
 
-
   public function setUp() {
     parent::setUp();
 
@@ -1421,7 +1421,10 @@ class ProfilOptionsControllerViewProfilJeunesseAccueilTest extends ProfilOptions
 
   public function setUp() {
     parent::setUp();
-    $this->fixture('Class_AdminVar',['id'=>'MULTIMEDIA_KEY','valeur'=>'81b3ab7b0b9a621afb6044a9c2f48ed2']);
+    $this->fixture('Class_AdminVar',
+                   ['id'=>'MULTIMEDIA_KEY',
+                    'valeur'=>'81b3ab7b0b9a621afb6044a9c2f48ed2']);
+
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_TypeDoc')
       ->whenCalled('findUsedTypeDocIds')
       ->answers([1, 2, 4]);
@@ -1668,6 +1671,8 @@ class UserRoleLevelThreeViewPrivateProfilTest extends AbstractControllerTestCase
 
 
 abstract class ProfilOptionsControllerProfilBoiteCalendarTestCase extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
   public function setup() {
     parent::setup();
 
@@ -1675,25 +1680,46 @@ abstract class ProfilOptionsControllerProfilBoiteCalendarTestCase extends Abstra
                           ['id' => 2,
                            'libelle' => 'Library']);
 
-    $category = $this->fixture('Class_ArticleCategorie',
-                               ['id' => 12,
-                                'libelle' => 'Fiesta à Annecy',
-                                'bib' => $bib]);
-
-    $article = $this->fixture('Class_Article',
-                              ['id' => 34,
-                               'titre' => 'Fête du caion',
-                               'contenu' => 'Fête du caion',
-                               'description' => 'à Annecy',
-                               'events_debut' => '2011-09-03',
-                               'events_fin' => '2011-09-03',
-                               'categorie' => $category,
-                               'bib' => $bib]);
+    $fiesta_annecy = $this->fixture('Class_ArticleCategorie',
+                                    ['id' => 12,
+                                     'libelle' => 'Fiesta à Annecy',
+                                     'bib' => $bib]);
+
+    $fete_caion = $this->fixture('Class_Article',
+                                    ['id' => 34,
+                                     'titre' => 'Fête du caion',
+                                     'contenu' => 'Fiesta fiesta',
+                                     'description' => 'à Annecy',
+                                     'events_debut' => '2011-09-01',
+                                     'events_fin' => '2011-09-01',
+                                     'categorie' => $fiesta_annecy,
+                                     'bib' => $bib]);
+
+    $fete_moulins = $this->fixture('Class_Article',
+                                   ['id' => 35,
+                                    'titre' => 'Fête du livre',
+                                    'contenu' => 'Fiesta fiesta',
+                                    'description' => 'à Moulins',
+                                    'events_debut' => '2011-09-05',
+                                    'events_fin' => '2011-10-05',
+                                    'pick_day' => '123',
+                                    'categorie' => $fiesta_annecy]);
+
+    $fete_valleiry = $this->fixture('Class_Article',
+                                    ['id' => 36,
+                                    'titre' => 'Fête de la FIA',
+                                    'contenu' => 'Fiesta fiesta à la FIA',
+                                    'description' => 'à Valleiry',
+                                    'events_debut' => '2011-09-15',
+                                    'events_fin' => '2011-10-30',
+                                    'pick_day' => '',
+                                    'categorie' => $fiesta_annecy]);
 
-    Class_AdminVar::beVolatile();
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Article')
       ->whenCalled('getArticlesByPreferences')
-      ->answers([$article]);
+      ->answers([$fete_caion,
+                 $fete_moulins,
+                 $fete_valleiry]);
   }
 }
 
@@ -1716,6 +1742,11 @@ class ProfilOptionsControllerProfilBoiteCalendarWithCalendarDisplayedAndModeSimp
       ->setLibelle('Profil Portail')
       ->setCfgAccueil($cfg_accueil);
 
+    $time_source = new TimeSourceForTest('2011-09-03 12:30:00');
+
+    Class_Calendar::setTimeSource($time_source);
+    ZendAfi_View_Helper_CalendarContent::setTimeSource($time_source);
+
     $this->dispatch('/opac');
   }
 
@@ -1777,7 +1808,13 @@ class ProfilOptionsControllerProfilBoiteCalendarWithCalendarDisplayedAndModeSimp
 
   /** @test **/
   public function boiteCalendarLiShouldContainsCalendarEventDate() {
-    $this->assertXPathContentContains('//li/span[@class="calendar_event_date"]', 'samedi 03 septembre 2011');
+    $this->assertXPathContentContains('//li/span[@class="calendar_event_date"]', 'jeudi 01 septembre 2011');
+  }
+
+
+  /** @test */
+  public function firstSeptemberShouldContainsLink() {
+    $this->assertXPath('//table//tr/td[4]//a[contains(@class, "day_clickable")]' );
   }
 
 
@@ -1791,9 +1828,20 @@ class ProfilOptionsControllerProfilBoiteCalendarWithCalendarDisplayedAndModeSimp
   public function linkToBibShouldBePresentcontextShouldExpectation() {
     $this->assertXPathContentContains('//div[contains(@class,"calendar")]//a[contains(@class, "calendar_event_info")][contains(@href, "cms/articleviewbydate/id_module/45/b/2")]', 'Library');
   }
-}
 
 
+  /** @test */
+  public function saturdayThe10ThShouldNotContainsLink() {
+    $this->assertXPathContentContains('//td[@class="calendar_weekend"]//span[@class="calendar_day_non_clickable"]', '10');
+  }
+
+
+  /** @test */
+  public function fridayShouldContainsLink() {
+    $this->assertXPath('//table//tr/td[5]//a[contains(@class, "day_clickable")]',  $this->_response->getBody());
+  }
+}
+
 
 
 class ProfilOptionsControllerProfilBoiteCalendarWithCalendarDisplayedWithModeSimpleAndCatOption extends ProfilOptionsControllerProfilBoiteCalendarTestCase {
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 27b369b6148fe4e8a77da153da31facd217e78ba..0e3ce5da0359b26cc6ee3a2b9907b505de3a7402 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -67,7 +67,7 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
       return false;
 
     try {
-      $adapter->query("select $column from $table limit 1");
+      $this->query("select $column from $table limit 1");
     } catch (Exception $e) {
       return false;
     }
@@ -93,6 +93,21 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
 
     return $result;
   }
+
+
+  protected function assertColumn($table, $column, $message='') {
+    try {
+      $this->query(sprintf('select `%s` from `%s` limit 1',
+                           $column, $table));
+    } catch (Exception $e) {
+      $message = $message
+        ? $message
+        : sprintf('Failed asserting that "%s" table CONTAINS a "%s" column.',
+                  $table, $column);
+
+      $this->fail($message);
+    }
+  }
 }
 
 
@@ -216,7 +231,6 @@ class UpgradeDB_278_Test extends UpgradeDBTestCase {
 
 
 
-
 class UpgradeDB_279_Test extends UpgradeDBTestCase {
   public function prepare() {
     try {
@@ -224,7 +238,6 @@ class UpgradeDB_279_Test extends UpgradeDBTestCase {
     } catch(Exception $e) {}
   }
 
-
   /** @test */
   public function newslettersDraftColumnshouldExist() {
     $this->assertColumnExists('newsletters.draft');
@@ -233,6 +246,20 @@ class UpgradeDB_279_Test extends UpgradeDBTestCase {
 
 
 
+class UpgradeDB_280_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    try {
+      $this->query('alter table `cms_article` drop `pick_day`');
+    } catch(Exception $e) {}
+  }
+
+  /** @test */
+  public function pickDayColumnShouldBeAdded() {
+    $this->assertColumn('cms_article', 'pick_day');
+  }
+}
+
+
 
 class UpgradeDB_281_Test extends UpgradeDBTestCase {
   public function prepare() {
@@ -262,4 +289,4 @@ class UpgradeDB_281_Test extends UpgradeDBTestCase {
     $this->assertEquals(['type_aff' => '3', 'nb_aff' => '2', 'display_order' => 'Random'],
                         unserialize($data['cfg_accueil'])['modules']['2']['preferences']);
   }
-}
\ No newline at end of file
+}
diff --git a/tests/library/Class/ArticleTest.php b/tests/library/Class/ArticleTest.php
index dfa55991f66f689a0f126058a8c0482612683a20..dabfc3b3daab0d4b097ccc3d82672107f133f196 100644
--- a/tests/library/Class/ArticleTest.php
+++ b/tests/library/Class/ArticleTest.php
@@ -1321,4 +1321,10 @@ class ArticleFirstImagePathTest extends Storm_Test_ModelTestCase {
   public function firstImagePathShoulReturnUserfileImagesCatMyImage() {
     $this->assertEquals('./userfiles/images/cat/my_image.jpg', Class_Article::find(1)->getFirstImagePath());
   }
+
+
+  /** @test */
+  public function pickDayShouldBeAnEmptyArray() {
+    $this->assertEquals([], $this->_article->getPickDayAsArray());
+  }
 }
diff --git a/tests/library/Class/Import/Typo3Test.php b/tests/library/Class/Import/Typo3Test.php
index eb6631263976e4d77f17340842f908d963f60daf..7ff9cf15502edc525d1d006c9582837ca80d79c4 100644
--- a/tests/library/Class/Import/Typo3Test.php
+++ b/tests/library/Class/Import/Typo3Test.php
@@ -665,7 +665,8 @@ class Import_Typo3UpdateArticleTest extends Import_Typo3TestCase {
   }
 
   public function expectedUpdateArticle() {
-    $articles = MockTypo3DB::findAllArticles();
+    $db = new MockTypo3DB();
+    $articles = $db->findAllArticles();
     return [
             [
              'title' => 'Tous à bord du bateau pirate',
diff --git a/tests/library/Class/ProfilTest.php b/tests/library/Class/ProfilTest.php
index 4e4f13d2f671dbc7aa1ab1512dc21a722b97c64f..4e90e0d108410b19dd2f8f1573bbe276202c9082 100644
--- a/tests/library/Class/ProfilTest.php
+++ b/tests/library/Class/ProfilTest.php
@@ -20,6 +20,8 @@
  */
 
 class ProfilVideTest extends ModelTestCase {
+  protected $_storm_default_to_volatile = true;
+
   public function setUp() {
     parent::setup();
     $this->profil_vide = new Class_Profil();
@@ -58,7 +60,7 @@ class ProfilVideTest extends ModelTestCase {
   public function profilZonesTitreShouldReturn200_E_H_I() {
     $this->assertEquals(
       ['200$e', '200$h', '200$i'],
-      Class_Profil::getCurrentProfil()->getZonesTitre());
+      $this->profil_vide->getZonesTitre());
   }
 
 
@@ -71,6 +73,8 @@ class ProfilVideTest extends ModelTestCase {
 
 
 class ProfilJeunesseAstrolabeTest extends ModelTestCase {
+  protected $_storm_default_to_volatile = true;
+
   public function setUp() {
     $this->fixture('Class_AdminVar', ['clef' => 'NOM_DOMAINE',
                                       'id' => 'NOM_DOMAINE',
@@ -81,77 +85,65 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
                                           'preferences' => []]],
                     'options' => []];
 
-    $this->profil_astro = Class_Profil::getLoader()
-      ->newInstanceWithId(7)
-      ->setIdSite(12)
-      ->setLibelle("Jeunesse")
-      ->setSkin('astrolabe')
-      ->setCfgMenus(array())
-      ->setHeaderCss('afi-opac3/userfiles/jeunesse.css')
-      ->setCfgAccueil($cfg_accueil);
+    $this->profil_astro = $this->fixture('Class_Profil',
+                                         ['id' => 7,
+                                          'id_site' => 12,
+                                          'libelle' => 'Jeunesse',
+                                          'skin' => 'astrolabe',
+                                          'cfg_menus' => [],
+                                          'header_css' => 'afi-opac3/userfiles/jeunesse.css',
+                                          'cfg_accueil' => $cfg_accueil]);
 
-    $this->bib_melun = Class_Bib::getLoader()
-      ->newInstanceWithId(12)
-      ->setLibelle('Melun');
+    $this->bib_melun = $this->fixture('Class_Bib',
+                                      ['id' => 12,
+                                       'libelle' => 'Melun']);
 
+    $this->onLoaderOfModel('Class_Profil')
+         ->whenCalled('findAllBy')
+         ->answers([]);
 
-    Storm_Test_ObjectWrapper
-      ::onLoaderOfModel('Class_Profil')
-      ->whenCalled('findAllBy')
-      ->answers(array());
-
-    Class_Profil::setFileWriter(Storm_Test_ObjectWrapper::mock()->whenCalled('fileExists')->answers(true));
+    Class_Profil::setFileWriter($this->mock()->whenCalled('fileExists')->answers(true));
   }
 
 
   /** @test */
   public function profilUrlShouldReturnBokehDotOrg() {
-    $this->assertEquals('http://bokeh.org/index/index?id_profil=7', Class_Profil::find(7)->getUrl());
+    $this->assertEquals('http://bokeh.org/index/index?id_profil=7',
+                        Class_Profil::find(7)->getUrl());
   }
 
 
   /** @test */
   public function getCfgMenuAsArrayShouldReturnDefaultMenus() {
-    $this->assertEquals(array(
-                              'H' => array(
-                                           "libelle" => "Menu horizontal",
-                                           "picto" => "vide.gif",
-                                           'menus' => array()),
-                              'V' => array(
-                                           "libelle" => "Menu vertical",
-                                           "picto" => "vide.gif",
-                                           'menus' => array())),
+    $this->assertEquals(['H' => ["libelle" => "Menu horizontal",
+                                 "picto" => "vide.gif",
+                                 'menus' => []],
+                         'V' => ["libelle" => "Menu vertical",
+                                 "picto" => "vide.gif",
+                                 'menus' => []]],
                         $this->profil_astro->getCfgMenusAsArray());
   }
 
 
   /** @test */
   public function shouldAddMenuHorizontalIfNotExists() {
-    $this->profil_astro->setCfgMenus( array(
-                                            'V' => array(
-                                                         "libelle" => "Les news",
-                                                         "picto" => "home.gif",
-                                                         "menus" => array()),
-
-                                            '4' => array(
-                                                         "libelle" => "Mon menu",
-                                                         "picto" => "home.png",
-                                                         "menus" => array())));
-
-    $this->assertEquals(array(
-                              'H' => array(
-                                           "libelle" => "Menu horizontal",
-                                           "picto" => "vide.gif",
-                                           "menus" => array()),
-                              'V' => array(
-                                           "libelle" => "Les news",
-                                           "picto" => "home.gif",
-                                           "menus" => array()),
-
-                              '4' => array(
-                                           "libelle" => "Mon menu",
-                                           "picto" => "home.png",
-                                           "menus" => array())),
+    $this->profil_astro
+      ->setCfgMenus(['V' => ["libelle" => "Les news",
+                             "picto" => "home.gif",
+                             "menus" => []],
+                     '4' => ["libelle" => "Mon menu",
+                             "picto" => "home.png",
+                             "menus" => []]]);
+
+    $this->assertEquals(['H' => ["libelle" => "Menu horizontal",
+                                 "picto" => "vide.gif",
+                                 "menus" => []],
+                         'V' => ["libelle" => "Les news",
+                                 "picto" => "home.gif",
+                                 "menus" => []],
+                         '4' => ["libelle" => "Mon menu",
+                                 "picto" => "home.png",
+                                 "menus" => []]],
                         $this->profil_astro->getCfgMenusAsArray());
   }
 
@@ -165,7 +157,8 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
 
   /** @test */
   public function astroHeaderCssShouldBeJeunesse() {
-    $this->assertEquals('afi-opac3/userfiles/jeunesse.css', $this->profil_astro->getHeaderCss());
+    $this->assertEquals('afi-opac3/userfiles/jeunesse.css',
+                        $this->profil_astro->getHeaderCss());
   }
 
 
@@ -232,7 +225,7 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
   /** @test */
   public function toArrayShouldContainsTitreSiteMediathequeAstrolabe() {
     $attributes = $this->profil_astro->toArray();
-    $this->assertEquals("", $attributes['titre_site']);
+    $this->assertEquals('', $attributes['titre_site']);
   }
 
 
@@ -280,7 +273,7 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
 
   /** @test */
   public function updateAttributesWithBoiteLoginTrueShouldAddIt() {
-    $this->profil_astro->updateAttributes(array('boite_login_in_banniere' => true));
+    $this->profil_astro->updateAttributes(['boite_login_in_banniere' => true]);
     $this->assertTrue($this->profil_astro->getBoiteLoginInBanniere());
   }
 
@@ -293,19 +286,14 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
 
   /** @test */
   public function getSubProfilsShouldReturnEmptyArray() {
-    $this->assertEquals(array(),
-                        $this->profil_astro->getSubProfils());
-    return  Class_Profil::getLoader();
-
+    $this->assertEquals([], $this->profil_astro->getSubProfils());
   }
 
 
-  /**
-   * @depends getSubProfilsShouldReturnEmptyArray
-   * @test
-   */
-  public function profilLoaderShouldHaveFindAllByCalledWithParentIdOfAstrolabe($loader) {
-    $param = $loader->getFirstAttributeForLastCallOn('findAllBy');
+  /** @test */
+  public function profilLoaderShouldHaveFindAllByCalledWithParentIdOfAstrolabe() {
+    $this->profil_astro->getSubProfils();
+    $param = Class_Profil::getFirstAttributeForLastCallOn('findAllBy');
     $this->assertEquals('parent_profil', $param['role']);
 
     $this->assertEquals($this->profil_astro->toArray(),
@@ -323,7 +311,8 @@ class ProfilJeunesseAstrolabeTest extends ModelTestCase {
 
 
 
-class PageBdAstrolabeTest extends Storm_Test_ModelTestCase {
+class PageBdAstrolabeTest extends ModelTestCase {
+  protected $_storm_default_to_volatile = true;
   protected $page_bd;
   protected $profil_astro;
   protected $bib_melun;
@@ -340,38 +329,33 @@ class PageBdAstrolabeTest extends Storm_Test_ModelTestCase {
                                           'preferences' => ['titre'=>'Connection']]],
                     'options' => []];
 
-    $this->profil_astro = Class_Profil::getLoader()
-      ->newInstanceWithId(7)
-      ->setIdSite(12)
-      ->setLibelle("Jeunesse")
-      ->setSkin('astrolabe')
-      ->setCfgMenus(array())
-      ->setHeaderCss('afi-opac3/userfiles/jeunesse.css')
-      ->setCfgAccueil($cfg_accueil);
-
-    $this->bib_melun = Class_Bib::getLoader()
-      ->newInstanceWithId(12)
-      ->setLibelle('Melun');
-
-    Storm_Test_ObjectWrapper
-      ::onLoaderOfModel('Class_Profil')
-      ->whenCalled('findAllBy')
-      ->answers(array());
-
-    Class_Profil::setFileWriter(
-      Storm_Test_ObjectWrapper::mock()
-      ->whenCalled('fileExists')
-      ->answers(true));
-
-    $this->page_bd = Class_Profil::getLoader()
-      ->newInstanceWithId(6,
-                          ['parent_id' => $this->profil_astro->getId(),
-                           'id_site' => 12,
-                           'libelle' => 'Bdd',
-                           'cfg_menus' => [],
-                           'cfg_accueil' => [],
-                           'page_css' => 'afi-opac3/userfiles/bd_jeunesse.css']);
-
+    $this->profil_astro = $this->fixture('Class_Profil',
+                                         ['id' => 7,
+                                          'id_site' => 12,
+                                          'libelle' => 'Jeunesse',
+                                          'skin' => 'astrolabe',
+                                          'cfg_menus' => [],
+                                          'header_css' => 'afi-opac3/userfiles/jeunesse.css',
+                                          'cfg_accueil' => $cfg_accueil]);
+
+    $this->bib_melun = $this->fixture('Class_Bib',
+                                      ['id' => 12,
+                                       'libelle' => 'Melun']);
+
+    $this->onLoaderOfModel('Class_Profil')
+         ->whenCalled('findAllBy')
+         ->answers([]);
+
+    Class_Profil::setFileWriter($this->mock()->whenCalled('fileExists')->answers(true));
+
+    $this->page_bd = $this->fixture('Class_Profil',
+                                    ['id' => 6,
+                                     'parent_id' => $this->profil_astro->getId(),
+                                     'id_site' => 12,
+                                     'libelle' => 'Bdd',
+                                     'cfg_menus' => [],
+                                     'cfg_accueil' => [],
+                                     'page_css' => 'afi-opac3/userfiles/bd_jeunesse.css',]);
     $this->profil_astro->setSubProfils([$this->page_bd]);
   }
 
@@ -1601,8 +1585,4 @@ class ProfilIsPortalTest extends Storm_Test_ModelTestCase {
   public function profilWithIdSevenStringShouldNotBePortal() {
     $this->assertFalse($this->fixture('Class_Profil', ['id' => '7'])->isPortail());
   }
-
-}
-
-
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/library/Class/WebService/SIGB/VSmartFixtures.php b/tests/library/Class/WebService/SIGB/VSmartFixtures.php
index 5dce65080cb2eddc909fb42b9375baf294bfb790..81f395920a77246aadbcc306acfb1cd14d00df66 100644
--- a/tests/library/Class/WebService/SIGB/VSmartFixtures.php
+++ b/tests/library/Class/WebService/SIGB/VSmartFixtures.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
  */
 
 class VSmartFixtures {
@@ -178,7 +178,20 @@ class VSmartFixtures {
                 </Items>
               </Reservation>
             </Reservations>
-            <StackRequests />
+            <StackRequests>
+              <StackRequest>
+                <RequestNumber>DC00001093</RequestNumber>
+                <Status>Active</Status>
+                <RequestDateTime>26/11/2015 12:14:27</RequestDateTime>
+                <ExpiryDateTime>24/02/2016 23:59:00</ExpiryDateTime>
+                <Delivery>Liv</Delivery>
+                <HeldItem>RES_MCP271095</HeldItem>
+                <RecordId>1.179920</RecordId>
+                <Title>A Moulins...le marché cou</Title>
+                <MaterialType>LDA</MaterialType>
+                <MaterialType>LDA</MaterialType>
+              </StackRequest>
+            </StackRequests>
         </Borrower>
     </VubisSmart>';
   }
diff --git a/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php b/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php
index 90fc393dc5fd3e70b19c8d89848fc8606d57fb44..74e5040a70ab832ffa7165aba4cb6152720c095c 100644
--- a/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php
+++ b/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php
@@ -18,7 +18,6 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-require_once 'library/ZendAfi/View/Helper/ViewHelperTestCase.php';
 
 abstract class CalendarViewHelperTestCase extends ViewHelperTestCase {
   public function setUp() {
@@ -1128,4 +1127,79 @@ class CalendarHelperWithFiltersTest extends CalendarHelperDisplayModeTestCase {
   }
 }
 
-?>
\ No newline at end of file
+
+
+class CalendarHelperWithPreviousMonthEventsTest extends ViewHelperTestCase {
+
+  protected
+    $_storm_default_to_volatile = true,
+    $_html;
+
+  public function setUp() {
+    parent::setUp();
+
+    $time_source = new TimeSourceForTest('2015-11-16 09:00:00');
+    ZendAfi_View_Helper_Calendar_Table::setTimeSource($time_source);
+    ZendAfi_View_Helper_CalendarContent::setTimeSource($time_source);
+    Class_Calendar::setTimeSource($time_source);
+
+    $bokeh = $this->fixture('Class_Article',
+                            [ 'id' => 8,
+                             'titre' => 'Bokeh en prod !',
+                             'events_debut' => '2015-10-29',
+                             'events_fin' => '2015-11-06',
+                             'categorie' => '',
+                             'contenu' => 'toto']);
+
+    $nanook = $this->fixture('Class_Article',
+                             [ 'id' => 9,
+                              'titre' => 'Nanook en prod !',
+                              'events_debut' => '2015-11-21',
+                              'events_fin' => '2016-01-01',
+                              'categorie' => '',
+                              'contenu' => 'toto']);
+
+    $this->onLoaderOfModel('Class_Article')
+         ->whenCalled('getArticlesByPreferences')
+         ->answers([$bokeh, $nanook]);
+
+    $params = ['type_module' => 'CALENDAR',
+               'division' => 2,
+               'preferences' => ['titre' => 'Agenda',
+                                 'rss_avis' => false,
+                                 'nb_events' => 3,
+                                 'rss_avis' => 0,
+                                 'display_calendar' => 1,
+                                 'display_event_info' => 'bib',
+                                 'display_mode' => 'Summary',
+                                 'display_order' => 'EventDebut',
+                                 'display_cat_select' => '',
+                                 'id_categorie' => '',
+                                 'mode-affichage' => 'diaporama_navigation',
+                                 'op_navigation_window_width' => '350',
+                                 'op_navigation_window_height' => '250',
+                                 'op_navigation_mode' => 'next_previous',
+                                 'op_navigation_unit' => 'px']];
+
+    $helper = new ZendAfi_View_Helper_Accueil_Calendar(12, $params);
+    $helper->setView(new ZendAfi_Controller_Action_Helper_View());
+    $this->_html = $helper->getBoite();
+  }
+
+
+  public function tearDown() {
+    ZendAfi_View_Helper_Calendar_Table::setTimeSource(null);
+    ZendAfi_View_Helper_CalendarContent::setTimeSource(null);
+    Class_Calendar::setTimeSource(null);
+
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldDisplayOtherMonthLink() {
+    $this->assertXPathContentContains($this->_html,
+                                      '//a[contains(@href, "2015-10-29")][contains(@class, "calendar_other_month")]',
+                                      '29');
+  }
+}
\ No newline at end of file
diff --git a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
index 240f92d87038d083b45697b3089e1750aaafb992..99b368030ee5fc6d7fa239971934619c21958f19 100644
--- a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
+++ b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
@@ -103,6 +103,47 @@ class TagArticleEventTest extends ViewHelperTestCase {
     $this->assertTagContains('lundi 05 septembre 2011');
   }
 
+
+  /** @test */
+  public function withMondayPickShouldAnswersTousLesLundi() {
+    $this->article
+      ->setEventsDebut('2011-09-05 08:00')
+      ->setEventsFin('2011-10-05 10:00')
+      ->setPickDay('1');
+    $this->assertTagContains('Les lundis du 5 septembre au 5 octobre 2011');
+  }
+
+
+  /** @test */
+  public function withSaturdayPickShouldAnswersTousLesSamedi() {
+    $this->article
+      ->setEventsDebut('2011-09-05 08:00')
+      ->setEventsFin('2011-10-05 10:00')
+      ->setPickDay('6');
+    $this->assertTagContains('Les samedis du 5 septembre au 5 octobre 2011');
+  }
+
+
+  /** @test */
+  public function withMondayAndSaturdayPickShouldAnswersTousLesLundiEtTousLesSamedi() {
+    $this->article
+      ->setEventsDebut('2011-09-05 08:00')
+      ->setEventsFin('2011-10-05 10:00')
+      ->setPickDay('1,6');
+    $this->assertTagContains('Les lundis et samedis du 5 septembre au 5 octobre 2011');
+  }
+
+
+  /** @test */
+  public function withAllDaysPickShouldAnswersTousLesJours() {
+    $this->article
+      ->setEventsDebut('2011-09-05 08:00')
+      ->setEventsFin('2011-10-05 10:00')
+      ->setPickDay('0,1,2,3,4,5,6');
+    $this->assertTagContains('Du lundi 05 septembre au mercredi 05 octobre 2011');
+  }
+
+
   protected function assertTagContains($expected) {
     $this->assertEquals(sprintf('<span class="calendar_event_date">%s</span>', $expected),
                         $this->_helper->tagArticleEvent($this->article));