diff --git a/VERSIONS_HOTLINE/132723 b/VERSIONS_HOTLINE/132723
new file mode 100644
index 0000000000000000000000000000000000000000..a81594a0f257c07bebb88f0e29e0d7f14c435842
--- /dev/null
+++ b/VERSIONS_HOTLINE/132723
@@ -0,0 +1 @@
+ - ticket #132723 : Administration : Une entrée de menu vers l'explorateur de fichiers a été ajouté dans la boite d'administration de l'interface publique.
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/133782 b/VERSIONS_HOTLINE/133782
new file mode 100644
index 0000000000000000000000000000000000000000..ad95a3cea681f28a4587cb74560887d467c8726e
--- /dev/null
+++ b/VERSIONS_HOTLINE/133782
@@ -0,0 +1 @@
+ - ticket #133782 : Cosmogramme : Les facettes dynamiques ne se créaient pas lorsqu'elles avaient le même champs de libellé et de filtre
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/136506 b/VERSIONS_HOTLINE/136506
new file mode 100644
index 0000000000000000000000000000000000000000..9c0a1947858058b718901a66ecda179b8fef2148
--- /dev/null
+++ b/VERSIONS_HOTLINE/136506
@@ -0,0 +1 @@
+ - ticket #136506 : Administration : Mise à jour de l'URL de publication automatique du rapport système 
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/137828 b/VERSIONS_HOTLINE/137828
new file mode 100644
index 0000000000000000000000000000000000000000..f7ac6494540eb2161c6a1380694edda0b9c204ae
--- /dev/null
+++ b/VERSIONS_HOTLINE/137828
@@ -0,0 +1 @@
+ - ticket #137828 : Notice : Suppression d'une colonne personnalisée vide restante après remise à 0 des colonnes personnalisées dans la configuration du tableau des exemplaires
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/138162 b/VERSIONS_HOTLINE/138162
new file mode 100644
index 0000000000000000000000000000000000000000..632740c1059b4ca03c185ecb64a8fc259d6c4e92
--- /dev/null
+++ b/VERSIONS_HOTLINE/138162
@@ -0,0 +1 @@
+ - ticket #138162 : Administration : Correction du lien de configuration de la liste des prêts de l'espace abonné
\ No newline at end of file
diff --git a/application/modules/opac/controllers/AbonneController.php b/application/modules/opac/controllers/AbonneController.php
index e4728c0c9f4996ad7e68d80caeaebb4533cb864f..a881c63aca85ea87c04e0161da67b9c9fbee1451 100644
--- a/application/modules/opac/controllers/AbonneController.php
+++ b/application/modules/opac/controllers/AbonneController.php
@@ -63,9 +63,11 @@ class AbonneController extends ZendAfi_Controller_Action {
       return;
     }
 
-    $this->view->_current_module['controller'] = 'abonne';
-    $this->view->_current_module['action'] = 'fiche';
-    $this->view->_current_module['action2'] = '';
+    if (!Class_Template::current()->isLegacy()) {
+      $this->view->_current_module['controller'] = 'abonne';
+      $this->view->_current_module['action'] = 'fiche';
+      $this->view->_current_module['action2'] = '';
+    }
   }
 
 
diff --git a/application/modules/opac/controllers/AuthorController.php b/application/modules/opac/controllers/AuthorController.php
index 6569616c233eae35dc7993c7e9765326a0e1cba1..b485fd936a565072890f25326a6aa09e8b6e1685 100644
--- a/application/modules/opac/controllers/AuthorController.php
+++ b/application/modules/opac/controllers/AuthorController.php
@@ -28,9 +28,11 @@ class AuthorController extends ZendAfi_Controller_Action {
     if (!$this->_author = $this->view->author = $this->_findAuthor())
       throw new Zend_Controller_Action_Exception($this->view->_('Désolé, cette page n\'existe pas'), 404);
 
-    $this->view->_current_module['controller'] = 'author';
-    $this->view->_current_module['action'] = 'view';
-    $this->view->_current_module['action2'] = '';
+    if (!Class_Template::current()->isLegacy()) {
+      $this->view->_current_module['controller'] = 'author';
+      $this->view->_current_module['action'] = 'view';
+      $this->view->_current_module['action2'] = '';
+    }
   }
 
 
diff --git a/application/modules/opac/controllers/BibController.php b/application/modules/opac/controllers/BibController.php
index 41dc0f6760133e3bcf331c13035f91585ac305e7..faa212bfa9bfb6f1c165fce7882a7f63dc472655 100644
--- a/application/modules/opac/controllers/BibController.php
+++ b/application/modules/opac/controllers/BibController.php
@@ -25,9 +25,11 @@ class BibController extends ZendAfi_Controller_Action {
     parent::preDispatch();
     $this->_helper->librarySelection();
 
-    $this->view->_current_module['controller'] = 'bib';
-    $this->view->_current_module['action'] = 'en-lire-plus';
-    $this->view->_current_module['action2'] = '';
+    if (!Class_Template::current()->isLegacy()) {
+      $this->view->_current_module['controller'] = 'bib';
+      $this->view->_current_module['action'] = 'en-lire-plus';
+      $this->view->_current_module['action2'] = '';
+    }
   }
 
 
diff --git a/cosmogramme/sql/patch/patch_414.php b/cosmogramme/sql/patch/patch_414.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a8e2f52ec8c5a6707c86406cb3550aef861c111
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_414.php
@@ -0,0 +1,7 @@
+<?php
+$adapter = Zend_Db_Table::getDefaultAdapter();
+
+try {
+  $adapter->query('update bib_admin_var set valeur="https://pola.afi-sa.net/smile.php" where clef="STATUS_REPORT_PUSH_URL" and valeur="http://pola.afi-sa.net/smile.php"');
+} catch(Exception $e) {
+}
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 6e177da829dcd63e1d523dfff282f0a7b4ccd3ad..11606d7200cb9e40eef71e7b32239a307d22f621 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -423,7 +423,8 @@ Pour vous désabonner de la lettre d\'information, merci de cliquer sur le lien
             'URL_COSMOGRAMME' => Class_AdminVar_Meta::newDefault('')->bePrivate(),
             'PACK_MOBILE' => Class_AdminVar_Meta::newOnOff($this->_('Activation des fonctions avancées du téléphone'))->bePrivate(),
             'BUID' => Class_AdminVar_Meta::newRawText($this->_('Identifiant unique (attention: la modification de cette variable impactera les outils de suivi de cette installation)'))->bePrivate(),
-            'STATUS_REPORT_PUSH_URL' => Class_AdminVar_Meta::newRawText($this->_('URL destinataire du rapport d\'état du système (0 pour désactiver)'), ['value' => 'http://pola.afi-sa.net/smile.php'])->bePrivate(),
+            'STATUS_REPORT_PUSH_URL' => Class_AdminVar_Meta::newRawText($this->_('URL destinataire du rapport d\'état du système (0 pour désactiver)'),
+                                                                        ['value' => 'https://pola.afi-sa.net/smile.php'])->bePrivate(),
             'STATUS_REPORT_TAGS' => Class_AdminVar_Meta::newMultiInput($this->_('Liste des tags à ajouter au rapport d\'état du système'))->bePrivate(),
             'FEATURES_TRACKING_ENABLE' => Class_AdminVar_Meta::newOnOff('Affiche les dernières modifications apportés au logiciel Bokeh', ['value' => 1]),
             'INSPECTOR_GADGET_MARC_XML' => Class_AdminVar_Meta::newOnOff('Affiche le MARC XML de la notice dans Inspector Gadget')->enable(),
diff --git a/library/Class/CodifThesaurus.php b/library/Class/CodifThesaurus.php
index 763c2dfdec1b23ed41ecc8e0aceefb6a55eed4ec..839b7d3d7ee840f492cfa7da1848f0bd7e3acee7 100644
--- a/library/Class/CodifThesaurus.php
+++ b/library/Class/CodifThesaurus.php
@@ -689,11 +689,6 @@ class Class_CodifThesaurus extends Storm_Model_Abstract {
   }
 
 
