diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 83fbc865813f488003627fb3e2986f80ff1902d8..878aa6cfd056f1903b54b418533891091081a279 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,3 +27,16 @@ test:php7_latest:
     - tags
   tags:
     - docker
+
+
+test:php74_latest:
+  image: localhost:5000/bokeh_php74
+  services:
+    - localhost:5000/bokeh_mysql5
+  script:
+    - bash scripts/ci_data_preparation.sh $MYSQL_DATABASE $MYSQL_HOST $MYSQL_ROOT_PASSWORD
+    - bash build.sh $MYSQL_DATABASE root $MYSQL_ROOT_PASSWORD $MYSQL_HOST
+  except:
+    - tags
+  tags:
+    - docker
diff --git a/INSTALL.en.md b/INSTALL.en.md
index 6226276f6fb2b6b9a24850ab2b71401d568917ca..49c5909392e89ba30085b7cd7668501e82fa26a7 100644
--- a/INSTALL.en.md
+++ b/INSTALL.en.md
@@ -3,22 +3,40 @@ This procedure is meant for Ubuntu et Debian GNU/Linux
 
 # Necessary packages :
 
-##  Ubuntu Trusty
+## PHP 7.4
+
+### Debian Buster
+```
+apt-get install  libapr1-dev libssl-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev libmcrypt-dev imagemagick libmagickwand-dev libfontconfig bzip2 libmemcached-dev zlib1g-dev yaz default-mysql-client zip libzip-dev libxslt-dev 
+```
+
+From PECL:
+
+```
+pecl install imagick-3.4.3RC1
+pecl install memcached
+pecl install mcrypt-1.0.3
+```
+
+
+## PHP 5.6
+
+###  Ubuntu Trusty
 ```
 apt-get install python-software-properties php5 php5-gd php5-imagick php5-xdebug php-pear php5-mysqlnd php5-xhprof graphviz apache2 mysql-server libapache2-mod-php5 git php5-mcrypt php5-curl yaz
 ```
 
-## Debian Wheezy
+### Debian Wheezy
 ```
 apt-get install php5 php5-gd php5-imagick php5-xdebug php-pear php5-mysqlnd graphviz apache2 mysql-server libapache2-mod-php5 git php5-mcrypt php5-curl yaz
 ```		
 
-## ArchLinux
+### ArchLinux
 ```
 yaourt -S php php-gd php-imagick xdebug php-pear apache mariadb php-apache php-xhprof graphviz git
 ```
 
-## CentOS
+### CentOS
 ```
 rpm -Uvh http://repo.webtatic.com/yum/el6/latest.rpm
 yum install php54w php54w-gd php54w-pear php54w-mysql php54w-pecl-xdebug php54w-xml php54w-soap php54w-mbstring
@@ -31,7 +49,7 @@ You need to build Imagick extension from scratch
 
 ```
   cd /usr/local/bin
-  wget https://phar.phpunit.de/phpunit.phar
+  wget https://phar.phpunit.de/phpunit-5.7.27.phar
   mv phpunit.phar phpunit
 ```
 
diff --git a/INSTALL.fr.md b/INSTALL.fr.md
index 11780dec7e02bea04f25427a9eea70658c9bebe2..0dd5acba70978060648295b6a0975f02f8d03f18 100644
--- a/INSTALL.fr.md
+++ b/INSTALL.fr.md
@@ -3,6 +3,24 @@ Sauf indiqué, cette procédure prends en compte par défaut les distributions G
 
 # Paquets nécessaires:
 
+## PHP 7.4
+
+### Debian Buster
+```
+apt-get install  libapr1-dev libssl-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev libmcrypt-dev imagemagick libmagickwand-dev libfontconfig bzip2 libmemcached-dev zlib1g-dev yaz default-mysql-client zip libzip-dev libxslt-dev 
+```
+
+Depuis PECL:
+
+```
+pecl install imagick-3.4.3RC1
+pecl install memcached
+pecl install mcrypt-1.0.3
+```
+
+
+## PHP 5.6
+
 ##  Ubuntu Trusty
 ```
 apt-get install python-software-properties php5 php5-gd php5-imagick php5-xdebug php-pear php5-mysqlnd php5-xhprof graphviz apache2 mysql-server libapache2-mod-php5 git php5-mcrypt php5-curl yaz