-  public function rulesTruncateLabels($labels) {
-    return $this->_getListRules()->truncateLabels($labels);
-  }
-
-
   public function getRulesAsArray() {
     return ['rule_list_zone' => $this->getRuleListZone(),
             'rule_list_label_field' => $this->getRuleListLabelField(),
diff --git a/library/Class/CodifThesaurus/Rules.php b/library/Class/CodifThesaurus/Rules.php
index f4dba2a67c9a186f99ce4dd5ae11d53024caaa2d..140c9469e4af1b2b8aff61201f58480774235688 100644
--- a/library/Class/CodifThesaurus/Rules.php
+++ b/library/Class/CodifThesaurus/Rules.php
@@ -116,68 +116,7 @@ class Class_CodifThesaurus_Rules extends Class_Entity {
    * @param $reader cosmogramme notice_unimarc
    */
   public function extractIdAndFields($reader) {
-    if (!$label_field = trim($this->getLabelField()))
-      return [];
-
-    $id_field = trim($this->getIdField());
-    $filter_field = $this->getFilterField();
-
-    $fields = $reader->cutBlockBySubfields($this->getZonePadded(),
-                                           [$label_field => 'label',
-                                            $id_field => 'id',
-                                            $filter_field => 'filter'],
-                                           [$this, 'filterSubfieldIn']);
-
-    return array_filter(array_map([$this, 'buildAuthorityStructure'],
-                                  $fields));
-  }
-
-
-  public function filterSubfieldIn($block) {
-    $label = $this->truncateLabel($block->get('label'));
-    if (!$this->isValidLabel($label))
-      return false;
-
-    $filter_zone = $this->getZone();
-    $filter_field = $this->getFilterField();
-    $filter_value = $this->getFilterValue();
-
-    if (!$filter_zone || (null === $filter_field) | !$filter_value)
-      return true;
-
-    return $block->get('filter') == $filter_value;
-  }
-
-
-  public function buildAuthorityStructure($block){
-    $label = $this->truncateLabel($block->get('label'));
-    return ['id' => ($block->get('id')
-                     ? strtoupper($block->get('id'))
-                     : ''),
-            'label' => $label];
-  }
-
-  public function truncateLabels($labels) {
-    return array_filter(
-                        array_map([$this, 'truncateLabel'], $labels),
-                        [$this, 'isValidLabel']);
-  }
-
-
-  public function truncateLabel($label) {
-    if (!$this->getLabelLength())
-      return $label;
-
-    return $this->isValidLabel($label)
-      ? substr($label,
-               max(0, $this->getLabelStartPos()-1),
-               $this->getLabelLength())
-      : '';
-  }
-
-
-  public function isValidLabel($label) {
-    return is_string($label) && $label > '';
+    return (new Class_NoticeUnimarc_ExtractFields($reader, $this))->getListFields();
   }
 
 
@@ -190,4 +129,4 @@ class Class_CodifThesaurus_Rules extends Class_Entity {
   public function getZonePadded() {
     return sprintf('%03d', trim($this->getZone()));
   }
-}
\ No newline at end of file
+}
diff --git a/library/Class/NoticeUnimarc.php b/library/Class/NoticeUnimarc.php
index 0c5d9944daa9b511017e0f24a9ecda13031267c3..b891d213c1242d8c5edd6641fcb47d2275069d17 100644
--- a/library/Class/NoticeUnimarc.php
+++ b/library/Class/NoticeUnimarc.php
@@ -231,59 +231,15 @@ class Class_NoticeUnimarc {
                         ),...
               ]
   */
-  public function cutBlockBySubfields($zone, $subfields_mapping, $closure_valid = null) {
-
-    $this->blocks=[];
-
-    foreach ($this->get_subfield($zone) as $line) {
-      $this->_cutLineBySubfields($line, $subfields_mapping,$closure_valid);
-    }
-    return $this->blocks;
+  public function cutBlockBySubfields($zone, $subfields_mapping) {
+    return (new Class_NoticeUnimarc_ExtractFields($this, $subfields_mapping, $zone))
+      ->getListFields();
   }
 
 
-  protected function _cutLineBySubfields($line, $subfields_mapping, $closure_valid) {
-    $block = $this->_newBlock();
-
-    $fields = $this->_bloc_to_array($line);
-
-    $bloc_values = array_values($subfields_mapping);
-
-    foreach($fields as $field) {
-      $code = substr($field, 0, 1);
-      $value = trim(substr($field, 1));
-      if (!in_array($code,array_keys($subfields_mapping)))
-        continue;
-
-      if ($block->get($subfields_mapping[$code])) {
-        $block = $this->_addBlock($block,$closure_valid);
-      }
-
-      $block->set($subfields_mapping[$code], $value);
-
-    }
-
-    $this->_addBlock($block,$closure_valid);
-
-    return $this;
-  }
-
-
-  protected function _newBlock() {
-    return new Class_Entity();
-  }
-
-
-  protected function _addBlock($block, $closure_valid) {
-    if (!$closure_valid || call_user_func($closure_valid,$block))
-      $this->blocks[] = $block;
-    return $this->_newBlock();
-  }
-
-
-  protected function _bloc_to_array($bloc) {
-    $bloc = substr($bloc, 3);
-    return explode($this->subfield_begin, $bloc);
+  public function lineToArray($line) {
+    $line = substr($line, 3);
+    return explode($this->subfield_begin, $line);
   }
 
 
@@ -383,7 +339,7 @@ class Class_NoticeUnimarc {
 
 
   public function get_field($zone) {
-    $instance = new Class_NoticeUnimarc_Field;
+    $instance = new Class_Entity();
 
     $bloc = substr($zone, 3);
     $fields = explode($this->subfield_begin, $bloc);
diff --git a/library/Class/NoticeUnimarc/ExtractFields.php b/library/Class/NoticeUnimarc/ExtractFields.php
new file mode 100644
index 0000000000000000000000000000000000000000..f983040bb65932e2e77177fd651c141c24090698
--- /dev/null
+++ b/library/Class/NoticeUnimarc/ExtractFields.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_NoticeUnimarc_ExtractFields {
+
+  protected
+    $_reader,
+    $_list_fields;
+
+  public function __construct($reader, $rules_or_array, $zone = null) {
+    $this->_reader = $reader;
+    $this->_list_fields = Class_NoticeUnimarc_ListFields::newFor($rules_or_array,
+                                                                 $zone);
+  }
+
+
+  public function getListFields() {
+    if (!$this->_list_fields->isValid())
+      return [];
+
+    foreach ($this->_reader->get_subfield($this->_list_fields->getZone()) as $line)
+      $this->_cutLineBySubfields($line);
+
+    return $this->_list_fields->getListResult();
+  }
+
+
+  protected function _cutLineBySubfields($line) {
+    $fields = $this->_reader->lineToArray($line);
+
+    foreach ($fields as $field) {
+      $code = substr($field, 0, 1);
+      $value = trim(substr($field, 1));
+      $this->_list_fields->addCodeValue($code, $value);
+    }
+
+    $this->_list_fields->addInResultAndReset();
+    return $this;
+  }
+}
diff --git a/library/Class/NoticeUnimarc/Field.php b/library/Class/NoticeUnimarc/Field.php
index 21c2e4f23c35e9f78bb17dfe74ea67636c4f5b73..68739bfb6f5419c06341138a4921638c23cde3e6 100644
--- a/library/Class/NoticeUnimarc/Field.php
+++ b/library/Class/NoticeUnimarc/Field.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved.
+ * Copyright (c) 2012-2021, 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
@@ -20,4 +20,41 @@
  */
 
 
-class Class_NoticeUnimarc_Field extends Class_Entity {}
+class Class_NoticeUnimarc_Field {
+
+  protected
+    $_code,
+    $_value,
+    $_label;
+
+  public function __construct($code, $label) {
+    $this->_code = $code;
+    $this->_label = $label;
+  }
+
+
+  public function getCode() {
+    return $this->_code;
+  }
+
+
+  public function isSameCode($code) {
+    return $code && $code == $this->getCode();
+  }
+
+
+  public function getLabel() {
+    return $this->_label;
+  }
+
+
+  public function getValue() {
+    return $this->_value;
+  }
+
+
+  public function setValue($value) {
+    $this->_value = $value;
+    return $this;
+  }
+}
diff --git a/library/Class/NoticeUnimarc/ListFields.php b/library/Class/NoticeUnimarc/ListFields.php
new file mode 100644
index 0000000000000000000000000000000000000000..04de3dee74ec8ee849c1beecaccd8ae78bd0a837
--- /dev/null
+++ b/library/Class/NoticeUnimarc/ListFields.php
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_NoticeUnimarc_ListFields {
+
+  protected
+    $_list_fields,
+    $_list_result,
+    $_zone,
+    $_valid = false;
+
+  public function __construct($rules_or_array, $zone = null) {
+    $this->_list_fields = new Storm_Collection();
+    $this->_list_result = [];
+    $this->_zone = $zone;
+    $this->_init($rules_or_array);
+  }
+
+
+  public static function newFor($rules_or_array, $zone = null) {
+    if (is_a($rules_or_array, Class_CodifThesaurus_Rules::class))
+      return new Class_NoticeUnimarc_ListFieldsRules($rules_or_array, $zone);
+
+    return new static($rules_or_array, $zone);
+  }
+
+
+  public function isValid() {
+    return $this->_valid;
+  }
+
+
+  public function getListResult() {
+    return $this->_list_result;
+  }
+
+
+  public function getZone() {
+    return $this->_zone;
+  }
+
+
+  public function addCodeValue($code, $value) {
+    if (!($working_list = $this->_getWorkingList($code)))
+      return $this;
+
+    $with_value = $working_list->detect(function ($field) {
+      return $field->getValue();
+    });
+
+    if ($with_value)
+      return $this->addInResultAndReset($code, $value);
+
+    $working_list->eachDo(function ($field) use ($value) {
+      $field->setValue($value);
+    });
+    return $this;
+  }
+
+
+  public function addInResultAndReset($code = null, $value = null) {
+    if ($this->_canAddResult())
+      $this->_list_result [] = $this->_buildOneResult();
+
+    $this->_reset();
+    $this->_addAfterReset($code, $value);
+    return $this;
+  }
+
+
+  protected function _init($array) {
+    if (!($this->_valid = is_array($array)))
+      return $this;
+
+    foreach ($array as $code => $label)
+      $this->_list_fields->add(new Class_NoticeUnimarc_Field($code, $label));
+
+    return $this;
+  }
+
+
+  protected function _getWorkingList($code) {
+    if (!$this->isValid() || !$code)
+      return null;
+
+    $working_list = $this->_list_fields->select(function ($field) use ($code) {
+      return $field->isSameCode($code);
+    });
+
+    return $working_list->isEmpty()
+      ? null
+      : $working_list;
+  }
+
+
+  protected function _reset() {
+    $this->_list_fields->eachDo(function ($field) {
+      $field->setValue(null);
+    });
+
+    return $this;
+  }
+
+
+  protected function _addAfterReset($code, $value) {
+    if (!($working_list = $this->_getWorkingList($code)))
+      return $this;
+
+    $working_list->eachDo(function ($field) use ($value) {
+      $field->setValue($value);
+    });
+
+    return $this;
+  }
+
+
+  protected function _canAddResult() {
+    return true;
+  }
+
+
+  protected function _buildOneResult() {
+    $array = [];
+
+    $this->_list_fields->eachDo(function ($field) use (&$array) {
+      $array [$field->getLabel()] = $field->getValue();
+    });
+
+    return $array;
+  }
+}
+
+
+
+
+class Class_NoticeUnimarc_ListFieldsRules extends Class_NoticeUnimarc_ListFields {
+
+  const
+    ID = 'id',
+    LABEL = 'label',
+    FILTER = 'filter';
+
+  protected $_rules;
+
+  protected function _init($rules) {
+    $label_field = new Class_NoticeUnimarc_Field(trim($rules->getLabelField()),
+                                                 static::LABEL);
+    if (!$label_field->getCode())
+      return $this;
+
+    $this->_zone = $rules->getZonePadded();
+    $this->_valid = true;
+    $this->_rules = $rules;
+    $this->_list_fields->add($label_field);
+    $this->_list_fields->add(new Class_NoticeUnimarc_Field(trim($rules->getIdField()),
+                                                           static::ID));
+    $this->_list_fields
+         ->add(new Class_NoticeUnimarc_Field($rules->getFilterField(),
+                                             static::FILTER));
+
+    return $this;
+  }
+
+
+  protected function _getFieldLabel() {
+    return $this->_detectByLabel(static::LABEL);
+  }
+
+
+  protected function _getFieldId() {
+    return $this->_detectByLabel(static::ID);
+  }
+
+
+  protected function _getFieldFilter() {
+    return $this->_detectByLabel(static::FILTER);
+  }
+
+
+  protected function _detectByLabel($label) {
+    return $this->_list_fields->detect(function ($field) use ($label) {
+      return $label === $field->getLabel();
+    });
+  }
+
+
+  protected function _canAddResult() {
+    $field_label = $this->_getFieldLabel();
+    $label = $this->_truncateLabel($field_label->getValue());
+    $field_label->setValue($label);
+    if (!$label)
+      return false;
+
+    $filter_zone = $this->_rules->getZone();
+    $filter_field = $this->_rules->getFilterField();
+    $filter_value = $this->_rules->getFilterValue();
+
+    if (!$filter_zone || (null === $filter_field) | !$filter_value)
+      return true;
+
+    return $this->_getFieldFilter()->getValue() == $filter_value;
+  }
+
+
+  protected function _buildOneResult() {
+    return [static::ID => (($id = $this->_getFieldId()->getValue())
+                           ? strtoupper($id)
+                           : ''),
+            static::LABEL => $this->_getFieldLabel()->getValue()];
+  }
+
+
+  protected function _truncateLabel($label) {
+    $valid = is_string($label) && $label > '';
+    if (!$this->_rules->getLabelLength())
+      return $valid ? $label : '';
+
+    return $valid
+      ? substr($label,
+               max(0, $this->_rules->getLabelStartPos() - 1),
+               $this->_rules->getLabelLength())
+      : '';
+  }
+}
diff --git a/library/ZendAfi/View/Helper/Admin/FrontNavEntries.php b/library/ZendAfi/View/Helper/Admin/FrontNavEntries.php
index 8e94003fc7dba571068ca0e15de304d4a480a9e8..9d712ac35ba9e44e88cc13474d901ac96863f924 100644
--- a/library/ZendAfi/View/Helper/Admin/FrontNavEntries.php
+++ b/library/ZendAfi/View/Helper/Admin/FrontNavEntries.php
@@ -78,6 +78,13 @@ class ZendAfi_View_Helper_Admin_FrontNavEntries extends ZendAfi_View_Helper_Base
                               $this->_('Accéder aux domaines dans l\'interface d\'administration'),
                               'domains');
 
+    if ($this->_isAllowed('file-manager', 'index'))
+      $links[] = $this->_item('/admin/file-manager/',
+                              $this->_('Explorateur de fichiers'),
+                              $this->_('Accéder à l\'explorateur de fichiers dans un nouvel onglet'),
+                              'filebrowser',
+                              ['target' => '_blank']);
+
     if ($this->_isAllowed('users', 'index'))
       $links[] = $this->_item('/admin/users',
                               $this->_('Utilisateurs'),
diff --git a/library/ZendAfi/View/Helper/Notice/Exemplaires.php b/library/ZendAfi/View/Helper/Notice/Exemplaires.php
index cb2083b15278f416d27c688ea8c8f8f9973f2b03..58b049f32a0ac6aaabde262d6f38c02d4d4c89f1 100644
--- a/library/ZendAfi/View/Helper/Notice/Exemplaires.php
+++ b/library/ZendAfi/View/Helper/Notice/Exemplaires.php
@@ -180,7 +180,10 @@ class ZendAfi_View_Helper_Notice_Exemplaires extends ZendAfi_View_Helper_BaseHel
     }
 
     $subfields = Class_Profil_ItemsSettings::current()->getIncludedSubfields();
-    foreach($subfields['datas_items_code'] as  $key => $code) {
+    foreach($subfields['datas_items_code'] as $key => $code) {
+      if ('' === $code)
+        continue;
+
       $renderer = (new ZendAfi_View_Helper_Notice_ExemplairesTable_CustomColumn())
         ->setView($this->view)
         ->setExemplaires($exemplaires)
diff --git a/tests/application/modules/opac/controllers/AbonneControllerPretsAdminTest.php b/tests/application/modules/opac/controllers/AbonneControllerPretsAdminTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b5cb13e9883498b2a24dbcb3e79279ab59b2654
--- /dev/null
+++ b/tests/application/modules/opac/controllers/AbonneControllerPretsAdminTest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, 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 AbonneControllerPretsAdminTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  /** @test */
+  public function historicPageShouldContainsLinkToEditLoansConfig() {
+    $this->dispatch('/abonne/prets');
+    $this->assertXPath('//a[contains(@href, "/admin/modules/abonne/config/site/type_module/abonne")]'
+                       .'[contains(@href, "/action1/prets")]');
+  }
+
+
+  /** @test */
+  public function intonationPageShouldContainsLinkToEditAccountConfig() {
+    $this->_buildTemplateProfil(['id' => 200]);
+    $this->dispatch('/abonne/prets');
+    $this->assertXPath('//a[contains(@href, "/admin/widget/edit-action/id/abonne_fiche")]');
+  }
+}
diff --git a/tests/application/modules/opac/controllers/BibControllerBibViewTest.php b/tests/application/modules/opac/controllers/BibControllerBibViewTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b2048b05a8cc8d2c400f11deee8823f77b289cc6
--- /dev/null
+++ b/tests/application/modules/opac/controllers/BibControllerBibViewTest.php
@@ -0,0 +1,959 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, 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 BibControllerBibViewInexistantTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  /** @test */
+  public function responseShouldRedirectToIndex() {
+    $this->dispatch('bib/bibview/');
+    $this->assertRedirectTo('/opac/bib/index');
+  }
+}
+
+
+
+
+abstract class BibControllerBibViewTestCase extends AbstractControllerTestCase {
+  protected
+    $_mock_sql,
+    $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+    unset($_REQUEST['geo_zone']);
+
+    $this->_mock_sql = $this->mock();
+    Zend_Registry::set('sql', $this->_mock_sql);
+
+
+    $this->bib_annecy = $this->fixture('Class_Bib', ['id' => 4,
+                                                     'id_zone' => 1,
+                                                     'libelle' => 'Annecy',
+                                                     'aff_zone' => '',
+                                                     'ville' => 'Annecy',
+                                                     'url_web' => 'http://www.annecy.fr',
+                                                     'mail' => 'jp@annecy.com',
+                                                     'telephone' => '04 50 51 32 12',
+                                                     'adresse' => '1 rue Jean Jaures',
+                                                     'photo' => '/userfiles/photobib/photobib4.jpg',
+                                                     'visibilite' => Class_Bib::V_DATA,
+                                                     'fond' => 'Tous les documents',
+                                                     'rewrite_url' => 'annecy']);
+
+    $this->bib_nozone = $this->fixture('Class_Bib', ['id' => 22,
+                                                     'id_zone' => 100,
+                                                     'libelle' => 'NoZone',
+                                                     'aff_zone' => '',
+                                                     'ville' => 'No Zone',
+                                                     'url_web' => 'http://www.nozone.fr',
+                                                     'mail' => 'mail@nozone.com',
+                                                     'telephone' => '04 50 51 32 12',
+                                                     'adresse' => '1 rue Jean Jaures',
+                                                     'visibilite' => Class_Bib::V_DATA]);
+
+    $this->haute_savoie = $this->fixture('Class_Zone', ['id' => 1,
+                                                        'libelle' => 'Haute-Savoie',
+                                                        'couleur_texte' => '#059',
+                                                        'couleur_ombre' => '#234',
+                                                        'taille_fonte' => '14',
+                                                        'image' => 'carte_74.png',
+                                                        'bibs' => [$this->bib_annecy]]);
+
+    $ecrivez_des_tests = $this->fixture('Class_Article', ['id' => 2,
+                                                          'id_site' => 0,
+                                                          'titre' => 'Ecrivez des tests !',
+                                                          'contenu' => 'Ça fera le plus grand bien',
+                                                          'categorie' => $this->fixture('Class_ArticleCategorie',
+                                                                                        ['id' => 5,
+                                                                                         'id_site' => 4,
+                                                                                         'libelle' => 'Bonnes pratiques'])]);
+
+    ZendAfi_View_Helper_NewsBibHelper::setTimeSource(new TimeSourceForTest('2016-07-14 22:22:22'));
+
+    $this->onLoaderOfModel('Class_Article')
+         ->whenCalled('getArticlesByPreferences')
+         ->with(['id_bib' => '4',
+                 'events_only' => true,
+                 'display_order' => 'EventDebut',
+                 'event_end_after' => '2016-07-14'])
+         ->answers([$ecrivez_des_tests])
+
+         ->whenCalled('getArticlesByPreferences')
+         ->with(['id_bib' => '4'])
+         ->answers([$ecrivez_des_tests])
+
+
+         ->whenCalled('getArticlesByPreferences')
+         ->with(['id_bib' => '22',
+                 'events_only' => true,
+                 'display_order' => 'EventDebut',
+                 'event_end_after' => '2016-07-14'])
+         ->answers([$ecrivez_des_tests])
+
+         ->whenCalled('getArticlesByPreferences')
+         ->with(['id_bib' => '22'])
+         ->answers([$ecrivez_des_tests])
+
+
+         ->whenCalled('filterByLocaleAndWorkflow')
+         ->with([$ecrivez_des_tests])
+         ->answers([$ecrivez_des_tests]);
+
+    $this->onLoaderOfModel('Class_Exemplaire')
+         ->whenCalled('countBy')
+         ->with(['model' => $this->bib_annecy, 'role' => 'bib'])
+         ->answers(20)
+
+         ->whenCalled('countBy')
+         ->with(['model' => $this->bib_nozone, 'role' => 'bib'])
+         ->answers(20)
+
+         ->whenCalled('countBy')
+         ->with([])
+         ->answers(20)
+
+         ->beStrict();
+
+    $this->_mock_sql
+      ->whenCalled('fetchOne')
+      ->answers('');
+  }
+
+
+  public function tearDown() {
+    Class_Ouverture_Visitor::setTimeSource(null);
+    ZendAfi_View_Helper_NewsBibHelper::setTimeSource(null);
+    parent::tearDown();
+  }
+}
+
+
+
+
+class BibControllerBibViewOnlyAfternoonAnnecyNotLoggedTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+    ZendAfi_Auth::getInstance()->clearIdentity();
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-06-24 08:08:42'));
+
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 21,
+                    'id_site' => 4,
+                    'label' => 'Horaires réduits',
+                    'jour_semaine' => Class_Ouverture::MARDI,
+                    'debut_matin' => '00:00',
+                    'fin_matin' => '00:00',
+                    'debut_apres_midi' => '15:00',
+                    'fin_apres_midi' => '17:00',
+                   ]);
+
+
+    Class_bib::find(4)
+      ->setHoraire('  ');
+
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  public function tearDown() {
+    Class_Ouverture_Visitor::setTimeSource(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsDefaultOpening() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//dd',
+                                      'Mardi : 15h - 17h', $this->_response->getBody());
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyNotLoggedTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+    ZendAfi_Auth::getInstance()->clearIdentity();
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-06-24 08:08:42'));
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 1,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI]);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 20,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::MARDI]);
+
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 21,
+                    'id_site' => 4,
+                    'label' => 'Horaires réduits',
+                    'jour_semaine' => Class_Ouverture::MARDI,
+                    'debut_matin' => '00:00',
+                    'fin_matin' => '00:00',
+                    'debut_apres_midi' => '15:00',
+                    'fin_apres_midi' => '17:00',
+                    'validity_start' => '2016-07-01',
+                    'validity_end' => '2016-07-20']);
+
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 2,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'validity_start' => '2016-07-01',
+                    'validity_end' => '2016-08-31']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 3,
+                    'id_site' => 4,
+                    'jour' => '2016-07-01']);
+
+    // too far away
+    $this->fixture('Class_Ouverture',
+                   ['id' => 4,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'validity_start' => '2020-06-31',
+                    'validity_end' => '2020-08-31']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 5,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'validity_start' => '2016-07-02',
+                    'validity_end' => '2016-07-03']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 6,
+                    'id_site' => 4,
+                    'jour' => '2016-06-12']);
+
+    // past
+    $this->fixture('Class_Ouverture',
+                   ['id' => 7,
+                    'id_site' => 4,
+                    'jour' => '2016-06-26',
+                    'debut_matin' => '00:00',
+                    'fin_matin' => '00:00',
+                    'debut_apres_midi' => '00:00',
+                    'fin_apres_midi' => '00:00']);
+
+    // multimedia dedicated
+    $this->fixture('Class_Ouverture',
+                   ['id' => 8,
+                    'id_site' => 4,
+                    'jour' => '2016-07-07',
+                    'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA]);
+
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  /** @test */
+  public function formRechercheShouldLinkToCurrentBib() {
+    $this->assertXPath('//form[contains(@action, "/recherche/simple/id/4/geo_bib/4")]');
+  }
+
+
+  /** @test */
+  public function fondsShouldDisplayTousLesDocuments() {
+    $this->assertXPathContentContains('//div[@class="library_fonds library_box"]/dl[preceding-sibling::h2[text()="Fonds"]]/dd',
+                                      'Tous les documents');
+  }
+
+
+  /** @test */
+  public function adresseShouldBe1RueJeanJaures() {
+    $this->assertXPathContentContains('//dd', '1 rue Jean Jaures');
+  }
+
+
+  /** @test */
+  public function photoBibShouldBeDisplayedWithClassBibviewImage() {
+    $this->assertXPath('//img[@class="bibview_image"][@src="'. BASE_URL .'/userfiles/photobib/photobib4.jpg"]');
+  }
+
+
+  /** @test */
+  public function articleEcrivezDesTestsShouldBeVisible() {
+    $this->assertXPathContentContains('//article//header//a[contains(@href, "/cms/articleview/id/2")]',
+                                      'Ecrivez des tests !');
+  }
+
+
+  /** @test */
+  public function articleEcrivezDesTestsShouldContainsHeaderSpan() {
+    $this->assertXPathContentContains('//article//header//span',
+                                      'Annecy');
+  }
+
+
+  /** @test */
+  public function libelleCatBonnesPratiquesShouldBeVisible() {
+    $this->assertXPathContentContains('//h2',
+                                      'Bonnes pratiques');
+  }
+
+
+  /** @test */
+  public function urlRetourShouldBeZoneViewIdOne() {
+    $this->assertXPathContentContains('//a[contains(@href, "/bib/zoneview/id/1")]',
+                                      'Retour');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsDefaultOpening() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//dd',
+                                      'Lundi : 10h - 18h');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsPeriodLabel() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'du vendredi 01 juillet au mercredi 31 août 2016');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsSecondPeriodLabel() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'du samedi 02 au dimanche 03 juillet 2016');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsHorairesReduits() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'Horaires réduits du vendredi 01 au mercredi 20 juillet 2016');
+  }
+
+
+  /** @test */
+  public function openingsShouldNotContainsTooFarPeriodLabel() {
+    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                         'du mercredi 01 juillet au lundi 31 août 2020');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsExceptionnalLabel() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'Ouverture exceptionnelle');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsExceptionnalDate() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                      'le vendredi 01 juillet : 10h - 18h');
+  }
+
+
+  /** @test */
+  public function openingsShouldNotContainsMultimediaDate() {
+    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                         'le jeudi 07 juillet : 10h - 18h');
+  }
+
+
+  /** @test */
+  public function openingsShouldNotContainsPastExceptionnalDate() {
+    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                         'le dimanche 12 juin : 10h - 18h');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsClosureLabel() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'Fermeture exceptionnelle');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsClosureDate() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                      'le dimanche 26 juin');
+  }
+
+
+  /** @test */
+  public function editBibShouldNotBePresent() {
+    $this->assertNotXPath('//a[contains(@href, "/admin/bib/edit/id/")]');
+  }
+
+
+  /** @test */
+  public function editOpeningsShouldNotBePresent() {
+    $this->assertNotXPath('//a[contains(@href, "/admin/ouvertures/index/id_site/")]');
+  }
+}
+
+
+
+
+class BibControllerBibViewLibraryOpeningClosedOnHolidaysTest extends BibControllerBibViewTestCase {
+
+  public function setUp() {
+    parent::setUp();
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2018-12-14 11:00:00'));
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 200,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'label' => 'Horaires habituels']);
+
+    foreach(range(1, 7) as $day)
+      $this->fixture('Class_Ouverture',
+                     ['id' => 400 + $day,
+                      'horaires' => ['00:00', '00:00', '00:00', '00:00'],
+                      'jour' => '',
+                      'id_site' => 4,
+                      'validity_start' => '2018-12-23',
+                      'validity_end' => '2019-01-01',
+                      'jour_semaine' => $day]);
+  }
+
+
+  /** @test */
+  public function firstOpeningsH3ShouldContainsHorairesHabituels() {
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertXPathContentContains('//li[1]/h3',
+                                      'Horaires habituels');
+  }
+
+
+  /** @test */
+  public function secondOpeningsH3ShouldContainsFermeturesVacances() {
+    Class_Ouverture::find(401)->setLabel('Fermetures vacances scolaires');
+    $this->dispatch('bib/bibview/id/4', true);
+
+    $this->assertXPathContentContains('//li[2]/h3',
+                                      'Fermetures vacances scolaires du dimanche 23 décembre 2018 au mardi 01 janvier 2019');
+  }
+
+
+  /** @test */
+  public function withoutLabelSecondOpeningsH3ShouldContainsFermetureExceptionnelle() {
+    $this->dispatch('bib/bibview/id/4', true);
+
+    $this->assertXPathContentContains('//li[2]/h3',
+                                      'Fermeture exceptionnelle du dimanche 23 décembre 2018 au mardi 01 janvier 2019');
+  }
+
+
+  /** @test */
+  public function secondOpeningsShouldNotContainsLundi() {
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertNotXPathContentContains('//li[2]//li',
+                                         'Lundi');
+  }
+}
+
+
+
+class BibControllerBibViewAnnecyHideNewsTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+    Class_Profil::getCurrentProfil()
+      ->setModulePreference('bib', 'bibview', 'hide_news', 1);
+
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  /** @test */
+  public function articleEcrivezDesTestsShouldNotBeVisible() {
+    $this->assertNotXPathContentContains('//article//header//a[contains(@href, "/cms/articleview/id/2")]',
+                                         'Ecrivez des tests !');
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyByRewriteUrlTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('annecy', true);
+  }
+
+
+  /** @test */
+  public function h1ShouldContainsAnnecy() {
+    $this->assertXPathContentContains('//h1', 'Annecy');
+  }
+
+
+  /** @test */
+  public function controllerShouldBeBib() {
+    $this->assertController('bib');
+  }
+
+
+  /** @test */
+  public function actionShouldBeBibview() {
+    $this->assertAction('bibview');
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyByRewriteUrlWithLinkToProfilTest
+  extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $profil = $this->fixture('Class_Profil',
+                             ['id' => 78,
+                              'libelle' => 'Bienvenue à Annecy']);
+
+    $this->bib_annecy->setLinkToProfil($profil)->assertSave();
+    $this->dispatch('annecy', true);
+  }
+
+
+  /** @test */
+  public function controllerShouldBeIndex() {
+    $this->assertController('index');
+  }
+
+
+  /** @test */
+  public function actionShouldBeBibview() {
+    $this->assertAction('index');
+  }
+
+
+  /** @test */
+  public function currentProfilShouldBeSeventyHeight() {
+    $this->assertEquals(78, Class_Profil::getCurrentProfil()->getId());
+  }
+
+}
+
+
+
+abstract class BibControllerLibraryWithOpeningsTestCase extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-07-01 08:08:42'));
+
+    $admin_bib = $this->fixture('Class_Users',
+                                ['id' => 54,
+                                 'login' => 'admin bib',
+                                 'password' => 'popup',
+                                 'id_site' => 4]);
+    $admin_bib->beAdminBib();
+    ZendAfi_Auth::getInstance()->logUser($admin_bib);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 1,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'label' => 'D\'habitude']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 2,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'validity_start' => '2016-06-30',
+                    'validity_end' => '2016-08-31',
+                    'label' => 'Horaires d\'été']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 3,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::MARDI,
+                    'validity_start' => '2016-06-30',
+                    'validity_end' => '2016-08-31',
+                    'label' => 'I will not be displayed']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 4,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::MERCREDI,
+                    'validity_start' => '2016-06-30',
+                    'validity_end' => '2016-08-31',
+                    'label' => 'Me neither']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 5,
+                    'id_site' => 4,
+                    'jour' => '2016-07-14',
+                    'label' => 'C\'est la teuff']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 6,
+                    'id_site' => 4,
+                    'jour' => '2016-07-15',
+                    'debut_matin' => '00:00',
+                    'fin_matin' => '00:00',
+                    'debut_apres_midi' => '00:00',
+                    'fin_apres_midi' => '00:00',
+                    'label' => 'Repos']);
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyRangeOpeningsTest extends BibControllerLibraryWithOpeningsTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  public function tearDown() {
+    Class_Ouverture_Visitor::setTimeSource(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldContainsDefault() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'D\'habitude',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function shouldContainsHorairesDEte() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'Horaires d\'été',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsExceptionnalLabelTeuff() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                      'C\'est la teuff ');
+  }
+
+
+  /** @test */
+  public function openingsShouldContainsExceptionnalLabelRepos() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
+                                      'Repos ');
+  }
+
+
+  /** @test */
+  public function editBibShouldBePresent() {
+    $this->assertXPath('//a[contains(@href,"admin/bib/edit/id/4")]');
+  }
+
+
+  /** @test */
+  public function editOpeningsShouldBePresent() {
+    $this->assertXPath('//a[contains(@href, "admin/ouvertures/index/id_site/4")]');
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyWithOutdatedRangeOpeningsTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-10-25 16:08:42'));
+
+    $admin_bib = $this->fixture('Class_Users',
+                                ['id' => 54,
+                                 'login' => 'admin bib',
+                                 'password' => 'popup',
+                                 'id_site' => 4]);
+    $admin_bib->beAdminBib();
+    ZendAfi_Auth::getInstance()->logUser($admin_bib);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 1,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'label' => 'D\'habitude']);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 2,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'validity_start' => '2016-06-30',
+                    'validity_end' => '2016-08-31',
+                    'label' => 'Horaires d\'été']);
+
+
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  public function tearDown() {
+    Class_Ouverture_Visitor::setTimeSource(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldContainsDefault() {
+    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                      'D\'habitude');
+  }
+
+
+  /** @test */
+  public function shouldNotContainsHorairesDEte() {
+    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                         'Horaires d\'été',
+                                         $this->_response->getBody());
+  }
+}
+
+
+
+class BibControllerBibViewAnnecyFreeTextOpeningsTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-07-01 08:08:42'));
+
+    $admin_bib = $this->fixture('Class_Users',
+                                ['id' => 54,
+                                 'login' => 'admin bib',
+                                 'password' => 'popup',
+                                 'id_site' => 4]);
+    $admin_bib->beAdminBib();
+    ZendAfi_Auth::getInstance()->logUser($admin_bib);
+
+    $this->fixture('Class_Ouverture',
+                   ['id' => 1,
+                    'id_site' => 4,
+                    'jour_semaine' => Class_Ouverture::LUNDI,
+                    'label' => 'D\'habitude']);
+
+    $this->bib_annecy
+      ->setHoraire('Fermé pour travaux')
+      ->assertSave();
+
+    $this->dispatch('bib/bibview/id/4', true);
+  }
+
+
+  public function tearDown() {
+    Class_Ouverture_Visitor::setTimeSource(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldNotContainsDefaultOpenings() {
+    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
+                                         'D\'habitude',
+                                         $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function editOpeningsShouldNotBePresent() {
+    $this->assertNotXPath('//a[contains(@href, "admin/ouvertures/index/id_site/4")]');
+  }
+}
+
+
+
+class BibControllerBibViewBackUrlTest extends BibControllerBibViewTestCase {
+
+  public function setUp() {
+    parent::setUp();
+    $_SERVER['HTTP_REFERER'] = '/link/to/referer';
+  }
+
+
+  /** @test */
+  public function backUrlShouldBeZoneView() {
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertXPathContentContains('//div[@class="back_link"]/a[contains(@href, "zoneview/id/1")]', 'Retour');
+  }
+
+
+  /** @test */
+  public function backUrlShouldBeReferer() {
+    $this->dispatch('bib/bibview/id/22', true);
+    $this->assertXPathContentContains('//div[@class="back_link"]/a[contains(@href, "/link/to/referer")]', 'Retour');
+  }
+}
+
+
+
+class BibControllerBibViewTextCustomFieldsTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->custom_field = $this->fixture('Class_CustomField',
+                                          ['id' => 5,
+                                           'priority' => 3,
+                                           'field_type' => Class_CustomField_Meta::TEXT_INPUT,
+                                           'label' => 'Lieu',
+                                           'model' => 'Bib']);
+  }
+
+
+  /** @test */
+  public function customFieldLieuShouldBeDisplayed() {
+    $this->bib_annecy->setCustomField('Lieu', 'Far Far Away');
+    $this->bib_annecy->saveWithCustomFields();
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertXPathContentContains("//div//dt", "Lieu", $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function customFieldValueShouldBeFarFarAway() {
+    $this->bib_annecy->setCustomField('Lieu', 'Far Far Away');
+    $this->bib_annecy->saveWithCustomFields();
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertXPathContentContains("//div//dd", "Far Far Away", $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function customFieldLieuShouldNotBeDisplayed() {
+    $this->bib_annecy->setCustomField('Lieu', '');
+    $this->bib_annecy->saveWithCustomFields();
+    $this->dispatch('bib/bibview/id/4', true);
+    $this->assertNotXPathContentContains("//div//dd", "Lieu", $this->_response->getBody());
+  }
+}
+
+
+
+
+class BibControllerBibViewMultiCheckBoxCustomFieldsTest extends BibControllerBibViewTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->custom_field = $this->fixture('Class_CustomField',
+                                          ['id' => 2,
+                                           'priority' => 3,
+                                           'field_type' => Class_CustomField_Meta::SELECT,
+                                           'label' => 'Mode',
+                                           'options_list' => 'Expert;Novice',
+                                           'model' => 'Bib']);
+
+
+    $this->custom_field = $this->fixture('Class_CustomField',
+                                          ['id' => 12,
+                                           'priority' => 3,
+                                           'label' => 'Services',
+                                           'options_list' => 'Wifi;Projection;Restauration;l\'Médias',
+                                           'field_type' => Class_CustomField_Meta::MULTI_CHECKBOX,
+                                           'model' => 'Bib']);
+
+
+
+
+    $this->bib_annecy->setCustomField('Services', ['Wifi', 'Restauration','l\'Médias']);
+    $this->bib_annecy->saveWithCustomFields();
+    $this->dispatch('bib/bibview/id/4', true);
+
+  }
+
+
+  /** @test */
+  public function customFieldServicesShouldBeDisplayed() {
+    $this->assertXPathContentContains("//div//h2", "Services");
+  }
+
+
+  /** @test */
+  public function servicesShouldContainsWifi() {
+    $this->assertXPathContentContains('//div[@class="custom1 library_box"]//dd', 'Wifi',$this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function servicesShouldContainsRestauration() {
+    $this->assertXPathContentContains('//div//dd[@class="restauration"]', 'Restauration',$this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function classShouldBeNameWithoutAccents() {
+    $this->assertXPathContentContains('//div//dd[@class="l_medias"]', 'l\'Médias',$this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function servicesShouldNotContainsProjection() {
+    $this->assertNotXPathContentContains('//div//dd', 'Projection');
+  }
+
+
+  /** @test */
+  public function servicesShouldNotContainsSeparator() {
+    $this->assertNotXPathContentContains('//div//dd', 'Wifi;Restauration');
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyWithParamRetourHistoriqueTest extends BibControllerBibViewTestCase {
+  /** @test */
+  public function withUrlRetourCmsArticleViewFiveShouldBeAccepted() {
+    $this->dispatch('bib/bibview/id/4?retour=cms+articleview+5');
+    $this->assertXPathContentContains('//a[contains(@href, "/cms/articleview/id/5")]',
+                                      'Retour');
+  }
+
+  /** @test */
+  public function withUrlRetourCmsOnlyShouldNotBeAccepted() {
+    $this->dispatch('bib/bibview/id/4?retour=cms');
+    $this->assertXPathContentContains('//a[contains(@href, "/bib/zoneview/id/1")]',
+                                      'Retour');
+  }
+}
+
+
+
+
+class BibControllerBibViewAnnecyEditActionConfigTest extends BibControllerBibViewTestCase {
+  /** @test */
+  public function editActionConfigShouldBePresent() {
+    $this->dispatch('/bib/bibview/id/4');
+    $this->assertXPath('//a[contains(@href, "/admin/modules/bib/config/site/type_module/bib")]'
+                       . '[contains(@href, "/action1/bibview")]');
+  }
+}
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/BibControllerTest.php b/tests/application/modules/opac/controllers/BibControllerTest.php
index 86cab05b7aff7b736225d1d643dafd8a2a4b4fb8..1dc49767ea688db923cde0c32c6f76b43f76c179 100644
--- a/tests/application/modules/opac/controllers/BibControllerTest.php
+++ b/tests/application/modules/opac/controllers/BibControllerTest.php
@@ -366,573 +366,6 @@ class BibControllerMapZoneViewWithoutProfilTest extends BibControllerMapViewTest
 
 
 
-abstract class BibControllerBibViewTestCase extends BibControllerWithZoneTestCase {
-  protected
-    $_mock_sql,
-    $_storm_default_to_volatile = true;
-
-  public function setUp() {
-    parent::setUp();
-
-    $this->_mock_sql = Storm_Test_ObjectWrapper::mock();
-    Zend_Registry::set('sql', $this->_mock_sql);
-
-    $this->onLoaderOfModel('Class_Exemplaire')
-      ->whenCalled('countBy')
-      ->with(['model' => $this->bib_annecy, 'role' => 'bib'])
-      ->answers(20)
-
-      ->whenCalled('countBy')
-      ->with(['model' => $this->bib_nozone, 'role' => 'bib'])
-      ->answers(20)
-
-      ->whenCalled('countBy')
-      ->with([])
-      ->answers(20)
-
-      ->beStrict();
-
-    $this->_mock_sql
-      ->whenCalled('fetchOne')
-      ->answers('');
-  }
-}
-
-
-
-
-class BibControllerBibViewOnlyAfternoonAnnecyTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-    ZendAfi_Auth::getInstance()->clearIdentity();
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-06-24 08:08:42'));
-
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 21,
-                    'id_site' => 4,
-                    'label' => 'Horaires réduits',
-                    'jour_semaine' => Class_Ouverture::MARDI,
-                    'debut_matin' => '00:00',
-                    'fin_matin' => '00:00',
-                    'debut_apres_midi' => '15:00',
-                    'fin_apres_midi' => '17:00',
-                   ]);
-
-
-    Class_bib::find(4)
-      ->setHoraire('  ');
-
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  public function tearDown() {
-    Class_Ouverture_Visitor::setTimeSource(null);
-    parent::tearDown();
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsDefaultOpening() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//dd',
-                                      'Mardi : 15h - 17h', $this->_response->getBody());
-  }
-}
-
-
-
-
-class BibControllerBibViewAnnecyTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-    ZendAfi_Auth::getInstance()->clearIdentity();
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-06-24 08:08:42'));
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 1,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI]);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 20,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::MARDI]);
-
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 21,
-                    'id_site' => 4,
-                    'label' => 'Horaires réduits',
-                    'jour_semaine' => Class_Ouverture::MARDI,
-                    'debut_matin' => '00:00',
-                    'fin_matin' => '00:00',
-                    'debut_apres_midi' => '15:00',
-                    'fin_apres_midi' => '17:00',
-                    'validity_start' => '2016-07-01',
-                    'validity_end' => '2016-07-20']);
-
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 2,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'validity_start' => '2016-07-01',
-                    'validity_end' => '2016-08-31']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 3,
-                    'id_site' => 4,
-                    'jour' => '2016-07-01']);
-
-    // too far away
-    $this->fixture('Class_Ouverture',
-                   ['id' => 4,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'validity_start' => '2020-06-31',
-                    'validity_end' => '2020-08-31']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 5,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'validity_start' => '2016-07-02',
-                    'validity_end' => '2016-07-03']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 6,
-                    'id_site' => 4,
-                    'jour' => '2016-06-12']);
-
-    // past
-    $this->fixture('Class_Ouverture',
-                   ['id' => 7,
-                    'id_site' => 4,
-                    'jour' => '2016-06-26',
-                    'debut_matin' => '00:00',
-                    'fin_matin' => '00:00',
-                    'debut_apres_midi' => '00:00',
-                    'fin_apres_midi' => '00:00']);
-
-    // multimedia dedicated
-    $this->fixture('Class_Ouverture',
-                   ['id' => 8,
-                    'id_site' => 4,
-                    'jour' => '2016-07-07',
-                    'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA]);
-
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  /** @test */
-  public function formRechercheShouldLinkToCurrentBib() {
-    $this->assertXPath('//form[contains(@action, "/recherche/simple/id/4/geo_bib/4")]');
-  }
-
-
-  /** @test */
-  public function fondsShouldDisplayTousLesDocuments() {
-    $this->assertXPathContentContains('//div[@class="library_fonds library_box"]/dl[preceding-sibling::h2[text()="Fonds"]]/dd',
-                                      'Tous les documents');
-  }
-
-
-  /** @test */
-  public function adresseShouldBe1RueJeanJaures() {
-    $this->assertXPathContentContains('//dd', '1 rue Jean Jaures');
-  }
-
-
-  /** @test */
-  public function photoBibShouldBeDisplayedWithClassBibviewImage() {
-    $this->assertXPath('//img[@class="bibview_image"][@src="'. BASE_URL .'/userfiles/photobib/photobib4.jpg"]');
-  }
-
-
-  /** @test */
-  public function articleEcrivezDesTestsShouldBeVisible() {
-    $this->assertXPathContentContains('//article//header//a[contains(@href, "/cms/articleview/id/2")]',
-                                      'Ecrivez des tests !');
-  }
-
-
-  /** @test */
-  public function articleEcrivezDesTestsShouldContainsHeaderSpan() {
-    $this->assertXPathContentContains('//article//header//span',
-                                      'Annecy');
-  }
-
-
-  /** @test */
-  public function libelleCatBonnesPratiquesShouldBeVisible() {
-    $this->assertXPathContentContains('//h2',
-                                      'Bonnes pratiques');
-  }
-
-
-  /** @test */
-  public function urlRetourShouldBeZoneViewIdOne() {
-    $this->assertXPathContentContains('//a[contains(@href, "/bib/zoneview/id/1")]',
-                                      'Retour');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsDefaultOpening() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//dd',
-                                      'Lundi : 10h - 18h');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsPeriodLabel() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'du vendredi 01 juillet au mercredi 31 août 2016');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsSecondPeriodLabel() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'du samedi 02 au dimanche 03 juillet 2016');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsHorairesReduits() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'Horaires réduits du vendredi 01 au mercredi 20 juillet 2016');
-  }
-
-
-  /** @test */
-  public function openingsShouldNotContainsTooFarPeriodLabel() {
-    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                         'du mercredi 01 juillet au lundi 31 août 2020');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsExceptionnalLabel() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'Ouverture exceptionnelle');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsExceptionnalDate() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                      'le vendredi 01 juillet : 10h - 18h');
-  }
-
-
-  /** @test */
-  public function openingsShouldNotContainsMultimediaDate() {
-    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                         'le jeudi 07 juillet : 10h - 18h');
-  }
-
-
-  /** @test */
-  public function openingsShouldNotContainsPastExceptionnalDate() {
-    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                         'le dimanche 12 juin : 10h - 18h');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsClosureLabel() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'Fermeture exceptionnelle');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsClosureDate() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                      'le dimanche 26 juin');
-  }
-
-
-  /** @test */
-  public function editBibShouldNotBePresent() {
-    $this->assertNotXPath('//a[contains(@href, "/admin/bib/edit/id/")]');
-  }
-
-
-  /** @test */
-  public function editOpeningsShouldNotBePresent() {
-    $this->assertNotXPath('//a[contains(@href, "/admin/ouvertures/index/id_site/")]');
-  }
-}
-
-
-
-
-class ZendAfi_View_Helper_RenderLibraryOpeningClosedOnHolidaysTestTest extends BibControllerBibViewTestCase {
-
-  public function setUp() {
-    parent::setUp();
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2018-12-14 11:00:00'));
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 200,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'label' => 'Horaires habituels']);
-
-    foreach(range(1, 7) as $day)
-      $this->fixture('Class_Ouverture',
-                     ['id' => 400 + $day,
-                      'horaires' => ['00:00', '00:00', '00:00', '00:00'],
-                      'jour' => '',
-                      'id_site' => 4,
-                      'validity_start' => '2018-12-23',
-                      'validity_end' => '2019-01-01',
-                      'jour_semaine' => $day]);
-  }
-
-
-  /** @test */
-  public function firstOpeningsH3ShouldContainsHorairesHabituels() {
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertXPathContentContains('//li[1]/h3',
-                                      'Horaires habituels');
-  }
-
-
-  /** @test */
-  public function secondOpeningsH3ShouldContainsFermeturesVacances() {
-    Class_Ouverture::find(401)->setLabel('Fermetures vacances scolaires');
-    $this->dispatch('bib/bibview/id/4', true);
-
-    $this->assertXPathContentContains('//li[2]/h3',
-                                      'Fermetures vacances scolaires du dimanche 23 décembre 2018 au mardi 01 janvier 2019');
-  }
-
-
-  /** @test */
-  public function withoutLabelSecondOpeningsH3ShouldContainsFermetureExceptionnelle() {
-    $this->dispatch('bib/bibview/id/4', true);
-
-    $this->assertXPathContentContains('//li[2]/h3',
-                                      'Fermeture exceptionnelle du dimanche 23 décembre 2018 au mardi 01 janvier 2019');
-  }
-
-
-  /** @test */
-  public function secondOpeningsShouldNotContainsLundi() {
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertNotXPathContentContains('//li[2]//li',
-                                         'Lundi');
-  }
-}
-
-
-
-class BibControllerBibViewAnnecyHideNewsTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-    Class_Profil::getCurrentProfil()
-      ->setModulePreference('bib', 'bibview', 'hide_news', 1);
-
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  /** @test */
-  public function articleEcrivezDesTestsShouldNotBeVisible() {
-    $this->assertNotXPathContentContains('//article//header//a[contains(@href, "/cms/articleview/id/2")]',
-                                         'Ecrivez des tests !');
-  }
-}
-
-
-
-
-class BibControllerBibViewAnnecyByRewriteUrlTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-    $this->dispatch('annecy', true);
-  }
-
-
-  /** @test */
-  public function h1ShouldContainsAnnecy() {
-    $this->assertXPathContentContains('//h1', 'Annecy');
-  }
-
-
-  /** @test */
-  public function controllerShouldBeBib() {
-    $this->assertController('bib');
-  }
-
-
-  /** @test */
-  public function actionShouldBeBibview() {
-    $this->assertAction('bibview');
-  }
-}
-
-
-
-
-class BibControllerBibViewAnnecyByRewriteUrlWithLinkToProfilTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-    $profil = $this->fixture('Class_Profil',
-                             ['id' => 78,
-                              'libelle' => 'Bienvenue à Annecy']);
-
-    $this->bib_annecy->setLinkToProfil($profil)->assertSave();
-    $this->dispatch('annecy', true);
-  }
-
-
-  /** @test */
-  public function controllerShouldBeIndex() {
-    $this->assertController('index');
-  }
-
-
-  /** @test */
-  public function actionShouldBeBibview() {
-    $this->assertAction('index');
-  }
-
-
-  /** @test */
-  public function currentProfilShouldBeSeventyHeight() {
-    $this->assertEquals(78, Class_Profil::getCurrentProfil()->getId());
-  }
-
-}
-
-
-
-abstract class BibControllerLibraryWithOpeningsTestCase extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-07-01 08:08:42'));
-
-    $admin_bib = $this->fixture('Class_Users',
-                                ['id' => 54,
-                                 'login' => 'admin bib',
-                                 'password' => 'popup',
-                                 'id_site' => 4]);
-    $admin_bib->beAdminBib();
-    ZendAfi_Auth::getInstance()->logUser($admin_bib);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 1,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'label' => 'D\'habitude']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 2,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'validity_start' => '2016-06-30',
-                    'validity_end' => '2016-08-31',
-                    'label' => 'Horaires d\'été']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 3,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::MARDI,
-                    'validity_start' => '2016-06-30',
-                    'validity_end' => '2016-08-31',
-                    'label' => 'I will not be displayed']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 4,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::MERCREDI,
-                    'validity_start' => '2016-06-30',
-                    'validity_end' => '2016-08-31',
-                    'label' => 'Me neither']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 5,
-                    'id_site' => 4,
-                    'jour' => '2016-07-14',
-                    'label' => 'C\'est la teuff']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 6,
-                    'id_site' => 4,
-                    'jour' => '2016-07-15',
-                    'debut_matin' => '00:00',
-                    'fin_matin' => '00:00',
-                    'debut_apres_midi' => '00:00',
-                    'fin_apres_midi' => '00:00',
-                    'label' => 'Repos']);
-  }
-}
-
-
-
-
-class BibControllerBibViewAnnecyRangeOpeningsTest extends BibControllerLibraryWithOpeningsTestCase {
-  public function setUp() {
-    parent::setUp();
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  public function tearDown() {
-    Class_Ouverture_Visitor::setTimeSource(null);
-    parent::tearDown();
-  }
-
-
-  /** @test */
-  public function shouldContainsDefault() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'D\'habitude',
-                                      $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function shouldContainsHorairesDEte() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'Horaires d\'été',
-                                      $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsExceptionnalLabelTeuff() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                      'C\'est la teuff ');
-  }
-
-
-  /** @test */
-  public function openingsShouldContainsExceptionnalLabelRepos() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//li',
-                                      'Repos ');
-  }
-
-
-  /** @test */
-  public function editBibShouldBePresent() {
-    $this->assertXPath('//a[contains(@href,"admin/bib/edit/id/4")]');
-  }
-
-
-  /** @test */
-  public function editOpeningsShouldBePresent() {
-    $this->assertXPath('//a[contains(@href, "admin/ouvertures/index/id_site/4")]');
-  }
-}
 
 
 
@@ -1069,284 +502,6 @@ class BibControllerOpeningHoursAnnecyTest extends BibControllerWithZoneTestCase
 
 
 
-class BibControllerBibViewAnnecyWithOutdatedRangeOpeningsTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-10-25 16:08:42'));
-
-    $admin_bib = $this->fixture('Class_Users',
-                                ['id' => 54,
-                                 'login' => 'admin bib',
-                                 'password' => 'popup',
-                                 'id_site' => 4]);
-    $admin_bib->beAdminBib();
-    ZendAfi_Auth::getInstance()->logUser($admin_bib);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 1,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'label' => 'D\'habitude']);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 2,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'validity_start' => '2016-06-30',
-                    'validity_end' => '2016-08-31',
-                    'label' => 'Horaires d\'été']);
-
-
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  public function tearDown() {
-    Class_Ouverture_Visitor::setTimeSource(null);
-    parent::tearDown();
-  }
-
-
-  /** @test */
-  public function shouldContainsDefault() {
-    $this->assertXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                      'D\'habitude');
-  }
-
-
-  /** @test */
-  public function shouldNotContainsHorairesDEte() {
-    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                         'Horaires d\'été',
-                                         $this->_response->getBody());
-  }
-}
-
-
-
-class BibControllerBibViewAnnecyFreeTextOpeningsTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-    Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2016-07-01 08:08:42'));
-
-    $admin_bib = $this->fixture('Class_Users',
-                                ['id' => 54,
-                                 'login' => 'admin bib',
-                                 'password' => 'popup',
-                                 'id_site' => 4]);
-    $admin_bib->beAdminBib();
-    ZendAfi_Auth::getInstance()->logUser($admin_bib);
-
-    $this->fixture('Class_Ouverture',
-                   ['id' => 1,
-                    'id_site' => 4,
-                    'jour_semaine' => Class_Ouverture::LUNDI,
-                    'label' => 'D\'habitude']);
-
-    $this->bib_annecy
-      ->setHoraire('Fermé pour travaux')
-      ->assertSave();
-
-    $this->dispatch('bib/bibview/id/4', true);
-  }
-
-
-  public function tearDown() {
-    Class_Ouverture_Visitor::setTimeSource(null);
-    parent::tearDown();
-  }
-
-
-  /** @test */
-  public function shouldNotContainsDefaultOpenings() {
-    $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//h3',
-                                         'D\'habitude',
-                                         $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function editOpeningsShouldNotBePresent() {
-    $this->assertNotXPath('//a[contains(@href, "admin/ouvertures/index/id_site/4")]');
-  }
-}
-
-
-
-class BibControllerBackUrlTest extends BibControllerBibViewTestCase {
-
-  public function setUp() {
-    parent::setUp();
-
-    $_SERVER['HTTP_REFERER'] = '/link/to/referer';
-  }
-
-
-  /** @test */
-  public function backUrlShouldBeZoneView() {
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertXPathContentContains('//div[@class="back_link"]/a[contains(@href, "zoneview/id/1")]', 'Retour');
-  }
-
-
-  /** @test */
-  public function backUrlShouldBeReferer() {
-    $this->dispatch('bib/bibview/id/22', true);
-    $this->assertXPathContentContains('//div[@class="back_link"]/a[contains(@href, "/link/to/referer")]', 'Retour');
-  }
-
-}
-
-
-
-class BibControllerTextCustomFieldsTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-
-    $this->custom_field = $this->fixture('Class_CustomField',
-                                          ['id' => 5,
-                                           'priority' => 3,
-                                           'field_type' => Class_CustomField_Meta::TEXT_INPUT,
-                                           'label' => 'Lieu',
-                                           'model' => 'Bib']);
-  }
-
-
-  /** @test */
-  public function customFieldLieuShouldBeDisplayed() {
-    $this->bib_annecy->setCustomField('Lieu', 'Far Far Away');
-    $this->bib_annecy->saveWithCustomFields();
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertXPathContentContains("//div//dt", "Lieu", $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function customFieldValueShouldBeFarFarAway() {
-    $this->bib_annecy->setCustomField('Lieu', 'Far Far Away');
-    $this->bib_annecy->saveWithCustomFields();
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertXPathContentContains("//div//dd", "Far Far Away", $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function customFieldLieuShouldNotBeDisplayed() {
-    $this->bib_annecy->setCustomField('Lieu', '');
-    $this->bib_annecy->saveWithCustomFields();
-    $this->dispatch('bib/bibview/id/4', true);
-    $this->assertNotXPathContentContains("//div//dd", "Lieu", $this->_response->getBody());
-  }
-}
-
-
-
-
-class BibControllerMultiCheckBoxCustomFieldsTest extends BibControllerBibViewTestCase {
-  public function setUp() {
-    parent::setUp();
-
-
-    $this->custom_field = $this->fixture('Class_CustomField',
-                                          ['id' => 2,
-                                           'priority' => 3,
-                                           'field_type' => Class_CustomField_Meta::SELECT,
-                                           'label' => 'Mode',
-                                           'options_list' => 'Expert;Novice',
-                                           'model' => 'Bib']);
-
-
-    $this->custom_field = $this->fixture('Class_CustomField',
-                                          ['id' => 12,
-                                           'priority' => 3,
-                                           'label' => 'Services',
-                                           'options_list' => 'Wifi;Projection;Restauration;l\'Médias',
-                                           'field_type' => Class_CustomField_Meta::MULTI_CHECKBOX,
-                                           'model' => 'Bib']);
-
-
-
-
-    $this->bib_annecy->setCustomField('Services', ['Wifi', 'Restauration','l\'Médias']);
-    $this->bib_annecy->saveWithCustomFields();
-    $this->dispatch('bib/bibview/id/4', true);
-
-  }
-
-
-  /** @test */
-  public function customFieldServicesShouldBeDisplayed() {
-    $this->assertXPathContentContains("//div//h2", "Services");
-  }
-
-
-  /** @test */
-  public function servicesShouldContainsWifi() {
-    $this->assertXPathContentContains('//div[@class="custom1 library_box"]//dd', 'Wifi',$this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function servicesShouldContainsRestauration() {
-    $this->assertXPathContentContains('//div//dd[@class="restauration"]', 'Restauration',$this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function classShouldBeNameWithoutAccents() {
-    $this->assertXPathContentContains('//div//dd[@class="l_medias"]', 'l\'Médias',$this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function servicesShouldNotContainsProjection() {
-    $this->assertNotXPathContentContains('//div//dd', 'Projection');
-  }
-
-
-  /** @test */
-  public function servicesShouldNotContainsSeparator() {
-    $this->assertNotXPathContentContains('//div//dd', 'Wifi;Restauration');
-  }
-}
-
-
-
-class BibControllerBibViewAnnecyWithParamRetourHistoriqueTest extends BibControllerWithZoneTestCase {
-  /** @test */
-  public function withUrlRetourCmsArticleViewFiveShouldBeAccepted() {
-    $this->dispatch('bib/bibview/id/4?retour=cms+articleview+5');
-    $this->assertXPathContentContains('//a[contains(@href, "/cms/articleview/id/5")]',
-                                      'Retour');
-  }
-
-  /** @test */
-  public function withUrlRetourCmsOnlyShouldNotBeAccepted() {
-    $this->dispatch('bib/bibview/id/4?retour=cms');
-    $this->assertXPathContentContains('//a[contains(@href, "/bib/zoneview/id/1")]',
-                                      'Retour');
-  }
-}
-
-
-class BibControllerBibViewInexistantTest extends BibControllerWithZoneTestCase {
-  public function setUp() {
-    parent::setUp();
-
-    $this->dispatch('bib/bibview/');
-  }
-
-  /** @test */
-  public function responseShouldRedirectToIndex() {
-    $this->assertRedirectTo('/opac/bib/index');
-  }
-}
-
-
-
 abstract class BibControllerSelectionTestCase extends AbstractControllerTestCase {
   protected
     $_storm_default_to_volatile = true,
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
index cb78590d048e37aafe35510a76ff1ce1e2e5c475..ba10a34b400e3f33928d17c21044698d1450c4a2 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
@@ -218,6 +218,29 @@ class NoticeAjaxControllerItemsWithoutOrderTest
 
 
 
+/** @see https://forge.afi-sa.net/issues/137828 */
+class NoticeAjaxControllerItemsWithEmptyDatasConfig extends NoticeAjaxControllerItemsTestCase {
+
+  protected function _prepareFixtures() {
+    $config = Class_Profil::getCurrentProfil()->getCfgNoticeAsArray();
+
+    $config['exemplaires']['grouper'] = '1';
+    $config['exemplaires']['datas_items_code'] = [''];
+    $config['exemplaires']['datas_items_label'] = [''];
+
+    Class_Profil::getCurrentProfil()->setCfgNotice($config);
+  }
+
+
+  /** @test */
+  public function itemsTableShouldNotContainsEmptySubfield() {
+    $this->assertNotXPath('//td[@class="subfield_"]');
+  }
+}
+
+
+
+
 class NoticeAjaxControllerItemsWithOrder995Dollar9AscTest
   extends NoticeAjaxControllerItemsTestCase {
 
diff --git a/tests/application/modules/opac/controllers/RechercheControllerThumbnailTest.php b/tests/application/modules/opac/controllers/RechercheControllerThumbnailTest.php
index b4619a9e082c6ca77f8fe067d7ea8d6ee9d943df..ffbf11850cb98f2f04bb384d8f12f09bcf4a8410 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerThumbnailTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerThumbnailTest.php
@@ -50,18 +50,30 @@ abstract class RechercheControllerThumbnailTestCase extends AbstractControllerTe
 
 
 
-class RechercheControllerThumbnailCmsTest extends RechercheControllerThumbnailTestCase {
+
+class RechercheControllerThumbnailCmsTest
+  extends RechercheControllerThumbnailTestCase {
 
   public function setUp() {
     parent::setUp();
 
-    $article = $this->fixture('Class_Article',
+    Class_Article::setFileWriter($this->mock()
+                                 ->whenCalled('fileExists')
+                                 ->answers(true));
+
+    $article = $this->fixture(Class_Article::class,
                               ['id' => 123,
                                'titre' => 'An article with img',
                                'contenu' => 'This article should contains an image like this <img src="/images/articles/img.png" />']);
+
     $article->index();
+    $this->dispatch('recherche/vignette/id_notice/1');
+  }
 
-    $this->dispatch('recherche/vignette/id_notice/1', true);
+
+  public function tearDown() {
+    Class_Article::setFileWriter(null);
+    parent::tearDown();
   }
 
 
@@ -75,7 +87,6 @@ class RechercheControllerThumbnailCmsTest extends RechercheControllerThumbnailTe
 
 
 
-
 class RechercheControllerThumbnailAlbumCmsWithImageTest
   extends RechercheControllerThumbnailTestCase {
 
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 8088a7a1df43e249f71cce624325af90c09325a8..ac8f4cf921c5a6cb5ee672eb6c294aa8a9c6c6d3 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -3930,3 +3930,29 @@ class UpgradeDB_413_Test extends UpgradeDBTestCase {
     $this->assertFieldType('album', 'interet', 'text');
   }
 }
+
+
+
+
+class UpgradeDB_414_Test extends UpgradeDBTestCase {
+
+  public function prepare() {
+    $this->query('replace into bib_admin_var (clef, valeur) values ("STATUS_REPORT_PUSH_URL", "http://pola.afi-sa.net/smile.php")');
+  }
+
+
+  public function tearDown() {
+    $this->query('replace into bib_admin_var (clef, valeur) values ("STATUS_REPORT_PUSH_URL", "no")');
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function statusReportPushUrlShouldBeHttps() {
+    $this->assertEquals('https://pola.afi-sa.net/smile.php',
+                        current($this
+                                ->query('select valeur from bib_admin_var where clef="STATUS_REPORT_PUSH_URL"')
+                                ->fetch()));
+
+  }
+}
diff --git a/tests/library/Class/Cosmogramme/Integration/PhaseEndProcessTest.php b/tests/library/Class/Cosmogramme/Integration/PhaseEndProcessTest.php
index d3d375eb2f9794f55f8770cc23ffc2e2727f8bc8..6e229ee263274e8619277381376ffaf4555886b3 100644
--- a/tests/library/Class/Cosmogramme/Integration/PhaseEndProcessTest.php
+++ b/tests/library/Class/Cosmogramme/Integration/PhaseEndProcessTest.php
@@ -49,7 +49,7 @@ class PhaseEndProcessTest extends Class_Cosmogramme_Integration_PhaseTestCase {
   /** @test */
   public function reportShouldBePushedToPola() {
     $this->_phase = $this->_buildPhase('EndProcess')->run();
-    $this->assertEquals('http://pola.afi-sa.net/smile.php',
+    $this->assertEquals('https://pola.afi-sa.net/smile.php',
                         $this->_http_client->getFirstAttributeForLastCallOn('postData'));
   }
 
@@ -100,7 +100,7 @@ class PhaseEndProcessTest extends Class_Cosmogramme_Integration_PhaseTestCase {
       ->willDo(function() { throw new RunTimeException('no more paper'); });
 
     $this->_phase = $this->_buildPhase('EndProcess')->run();
-    $this->assertLogContains('Echec de la publication du rapport sur http://pola.afi-sa.net/smile.php (no more paper)');
+    $this->assertLogContains('Echec de la publication du rapport sur https://pola.afi-sa.net/smile.php (no more paper)');
   }
 }
 ?>
\ No newline at end of file
diff --git a/tests/library/Class/Cosmogramme/Integration/PhaseNoticeLomTest.php b/tests/library/Class/Cosmogramme/Integration/PhaseNoticeLomTest.php
index 5bf57c4c8be3f80bbb932de1d7261119287b5317..74e2ebd2d16b81bbc503d73051ce876979c9a664 100644
--- a/tests/library/Class/Cosmogramme/Integration/PhaseNoticeLomTest.php
+++ b/tests/library/Class/Cosmogramme/Integration/PhaseNoticeLomTest.php
@@ -215,7 +215,7 @@ class PhaseNoticeLomTest extends PhaseNoticeTestCase {
     $this->assertEquals(['Name' => 'Anne Teyssèdre',
                          'Responsibility' => 'author',
                          'Affiliation' => 'Museum National d\'Histoire Naturelle'],
-                        $author->toArray());
+                        $author);
   }
 
 
diff --git a/tests/scenarios/Authorities/AuthoritiesTest.php b/tests/scenarios/Authorities/AuthoritiesTest.php
index ff4bfc7a9408bb0b308f1256dbeb5fac5281033c..50c2e13e97b07ed26241b140037876acef422c99 100644
--- a/tests/scenarios/Authorities/AuthoritiesTest.php
+++ b/tests/scenarios/Authorities/AuthoritiesTest.php
@@ -886,3 +886,152 @@ class AuthoritiesFacetsWithIndexSystemThesaurusNotExistingTest
     $this->assertFalse($this->_thesaurus_loader->methodHasBeenCalled('save'));
   }
 }
+
+
+
+
+/* hotline: #133782 */
+abstract class AuthoritiesThesaurusWithFilterFieldTestCase extends ModelTestCase {
+
+  protected $_thesaurus;
+
+  public function setUp() {
+    parent::setUp();
+
+    Class_CodifThesaurus::clearCache();
+    $this->_createThesaurus();
+
+    $attributs = [[Class_IntProfilDonnees::PROFILE_INDEX_SYSTEMS_FIELDS
+                   => [['rule' => '',
+                        'system' => 'TESS',
+                        'default_type' => 'j',
+                        'thesaurus' => '6']]]];
+
+    $this->fixture(Class_IntProfilDonnees::class,
+                   ['id' => 1004,
+                    'libelle' => 'Authorities',
+                    'type_fichier' => Class_IntProfilDonnees::FT_AUTHORITY,
+                    'format' => Class_IntProfilDonnees::FORMAT_UNIMARC,
+                    'accents' => Class_IntProfilDonnees::ENCODING_UTF8,
+                    'attributs' => serialize($attributs),
+                   ]);
+
+    $this->fixture(Class_Notice::class,
+                   ['id' => 14,
+                    'type' => Class_Notice::TYPE_AUTHORITY,
+                    'type_doc' => 'j',
+                    'facettes' => '']);
+
+    $this->fixture(Class_Exemplaire::class,
+                   ['id' => 145,
+                    'type' => Class_Notice::TYPE_AUTHORITY,
+                    'id_origine' => 213399,
+                    'id_int_bib' => 3,
+                    'notice' => Class_Notice::find(14)]);
+
+    $this->_thesaurus = (new Class_Cosmogramme_Integration_Record_BibliographicDynamicFacets())
+      ->thesauriOf((new Class_NoticeUnimarc())->setNotice(file_get_contents(__DIR__ . '/vagabond_filtre.mrc')),
+                   $this->fixture(Class_IntBib::class, ['id' => 3]));
+  }
+
+
+  protected function _createThesaurus() {
+  }
+}
+
+
+
+
+class AuthoritiesThesaurusWithSameLabelAndFilterFieldTest
+  extends AuthoritiesThesaurusWithFilterFieldTestCase {
+
+  protected function _createThesaurus() {
+    $this->fixture(Class_CodifThesaurus::class,
+                   ['id' => 6,
+                    'libelle' => 'Mots-clés TESS',
+                    'id_thesaurus' => 'MOTS',
+                    'id_origine' => null,
+                    'code' => 'MOTS',
+                    'rule_list_zone' => ['500', '619'],
+                    'rule_list_label_field' => ['b', 'a'],
+                    'rule_list_id_field' => ['', ''],
+                    'rule_list_filter_field' => ['b', 'a'],
+                    'rule_list_filter_value' => ['Titre vagabond', 'Ne pas prendre']
+                   ]);
+  }
+
+
+  /** @test */
+  public function numberOfThesaurusShouldBeOne() {
+    $this->assertEquals(1, count($this->_thesaurus));
+  }
+
+
+  /** @test */
+  public function thesaurusLabelShouldBeCodifFiltre() {
+    $this->assertEquals('Titre vagabond', $this->_thesaurus[0]->getLibelle());
+  }
+}
+
+
+
+
+class AuthoritiesThesaurusWithOneFilterAndTwoLabelMatchesTest
+  extends AuthoritiesThesaurusWithFilterFieldTestCase {
+
+  protected function _createThesaurus() {
+    $this->fixture(Class_CodifThesaurus::class,
+                   ['id' => 6,
+                    'libelle' => 'Mots-clés TESS',
+                    'id_thesaurus' => 'MOTS',
+                    'id_origine' => null,
+                    'code' => 'MOTS',
+                    'rule_list_zone' => ['510'],
+                    'rule_list_label_field' => ['b'],
+                    'rule_list_id_field' => [''],
+                    'rule_list_filter_field' => ['c'],
+                    'rule_list_filter_value' => ['Afiltrer']
+                   ]);
+  }
+
+
+  /** @test */
+  public function numberOfThesaurusShouldBeOne() {
+    $this->assertEquals(1, count($this->_thesaurus));
+  }
+
+
+  /** @test */
+  public function thesaurusLabelShouldBeTakehikoManga() {
+    $this->assertEquals('Takehiko manga', $this->_thesaurus[0]->getLibelle());
+  }
+}
+
+
+
+
+
+class AuthoritiesThesaurusWithTwoFilterAndOneLabelMatchesTest
+  extends AuthoritiesThesaurusWithFilterFieldTestCase {
+
+  protected function _createThesaurus() {
+    $this->fixture(Class_CodifThesaurus::class,
+                   ['id' => 6,
+                    'libelle' => 'Mots-clés TESS',
+                    'id_thesaurus' => 'MOTS',
+                    'id_origine' => null,
+                    'code' => 'MOTS',
+                    'rule_list_zone' => ['510'],
+                    'rule_list_label_field' => ['c'],
+                    'rule_list_id_field' => [''],
+                    'rule_list_filter_field' => ['b'],
+                    'rule_list_filter_value' => ['Takehiko']
+                   ]);
+  }
+
+
+  /** @test */
+  public function thesaurusShouldNotBeCreated() {
+    $this->assertEquals(0, count($this->_thesaurus));
+  }
+}
diff --git a/tests/scenarios/Authorities/vagabond_filtre.mrc b/tests/scenarios/Authorities/vagabond_filtre.mrc
new file mode 100644
index 0000000000000000000000000000000000000000..b8b7927b900b44722056f7fe13dcdad1ea237ddf
--- /dev/null
+++ b/tests/scenarios/Authorities/vagabond_filtre.mrc
@@ -0,0 +1 @@
+01645nam  2200409   4500001000400000003004700004010004700051020001700098021002000115039001900135090000800154091001900162099001300181100004100194101000800235102000700243105001800250106000600268200017600274210005600450215009700506461003300603500001900636510004500655610001000700619001800710619002300728619001100751676001000762700007200772702005000844702006100894702004000955801007000995930002901065995014101094869http://catalogue.bnf.fr/ark:/12148/cb37710769n  a2-84580-144-0bbr.dPrix : 59 F : 8,99 EUR  aFRb00201411  aFRbDL 01-54100  oOPLa027037083  a869  b20140822c1a2  eUZELtBD  a20020103d2001    m  y0frey50      ba1 afre  aFR  aa   t   00|a|  ar1 aVagabondbTexte impriméh4fTakehiko Inouégd'après l'oeuvre d'Eiji Yoshikawa, "Miyamoto Musashi"g[trad. du japonais par Jacques Lalloz]g[adapté par Philippe Marcel]  aPariscÉd. Tonkamd2001e35-RennesgImpr. Oberthur  aNon paginé [ca 206] p.cill. en noir et en coul., couv. ill., jaquette ill. en coul.d19 cm 0tVagabondv4aTakehiko Inoué  bTitre vagabond  bManga vagabondbTakehiko mangacAfiltrer  aManga  aManga9213399  a   	Cicatrice	978  a98839  a741.5  313527612935oISNI0000000121039965aInouebTakehikof1967-....4070  311929525936aYoshikawabEijif1892-19624100  312040286937oISNI0000000108815456aLallozbJacques4730  312320807938aMarcelbPhilippe4010 0aFRbFR-751131015c20020103gAFNORhFRBNF3771076900000002intermrc  5FR-751131010:2003-196584  fSTJ000095w2014-06-2630009117cMTRSTJ20kBD VAG 452012-09-07o0efiction adulte bande dessineesddcrBDm2014-06-19bMTRSTJ6MTRSTJ
\ No newline at end of file
diff --git a/tests/scenarios/Templates/TerreDuMilieuTemplateTest.php b/tests/scenarios/Templates/TerreDuMilieuTemplateTest.php
index 7a62baf25912de8511f6f5f82fe2048b272615c2..9b3321e925b4e5f2f1a8bfe6fdcc75845f4095f1 100644
--- a/tests/scenarios/Templates/TerreDuMilieuTemplateTest.php
+++ b/tests/scenarios/Templates/TerreDuMilieuTemplateTest.php
@@ -96,6 +96,12 @@ class TerreDuMilieuTemplateProfilePatcherTest extends TerreDuMilieuTemplateTestC
   public function editTemplateTerreDuMilieuShouldBePresent() {
     $this->assertXPathContentContains('//a[contains(@href, "admin/template/edit/template/TERREDUMILIEU")]', 'Configuration du thème');
   }
+
+
+  /** @test */
+  public function adminToolsWidgetShouldContainsFileManagerTargetBlank() {
+    $this->assertXPathContentContains('//div[contains(@class, "admin_tools")]//a[@target="_blank"][@href="/admin/file-manager/"]', 'Explorateur de fichiers');
+  }
 }