@@ -32,7 +50,7 @@ Rechercher le numéro de la dernière version *phpunit-x.x.x.phar* sur [https://
 
 ```
   cd /usr/local/bin
-  wget https://phar.phpunit.de/phpunit-x.x.x.phar
+  wget https://phar.phpunit.de/phpunit-5.7.27.phar
   mv phpunit-x.x.x.phar phpunit
   chmod +x phpunit
 ```
diff --git a/application/modules/admin/controllers/OaiController.php b/application/modules/admin/controllers/OaiController.php
index 135f2708d46e623cc01ddcee35127ca415b5bc9d..2a378fc991729c2837247715a784079c2eb4ef60 100644
--- a/application/modules/admin/controllers/OaiController.php
+++ b/application/modules/admin/controllers/OaiController.php
@@ -39,6 +39,9 @@ class Admin_OaiController extends ZendAfi_Controller_Action {
 
 
   public function browseAction() {
+    $oai_sets = [];
+    $oai_set = '';
+
     $entrepot_id = $this->_getparam("id");
     if ($entrepot_id) {
       $entrepot = Class_EntrepotOAI::getLoader()->find($entrepot_id);
@@ -55,10 +58,10 @@ class Admin_OaiController extends ZendAfi_Controller_Action {
     }
 
     $this->view->subview = $this->view->partial('oai/browse.phtml',
-                                                array('titre' => $this->_('Parcours de l\'entrepôt "%s"', $entrepot->getLibelle()),
-                                                      'entrepot_id' => $entrepot_id,
-                                                      'oai_sets' => $oai_sets,
-                                                      'oai_set' => $oai_set));
+                                                ['titre' => $this->_('Parcours de l\'entrepôt "%s"', $entrepot->getLibelle()),
+                                                 'entrepot_id' => $entrepot_id,
+                                                 'oai_sets' => $oai_sets,
+                                                 'oai_set' => $oai_set]);
     $this->_forward('index');
   }
 
diff --git a/application/modules/opac/views/scripts/blog/viewcritiques.phtml b/application/modules/opac/views/scripts/blog/viewcritiques.phtml
index c692cee123a581df6776094ca6cc61d9e3ea18f8..ba6c166ea3c052c2b31b95eeefc679c8bfd589f6 100644
--- a/application/modules/opac/views/scripts/blog/viewcritiques.phtml
+++ b/application/modules/opac/views/scripts/blog/viewcritiques.phtml
@@ -14,9 +14,12 @@ if (is_array($this->liste_avis)) {
 }
 
 
+$nb_display = ($this->config && isset($this->config['nb_display']))
+                                     ? $this->config['nb_display']
+                                     : 10;
 echo BR . $this->tag('div',
                      $this->pager($this->total,
-                                  $this->config['nb_display'],
+                                  $nb_display,
                                   $this->page,
                                   $this->params_url),
                      ['align' => 'center',
diff --git a/build.sh b/build.sh
index c9c40756f8cf9f8486422c86a15ccd1a0760bbf2..1e400a4cca0780a2049c9c1760809d5a2cb7ce93 100755
--- a/build.sh
+++ b/build.sh
@@ -24,6 +24,8 @@ sed -i "s/integration_pwd=root/integration_pwd=$DBPASS/g" config.php
 sed -i "s/integration_base=opac3/integration_base=$DBNAME/g" config.php
 cd ..
 
+export OPENSSL_CONF=/etc/ssl/
+
 phpunit -c tests/phpunit.xml --list-suites && phpunit -c tests/phpunit.xml --exclude-group no-ci \
     && phpunit -c tests/phpunit_db.xml --list-suites && phpunit -c tests/phpunit_db.xml --exclude-group no-ci \
     && phpunit -c tests/phpunit_js.xml --list-suites && phpunit -c tests/phpunit_js.xml --exclude-group no-ci \
diff --git a/cosmogramme/php/classes/classe_unimarc.php b/cosmogramme/php/classes/classe_unimarc.php
index 4d8e025357ccbb9e331490af43179d8d709a2bfd..0d4f294a08af690d867c52303982ba7a842023c4 100644
--- a/cosmogramme/php/classes/classe_unimarc.php
+++ b/cosmogramme/php/classes/classe_unimarc.php
@@ -1238,8 +1238,8 @@ class notice_unimarc extends iso2709_record {
 
   private function filtreTitre($valeur) {
     if (!isset(static::$CHAR_NSB)) {
-      static::$CHAR_NSB = chr(0xc2)+chr(0x88); // NON SORTING BLOCK BEGIN
-      static::$CHAR_NSE = chr(0xc2)+chr(0x89); // NON SORTING BLOCK END
+      static::$CHAR_NSB = chr(0xc2) . chr(0x88); // NON SORTING BLOCK BEGIN
+      static::$CHAR_NSE = chr(0xc2) . chr(0x89); // NON SORTING BLOCK END
     }
 
     $titre = trim($valeur);
diff --git a/cosmogramme/storm_init.php b/cosmogramme/storm_init.php
index 8679fdebaa09aca2f893504cc07380dc537915af..b9342dda34f420610fe700fe34576d47330202de 100644
--- a/cosmogramme/storm_init.php
+++ b/cosmogramme/storm_init.php
@@ -47,6 +47,7 @@ require_once 'Zend/Loader.php';
 Zend_Loader::registerAutoload();
 require_once "startup.php";
 
+defineConstant('BASE_URL',  Class_Url::baseUrl());
 
 $cfg_file = (new CosmoPaths())->getBokehConfigPath();
 $bokeh = new Bokeh_Engine();
diff --git a/library/Class/Bib/DriveOpening.php b/library/Class/Bib/DriveOpening.php
index 723c6d6435720b9e5b2d2e2472aa682267a96226..6148fe01964445f8cd866756d210d74714ed3559 100644
--- a/library/Class/Bib/DriveOpening.php
+++ b/library/Class/Bib/DriveOpening.php
@@ -80,7 +80,7 @@ class Class_Bib_DriveOpening {
                });
 
     if ($period)
-      $collection->append(new DateTime($period->format('c')));
+      $collection->append(new DateTime($period->format('Y-m-d  H:i:s')));
   }
 
 
diff --git a/library/Class/DriveCheckout/Plan.php b/library/Class/DriveCheckout/Plan.php
index 11f8d4eb95c6bf2e72e7edc19698f4d68130516d..5e0908b2144a06e4ad632a0d9e47a55164565369 100644
--- a/library/Class/DriveCheckout/Plan.php
+++ b/library/Class/DriveCheckout/Plan.php
@@ -102,9 +102,11 @@ class Class_DriveCheckout_Plan {
     if ($this->_date)
       return $this->_date;
 
-    return (false !== $date = DateTime::createFromFormat('Y-m-d',
-                                                         $this->_getParam(static::DATE_PARAM)))
-      && $this->_getOpenings()->includes($date->modify('midnight'))
+    $date = DateTime::createFromFormat('Y-m-d',
+                                       $this->_getParam(static::DATE_PARAM))->modify('midnight');
+
+    return (false !== $date)
+      && in_array($date, $this->_getOpenings()->getArrayCopy())
       ? $this->_date = $date
       : null;
   }
@@ -141,7 +143,7 @@ class Class_DriveCheckout_Plan {
                                                $date->format('Y-m-d'),
                                                $this->_getParam(static::TIME_PARAM)));
 
-    return (false !== $time) && $this->_getTimes()->includes($time)
+    return (false !== $time) && in_array($time, $this->_getTimes()->getArrayCopy())
       ? $this->_time = $time
       : null;
   }
diff --git a/library/Class/Notice/DoubleFinder.php b/library/Class/Notice/DoubleFinder.php
index 352bdb17b7bc643db0b9549b5994c908e3e1d300..d5b71994444f64381a7ec7c66a186ebc41b81dda 100644
--- a/library/Class/Notice/DoubleFinder.php
+++ b/library/Class/Notice/DoubleFinder.php
@@ -195,7 +195,7 @@ abstract class Class_Notice_DoubleFinder_Strategy {
 
   protected function _hasItems() {
     return ($statuses = $this->_data->getstatut_exemplaires())
-      || $statuses['nb_ex'] > 0;
+      && $statuses['nb_ex'] > 0;
   }
 }
 
diff --git a/library/Class/Systeme/PergameService.php b/library/Class/Systeme/PergameService.php
index 7929f6a2f7f035701f96dc748033ae100c4173ef..bfcefe5aba04064f05db5bde5439860dd2143db7 100644
--- a/library/Class/Systeme/PergameService.php
+++ b/library/Class/Systeme/PergameService.php
@@ -241,6 +241,7 @@ class Class_Systeme_PergameService {
     $heure = $time_source->dateFormat('H');
 
     $this->ecrireTransaction(6, [$id_abon, $ordre_abon, $support, $id_origine, $date, $id_bib, $heure]);
+    return ['erreur' => '', 'statut' => true];
   }
 
 
diff --git a/library/Class/WebService/Lastfm.php b/library/Class/WebService/Lastfm.php
index fc38a521f02a41ec37a55115298a450821916d86..a7983b5d037acec6d8656f0864513c904b440e34 100644
--- a/library/Class/WebService/Lastfm.php
+++ b/library/Class/WebService/Lastfm.php
@@ -129,7 +129,7 @@ class Class_WebService_Lastfm  extends Class_WebService_Abstract {
 
   public function getMorceaux($titre, $auteur) {
     if (!$album = $this->getAlbum($titre,$auteur))
-      return false;
+      return ['morceaux' => [], 'nb_resultats' => 0];
 
     $data = $this->httpGet($album['url']);
 
diff --git a/library/ZendAfi/Controller/Plugin/Manager/Article.php b/library/ZendAfi/Controller/Plugin/Manager/Article.php
index f5340f6f93c2c3f2b7277dd2530c7da3f1ec3ed2..b562fa28a9b3870e847d8af62f5f02b71ea9f095 100644
--- a/library/ZendAfi/Controller/Plugin/Manager/Article.php
+++ b/library/ZendAfi/Controller/Plugin/Manager/Article.php
@@ -436,13 +436,16 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi
 
 
   public function getActions($model) {
-    if('Class_Article' == get_class($model))
+    if (!$model)
+      return [];
+
+    if ('Class_Article' == get_class($model))
       return $this->_articleActions($model);
 
-    if('Class_ArticleCategorie' == get_class($model))
+    if ('Class_ArticleCategorie' == get_class($model))
       return $this->_articleCategoryActions($model);
 
-    if('Class_Bib' == get_class($model))
+    if ('Class_Bib' == get_class($model))
       return $this->_bibActions($model);
 
     return [];
diff --git a/library/ZendAfi/Controller/Plugin/MultiSelection/Article.php b/library/ZendAfi/Controller/Plugin/MultiSelection/Article.php
index c7a2fb706c3f88bc9db01aa148284c1be800ed7c..103f0e926bc977445332a4ac428fcbf9f16c71a2 100644
--- a/library/ZendAfi/Controller/Plugin/MultiSelection/Article.php
+++ b/library/ZendAfi/Controller/Plugin/MultiSelection/Article.php
@@ -28,11 +28,14 @@ class ZendAfi_Controller_Plugin_MultiSelection_Article extends ZendAfi_Controlle
 
 
   public function getActions($model) {
-    if('Class_Article' == get_class($model))
+    if (!$model)
+      return [];
+
+    if ('Class_Article' == get_class($model))
       return (new ZendAfi_Controller_Plugin_MultiSelection_LeafActions($this->_multi_selection))
         ->getActions();
 
-    if('Class_ArticleCategorie' == get_class($model))
+    if ('Class_ArticleCategorie' == get_class($model))
       return (new ZendAfi_Controller_Plugin_MultiSelection_NodeActions($this->_multi_selection))
         ->getActions();
 
diff --git a/library/ZendAfi/Controller/Plugin/Versionning/Article.php b/library/ZendAfi/Controller/Plugin/Versionning/Article.php
index 8a1b0914567f49084a0b1fb9c33622f33914d79c..315d9165c92819b139d71f976339d658bc89d249 100644
--- a/library/ZendAfi/Controller/Plugin/Versionning/Article.php
+++ b/library/ZendAfi/Controller/Plugin/Versionning/Article.php
@@ -24,6 +24,9 @@ class ZendAfi_Controller_Plugin_Versionning_Article
   extends ZendAfi_Controller_Plugin_Versionning_Abstract {
 
   public function getActions($model) {
+    if (!$model)
+      return [];
+
     if ('Class_Article' != get_class($model))
       return [];
 
diff --git a/library/ZendAfi/View/Helper/Abonne/Operation.php b/library/ZendAfi/View/Helper/Abonne/Operation.php
index 1a22ddc75699abd3de8316998c72e4468f2ac641..2b6d7e33fa5951a514e811e87d7266ac6546efb8 100644
--- a/library/ZendAfi/View/Helper/Abonne/Operation.php
+++ b/library/ZendAfi/View/Helper/Abonne/Operation.php
@@ -23,7 +23,7 @@
 class ZendAfi_View_Helper_Abonne_Operation extends ZendAfi_View_Helper_BaseHelper {
   const STORM_LIMIT = 100;
 
-  protected $_operations;
+  protected $_operations = [];
 
   protected function _renderAuthor($operation, $attribs = []) {
     if($this->_isStormLimited())
diff --git a/library/ZendAfi/View/Helper/Admin/Rss.php b/library/ZendAfi/View/Helper/Admin/Rss.php
index d9ef024b5869860b621bf766a42b871707aab706..0b860ba5c325bd332949ba42494b565fe6909e9d 100644
--- a/library/ZendAfi/View/Helper/Admin/Rss.php
+++ b/library/ZendAfi/View/Helper/Admin/Rss.php
@@ -26,59 +26,58 @@ class ZendAfi_View_Helper_Admin_Rss extends ZendAfi_View_Helper_BaseHelper {
     $niveau=0; $id=0;
     $html= '<ul class="treeMenu">';
 
-    if(count($rss->arbre_array) > 0){
-      foreach($rss->arbre_array as $menu)
-        {
-          if ($niveau != $menu["NIVEAU"])
-            {
-              $id++;
-              if(!$menu["ID_CAT_MERE"]) $test = $menu["ID_CAT"]; else $test = $menu["ID_CAT_MERE"];
-              if ($niveau < $menu["NIVEAU"] ) $html.='</li><li class="sousTreeMenu" id="sm_'.$test.'" style="display:none;"><ul class="sousTreeMenu">';
-              else $html.= str_repeat('</ul></li>',($niveau - $menu["NIVEAU"] ));
-              $niveau = $menu["NIVEAU"];
-            }
-          $cat_avec_rss = $rss->getAllFluxByIdCat($menu["ID_CAT"]);
-          $cat_avec_subcat = $rss->getAllSousCategorie($menu["ID_CAT"]);
+    if (empty($rss->arbre_array) || (count($rss->arbre_array) === 0))
+      return '';
 
-          if(count($cat_avec_rss) > 0 || count($cat_avec_subcat) > 0) {
-            $ico_del='<a href="#" onclick="alert(\'Suppression non autorisée : cette catégorie contient des fils Rss.\')">' . $this->view->boutonIco('type=del') . '</a>';
+    foreach($rss->arbre_array as $menu)
+      {
+        if ($niveau != $menu["NIVEAU"])
+          {
+            $id++;
+            if(!$menu["ID_CAT_MERE"]) $test = $menu["ID_CAT"]; else $test = $menu["ID_CAT_MERE"];
+            if ($niveau < $menu["NIVEAU"] ) $html.='</li><li class="sousTreeMenu" id="sm_'.$test.'" style="display:none;"><ul class="sousTreeMenu">';
+            else $html.= str_repeat('</ul></li>',($niveau - $menu["NIVEAU"] ));
+            $niveau = $menu["NIVEAU"];
           }
-          else {
-            $ico_del='<a href="'.BASE_URL.'/admin/rss/catdel/id/'.$menu["ID_CAT"].'" onclick="javascript:if(!confirm(\'Êtes vous sûr de vouloir supprimer catte catégorie ?\')) return false;">' . $this->view->boutonIco('type=del') . '</a>';}
+        $cat_avec_rss = $rss->getAllFluxByIdCat($menu["ID_CAT"]);
+        $cat_avec_subcat = $rss->getAllSousCategorie($menu["ID_CAT"]);
 
-          $ico_add_cat='<a href="'.BASE_URL.'/admin/rss/catadd/id/'.$menu["ID_CAT"].'">' . $this->view->boutonIco('picto=add_category', 'bulle=Ajouter une sous-catégorie') . '</a>';
-          $ico_add_news='<a href="'.BASE_URL.'/admin/rss/rssadd/id/'.$menu["ID_CAT"].'">' . $this->view->boutonICo('picto=add_page', 'bulle=Ajouter un fil Rss') . '</a>';
-          $ico_edit='<a href="'.BASE_URL.'/admin/rss/catedit/id/'.$menu["ID_CAT"].'">' . $this->view->boutonIco('type=edit') . '</a>';
+        if(count($cat_avec_rss) > 0 || count($cat_avec_subcat) > 0) {
+          $ico_del='<a href="#" onclick="alert(\'Suppression non autorisée : cette catégorie contient des fils Rss.\')">' . $this->view->boutonIco('type=del') . '</a>';
+        }
+        else {
+          $ico_del='<a href="'.BASE_URL.'/admin/rss/catdel/id/'.$menu["ID_CAT"].'" onclick="javascript:if(!confirm(\'Êtes vous sûr de vouloir supprimer catte catégorie ?\')) return false;">' . $this->view->boutonIco('type=del') . '</a>';}
+
+        $ico_add_cat='<a href="'.BASE_URL.'/admin/rss/catadd/id/'.$menu["ID_CAT"].'">' . $this->view->boutonIco('picto=add_category', 'bulle=Ajouter une sous-catégorie') . '</a>';
+        $ico_add_news='<a href="'.BASE_URL.'/admin/rss/rssadd/id/'.$menu["ID_CAT"].'">' . $this->view->boutonICo('picto=add_page', 'bulle=Ajouter un fil Rss') . '</a>';
+        $ico_edit='<a href="'.BASE_URL.'/admin/rss/catedit/id/'.$menu["ID_CAT"].'">' . $this->view->boutonIco('type=edit') . '</a>';
 
-          $ico_cat = Class_Admin_Skin::current()->renderActionIconOn('category', $this->view,
+        $ico_cat = Class_Admin_Skin::current()->renderActionIconOn('category', $this->view,
                                                                    ['alt' => 'Afficher les sous catégories',
                                                                     'title' => 'Afficher les sous catégories',
                                                                     'style' => 'width:19px; height:12px;',
                                                                     'class' => 'ico']);
 
-          $ico_news = $this->view->boutonIco('picto=liste', 'bulle=Afficher le flux');
-
-          if(strlen($menu["LIBELLE"]) >= 50){$libelle_cat = substr($menu["LIBELLE"],0,50).'...'; } else $libelle_cat =$menu["LIBELLE"] ;
+        $ico_news = $this->view->boutonIco('picto=liste', 'bulle=Afficher le flux');
 
-          $nb_items = count($cat_avec_rss) + count($cat_avec_subcat);
-          $html.= '<li style="width:100%; height:25px;padding-top:10px;border-bottom:1px solid #DEE4E8;" id="m_'.$menu["ID_CAT"].'"><div style="float:left;cursor:pointer;" onclick="show(\'sm_'.$menu["ID_CAT"].'\');">';
-
-          if (isset($menu["ID_RSS"])) {
-            $ico_edit_news='<a href="'.BASE_URL.'/admin/rss/rssedit/id/'.$menu["ID_RSS"].'">' . $this->view->boutonIco('type=edit') . '</a>';
-            $ico_del_news='<a href="'.BASE_URL.'/admin/rss/rssdel/id/'.$menu["ID_RSS"].'" onclick="javascript:if(!confirm(\'Êtes vous sûr de vouloir supprimer ce fil Rss ?\')) return false;">' . $this->view->boutonIco('type=del') . '</a>';
-            if(strlen($menu["TITRE"]) >= 50){$libelle_rss = substr($menu["TITRE"],0,50).'...'; } else $libelle_rss =$menu["TITRE"] ;
-            $html.='<a id="rss_a_'.$menu["ID_RSS"].'" href="#" onclick="requestRss(\''.$menu["ID_RSS"].'\');show(\'sm_'.$menu["ID_CAT"].'\');">'.$ico_news.'&nbsp;'.$libelle_rss. '</a></div> <div style="height:20px;" align="right"> '.$ico_edit_news.$ico_del_news.'</div>';
-          } else
-            $html.=$ico_cat.'&nbsp;'.$libelle_cat. ' ('.$nb_items.')</div> <div style="height:20px;" align="right">'.$ico_add_cat.$ico_add_news.$ico_edit.$ico_del.'</div>';
-          $html.='</li>';
-        }
+        if(strlen($menu["LIBELLE"]) >= 50){$libelle_cat = substr($menu["LIBELLE"],0,50).'...'; } else $libelle_cat =$menu["LIBELLE"] ;
 
-      $html.= "</li></ul>";
-      if($html=='<ul class="treeMenu"></li></ul>') $html="";
-      return ($html);
-    }
+        $nb_items = count($cat_avec_rss) + count($cat_avec_subcat);
+        $html.= '<li style="width:100%; height:25px;padding-top:10px;border-bottom:1px solid #DEE4E8;" id="m_'.$menu["ID_CAT"].'"><div style="float:left;cursor:pointer;" onclick="show(\'sm_'.$menu["ID_CAT"].'\');">';
 
+        if (isset($menu["ID_RSS"])) {
+          $ico_edit_news='<a href="'.BASE_URL.'/admin/rss/rssedit/id/'.$menu["ID_RSS"].'">' . $this->view->boutonIco('type=edit') . '</a>';
+          $ico_del_news='<a href="'.BASE_URL.'/admin/rss/rssdel/id/'.$menu["ID_RSS"].'" onclick="javascript:if(!confirm(\'Êtes vous sûr de vouloir supprimer ce fil Rss ?\')) return false;">' . $this->view->boutonIco('type=del') . '</a>';
+          if(strlen($menu["TITRE"]) >= 50){$libelle_rss = substr($menu["TITRE"],0,50).'...'; } else $libelle_rss =$menu["TITRE"] ;
+          $html.='<a id="rss_a_'.$menu["ID_RSS"].'" href="#" onclick="requestRss(\''.$menu["ID_RSS"].'\');show(\'sm_'.$menu["ID_CAT"].'\');">'.$ico_news.'&nbsp;'.$libelle_rss. '</a></div> <div style="height:20px;" align="right"> '.$ico_edit_news.$ico_del_news.'</div>';
+        } else
+          $html.=$ico_cat.'&nbsp;'.$libelle_cat. ' ('.$nb_items.')</div> <div style="height:20px;" align="right">'.$ico_add_cat.$ico_add_news.$ico_edit.$ico_del.'</div>';
+        $html.='</li>';
+      }
 
+    $html.= "</li></ul>";
+    if($html=='<ul class="treeMenu"></li></ul>') $html="";
+    return ($html);
   }
 }
 ?>
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/FonctionsAdmin.php b/library/ZendAfi/View/Helper/FonctionsAdmin.php
index c77a419dd1f1cdc0177c636ee718fcda01581337..3a239d8397f35449d8f43531d517e92265d65fab 100644
--- a/library/ZendAfi/View/Helper/FonctionsAdmin.php
+++ b/library/ZendAfi/View/Helper/FonctionsAdmin.php
@@ -136,6 +136,9 @@ class ZendAfi_View_Helper_FonctionsAdmin extends ZendAfi_View_Helper_BaseHelper
 
 
   protected function getPopupUrlParams()  {
+    if (!$this->view->_current_module)
+      return [];
+
     $controller = $this->view->_current_module['controller'];
     $action = $this->view->_current_module['action'];
     $action2 = $this->view->_current_module['action2'];
diff --git a/library/ZendAfi/View/Helper/RenderModelAction.php b/library/ZendAfi/View/Helper/RenderModelAction.php
index c57e4b6bb93bd2da75ad0fd19a6af94a04bec513..8b35b1094267d11a48dfc2205416af64eef16d9f 100644
--- a/library/ZendAfi/View/Helper/RenderModelAction.php
+++ b/library/ZendAfi/View/Helper/RenderModelAction.php
@@ -110,6 +110,9 @@ class ZendAfi_View_Helper_RenderModelAction extends ZendAfi_View_Helper_BaseHelp
 
 
   protected function _initTitle($model) {
+    if (!isset($this->_conf[self::LABEL]))
+      return '';
+
     return is_a($this->_conf[self::LABEL], 'Closure')
       ? $this->_conf[self::LABEL]($model)
       : $this->_conf[self::LABEL];
diff --git a/library/storm b/library/storm
index 17d31fdfc59be5319cb5201ac2280dbb9e7c2c5b..d9ffcc9739cf32b9e9143564cc3e14328eb6aa5e 160000
--- a/library/storm
+++ b/library/storm
@@ -1 +1 @@
-Subproject commit 17d31fdfc59be5319cb5201ac2280dbb9e7c2c5b
+Subproject commit d9ffcc9739cf32b9e9143564cc3e14328eb6aa5e
diff --git a/tests/application/modules/opac/controllers/BibNumeriqueControllerTest.php b/tests/application/modules/opac/controllers/BibNumeriqueControllerTest.php
index 694b677027856f814d4aac81a104037df47406f8..6f832fe9c3c411d633e140c92b2a3f915a014696 100644
--- a/tests/application/modules/opac/controllers/BibNumeriqueControllerTest.php
+++ b/tests/application/modules/opac/controllers/BibNumeriqueControllerTest.php
@@ -452,6 +452,8 @@ class BibNumeriqueControllerViewCategorieActionTest extends AbstractControllerTe
 
 
 class BibNumeriqueControllerViewCategorieActionLesEnluminuresTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
   public function setUp() {
     parent::setUp();
 
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
index da32ba324ec8a029cf5f30ca4a1b31ac7d8731dd..8bd0b9153469eede719ae60d4a3988078620a25a 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
@@ -241,6 +241,7 @@ class NoticeAjaxControllerNoticeSimilairesSouleymaneTest extends NoticeAjaxContr
 
 
 class NoticeAjaxControllerResNumeriquesTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
 
   public function setup() {
     parent::setup();
diff --git a/tests/application/modules/opac/controllers/OnSiteConsultationTest.php b/tests/application/modules/opac/controllers/OnSiteConsultationTest.php
index 975280cae7bd2700856b49c145a1df93d2beddde..e54b0835deff9040654d4ce357ae095f11ee66d0 100644
--- a/tests/application/modules/opac/controllers/OnSiteConsultationTest.php
+++ b/tests/application/modules/opac/controllers/OnSiteConsultationTest.php
@@ -240,7 +240,7 @@ class OnSiteConsultationPickupLocationPostTest extends OnSiteConsultationPickupL
     $this->_mock_service
       ->whenCalled('placeConsultationRequest')
       ->with(79, 'OC2', 'RES_Liv', '0000007')
-      ->answers(true);
+      ->answers(['erreur' => '']);
 
     $this->postDispatch('recherche/consultation-pickup-ajax/copy_id/799',
                         ['location' => 'RES_Liv']);
diff --git a/tests/application/modules/opac/controllers/RechercheControllerAtomTest.php b/tests/application/modules/opac/controllers/RechercheControllerAtomTest.php
index f6e1de88be9556f93c6e7c7d49a98afbe72504b9..240824072122ba9f28600a6008bae312a10906eb 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerAtomTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerAtomTest.php
@@ -26,7 +26,7 @@ abstract class RechercheControllerAtomTestCase extends AbstractControllerTestCas
 
   public function setUp() {
     parent::setUp();
-
+    Class_AdminVar::set('FEATURES_TRACKING_ENABLE', '0');
     $this->_prepareLoader($this->onLoaderOfModel('Class_Notice'));
     $mock_sql = $this->mock();
     $this->_prepareSql($mock_sql);
diff --git a/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php b/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
index 93766dea980766d37ce7f37d1ce7a9bc021beee0..3a232229c4fc75f4829c54109694274efd98cadd 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
@@ -183,7 +183,7 @@ class RechercheControllerReservationPickupAjaxActionTestWithChosenPickupDispatch
 
     $this->nanook = Storm_Test_ObjectWrapper::mock()
       ->whenCalled('isConnected')->answers(true)
-      ->whenCalled('reserverExemplaire')->answers(true);
+      ->whenCalled('reserverExemplaire')->answers(['erreur' => '', 'statut' => true]);
 
     Class_WebService_SIGB_Nanook::setService(['url_serveur' => 'http://bib.valensol.net',
                                               'id_bib' => 1,
@@ -240,7 +240,7 @@ class RechercheControllerReservationPickupAjaxActionTestWithPatronLibraryPickup
 
     $this->nanook = Storm_Test_ObjectWrapper::mock()
       ->whenCalled('isConnected')->answers(true)
-      ->whenCalled('reserverExemplaire')->answers(true)
+      ->whenCalled('reserverExemplaire')->answers(['erreur' => '', 'statut' => true])
       ->whenCalled('providesPickupLocations')->answers(false)
       ->whenCalled('getUserAnnexe')->answers('12');
 
@@ -292,7 +292,7 @@ class RechercheControllerReservationPickupAjaxActionTestWithItemLibraryPickup
     $this->nanook = Storm_Test_ObjectWrapper::mock()
       ->whenCalled('isConnected')->answers(true)
       ->whenCalled('providesPickupLocations')->answers(false)
-      ->whenCalled('reserverExemplaire')->answers(true);
+      ->whenCalled('reserverExemplaire')->answers(['statut' => true, 'erreur' => '']);
 
     Class_WebService_SIGB_Nanook::setService(['url_serveur' => 'http://bib.valensol.net',
                                               'id_bib' => 1,
diff --git a/tests/library/Class/BatchTest.php b/tests/library/Class/BatchTest.php
index 968813261b7b97711f20c4fca9f722fe784cf028..ad75d4484045dad1f3dbaa39d115c6e13d3d2648 100644
--- a/tests/library/Class/BatchTest.php
+++ b/tests/library/Class/BatchTest.php
@@ -180,7 +180,7 @@ class BatchBuildSitemapTest extends ModelTestCase {
 
     $this->handle = new stdClass;
     $this->_file_system = $this->mock()
-      ->whenCalled('fopen')->with(ROOT_PATH.'temp/sitemap.xml', 'w')->answers($this->handle)
+      ->whenCalled('fopen')->with(PATH_TEMP.'sitemap.xml', 'w')->answers($this->handle)
       ->whenCalled('fwrite')->answers(null)
       ->whenCalled('unlink')->answers(null);
 
diff --git a/tests/library/Class/Multimedia/DeviceTest.php b/tests/library/Class/Multimedia/DeviceTest.php
index 6cbeeb7e8505ccecf12a01547d36c4f7180237cc..b8e9f2fee4a3a1160506057e91cbac7b499b9a5d 100644
--- a/tests/library/Class/Multimedia/DeviceTest.php
+++ b/tests/library/Class/Multimedia/DeviceTest.php
@@ -55,7 +55,7 @@ abstract class Multimedia_DeviceCurrentHoldTestCase extends ModelTestCase {
                                     ['id' => 2,
                                      'group' => $this->_group]);
 
-    $time = strtotime('today');
+    $time = strtotime('2020-07-13 10:00:00');
     $this->_time_source = (new TimeSourceForTest())->setTime($time);
 
 
@@ -284,7 +284,7 @@ abstract class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndMaxSlotsAfterNex
       ->getBib()
       ->setOuvertures([$this->fixture('Class_Ouverture',
                                       ['id' => 5,
-                                       'jour_semaine' => date('w'),
+                                       'jour_semaine' => Class_Ouverture::LUNDI,
                                        'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA,
                                        'horaires' => ['08:00', '12:00', '14:00', '23:00']])])
       ->assertSave();
diff --git a/tests/library/Class/ProfilTest.php b/tests/library/Class/ProfilTest.php
index 1c22f0197c35cae98c0c05dd5ca0fb2dfb0da5d5..a7c7306ff7d3a9186a83117b9e61d7fc34f420ed 100644
--- a/tests/library/Class/ProfilTest.php
+++ b/tests/library/Class/ProfilTest.php
@@ -1168,9 +1168,9 @@ class ProfilPesseyValandryTranslatedTest extends ModelTestCase {
   public function getCfgNoticeAsArrayShouldCallTranslator() {
     $mockTranslator = $this->_getMockTranslator();
     $mockTranslator->expects($this->once())
-                        ->method('translate')
-                        ->with(ProfilPesseyValandryFixtures::createNoticeConfiguration(), 'Notice')
-                        ;
+                   ->method('translate')
+                   ->with(ProfilPesseyValandryFixtures::createNoticeConfiguration(), 'Notice')
+                   ->willReturn(['exemplaires' => []]);
 
     $this->_profil->setTranslator($mockTranslator);
     $this->_profil->getCfgNoticeAsArray();
diff --git a/tests/library/Class/WebService/ArteVODTest.php b/tests/library/Class/WebService/ArteVODTest.php
index 6604ae671f08139e36d54bce736787471e2227e8..5f6d25bd000f77c267752539127fe61e91ae8a45 100644
--- a/tests/library/Class/WebService/ArteVODTest.php
+++ b/tests/library/Class/WebService/ArteVODTest.php
@@ -52,7 +52,7 @@ abstract class ArteVODHarverstingTestCase extends ModelTestCase {
 
 
 
-class ArteVODHarverstingFourFilmsInTwoPages extends ArteVODHarverstingTestCase {
+class ArteVODHarverstingFourFilmsInTwoPagesTest extends ArteVODHarverstingTestCase {
   public function setUp() {
     parent::setUp();
     $auth = ['auth' => ['user' => 'user',
@@ -71,8 +71,6 @@ class ArteVODHarverstingFourFilmsInTwoPages extends ArteVODHarverstingTestCase {
 
     $this->_web_client->beStrict();
 
-    $this->fixture('Class_AlbumCategorie', null);
-
     // should delete nothing after harvest
     $this->onLoaderOfModel('Class_Album')
          ->whenCalled('deleteBy')
diff --git a/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php b/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php
index 9c3b8bfa23f24ca74cb7107756b999da46ec16c1..fea01be34af261c16d53687b264106d55807f680 100644
--- a/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php
+++ b/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php
@@ -621,7 +621,8 @@ class SitoViewHelperSelectItemsByRecentOrderTest extends SitoViewHelperTestCase
     protected function addFixturesSito() {
       parent::addFixturesSito();
       $this->onLoaderOfModel('Class_Sitotheque')
-           ->whenCalled('getSitesFromIdsAndCategories');
+           ->whenCalled('getSitesFromIdsAndCategories')
+           ->answers([]);
     }
 
 
diff --git a/tests/scenarios/PnbDilicom/PnbDilicomTest.php b/tests/scenarios/PnbDilicom/PnbDilicomTest.php
index a93bb251042e13dc04fa26620b9ffe1f5c201606..b089e263bf12dd3685ab53b91c80667631e0c85d 100644
--- a/tests/scenarios/PnbDilicom/PnbDilicomTest.php
+++ b/tests/scenarios/PnbDilicom/PnbDilicomTest.php
@@ -3938,6 +3938,11 @@ class PnbDilicomAdminIndexControllerTest extends AbstractControllerTestCase {
                                    'role_level' => ZendAfi_Acl_AdminControllerRoles::SUPER_ADMIN]);
     ZendAfi_Auth::getInstance()->logUser($super_admin);
 
+    Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient($this
+                                                                    ->mock()
+                                                                    ->whenCalled('open_url')
+                                                                    ->answers(''));
+
     Class_AdminVar::set('BIBNUM', '1');
     Class_AdminVar::set('DILICOM_PNB', 1);
     Class_AdminVar::set('DILICOM_PNB_GLN_COLLECTIVITE', 'afi-bib');
@@ -3946,9 +3951,16 @@ class PnbDilicomAdminIndexControllerTest extends AbstractControllerTestCase {
     Class_AdminVar::set('DILICOM_PNB_GLN_CONTRACTOR', 123456789);
     Class_AdminVar::set('DILICOM_PNB_IP_ADRESSES', '127.0.0.1');
 
+
     $this->dispatch('/admin/index/index', true);
     $this->assertXPathContentContains('//a', 'PNB Dilicom');
   }
+
+
+  public function tearDown() {
+    Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient(null);
+    parent::tearDown();
+  }
 }
 
 
diff --git a/tests/scenarios/RGPD/PatronDownloadDatasTest.php b/tests/scenarios/RGPD/PatronDownloadDatasTest.php
index c9317af1a7e682e8c4869e31a8b895fdaf54b719..33794b50fa61de5c86474bc21d3cdd512a5caf09 100644
--- a/tests/scenarios/RGPD/PatronDownloadDatasTest.php
+++ b/tests/scenarios/RGPD/PatronDownloadDatasTest.php
@@ -226,6 +226,11 @@ class RGPD_PatronDowloadDatasTest extends AbstractControllerTestCase {
 
 
   public function _createPnb() {
+    Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient($this
+                                                                    ->mock()
+                                                                    ->whenCalled('open_url')
+                                                                    ->answers(''));
+
     RessourcesNumeriquesFixtures::activateDilicom();
     $book = (new DilicomFixtures())->albumTotemThora();
     $this->fixture('Class_Hold_Pnb',
@@ -245,6 +250,12 @@ class RGPD_PatronDowloadDatasTest extends AbstractControllerTestCase {
   }
 
 
+  public function tearDown() {
+    Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient(null);
+    parent::tearDown();
+  }
+
+
   protected function _createLinkedCards() {
     $death_star = $this->fixture('Class_Bib',
                                  ['id' => 23,