diff --git a/VERSIONS b/VERSIONS
index f1371b0eca1a4714a64885ab880b0a4fbc95e7b6..5f78ffb0f6eab06bba65fde4e056c20fa932cfa8 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,3 +1,34 @@
+19/04/2021 - v8.0.115
+
+ - ticket #125413 : Magasin de thèmes : Correction des liens vers les fiches bibliotèques ayant des profils dédiés.
+ 
+ - ticket #132745 : Magasin de thèmes : Prise en compte de la variable CHAMP_FICHE_UTILISATEUR pour la modification des informations dans l'espace mon compte
+ 
+ - ticket #115415 : Magasin de thèmes : Correction du nom de l'auteur dans les documents.
+                    Le nom de l'auteur utilisé est celui indexé par Bokeh et non le nom présent dans l'unimarc.
+		    Ajout de la modération des bandes annonces dans l'onglet média des documents grâce au bouton activer ou désactiver la bande annonce.
+ 
+ - ticket #129137 : Magasin de thèmes : Ajout des boutons voir les sélections rangées dans les domaines, voir les sélections des professionnels et lier une sélection à un domaine.
+                    Ces actions sont utilisables par les comptes professionnels uniquement.
+                    Les actions sur une sélection sont maintenant regroupées dans le lien voir plus d'actions.
+ 
+ - ticket #131329 : Magasin de thèmes : Amélioration du code embeded des boites.
+   	  	    On peut maintenant manipuler la propriété target des liens générés dans l'iframe.
+		    Par défaut, le code embed initialise l'attribut target avec l'option _blank.
+		    
+ - ticket #129144 : Magasin de thèmes : Correction de l'affichage paginé d'un flux RSS
+
+ - ticket #126267 : Vue notice : Correction du comportement des blocs dépliés dans le thème historique.
+ 
+ - ticket #128643 : Recherche avancée : Le champ de saisie éditeur se comporte comme les autres champs de saisie (titres, auteurs, ...)
+ 
+ - ticket #132724 : Administration : Ajout d'une variable DISABLE BLOCKS SORTING permettant de désactiver la possibilité de déplacer les boîtes par glisser/déposer en front
+
+ - ticket #132508 : Administration : Ajout d'un script pour mise à jour des informations de vignettes à partir de champs unimarc configurés dans un profil
+ 
+ - ticket #125833 : Administration : Correction de l'apparence du formulaire de modification d'un avis et ajout de l'affichage de la clef oeuvre pour les avis orphelins
+
+
 12/04/2021 - v8.0.114
 
  - ticket #131969 : Sécurité : Corrections de vulnérabilités Cross Site Scripting
@@ -197,6 +228,8 @@
 
  - ticket #127427 : Magasin de Thèmes : Correction de l'affichage des pages lorsque les onglets étaient renommés
 
+ - ticket #117242 : MusicMe : Ajout de l'adresse Mail de l'usager dans la réponse envoyé au service d'authentification
+
 
 09/02/2021 - v8.0.101
 
@@ -220,7 +253,7 @@
 
  - ticket #115804 : Magasin de thèmes : Correction d'une erreur possible lors de l'impression d'un panier de notices
 
- - ticket #79898 : Dilicom : Prise en compte de la DRM LCP 
+ - ticket #79898 : Dilicom : Prise en compte de la DRM LCP
 
 
 01/02/2021 - v8.0.100
@@ -332,6 +365,8 @@
 
  - ticket #123679 : Magasin de thèmes : amélioration du scroll automatique lors de l'ouverture des onglets des Jumbotrons dans le thème Chili.
 
+ - ticket #123249 : Cosmogramme : Correction du paramétrage du champ nouveauté en 995$0
+
 
 17/12/2020 - v8.0.93
 
@@ -387,6 +422,8 @@
 
  - ticket #121171 : Magasin de thèmes : réintroduction d'une surchage CSS reliée au profil
 
+ - ticket #118254 : MusicMe : maintenance du calcul de l'url de la jacquette
+
 
 24/11/2020 - v8.0.91
 
@@ -415,6 +452,8 @@
 
  - ticket #121942 : Réservation : Possibilité d'insérer un message html lors de l'affichage de la liste des sites de retrait
 
+ - ticket #108289 : Cosmogramme : Correction de l'indexation full text des mots du genre qui pouvaient être concaténés 
+
 
 24/11/2020 - v8.0.89
 
@@ -451,6 +490,8 @@
 
  - ticket #121248 : Fiche Abonné : Le bouton mes comptes associés dans le compte lecteur ne s\'affiche que si IdentityProviders est Activé.
 
+ - ticket #119199 : Articles : Correction de l'affichage le jour de la date de fin de publication.
+
 
 
 09/11/2020 - v8.0.86
@@ -568,7 +609,8 @@
  
  - ticket #117608 : Magasin de thèmes : correction de l'affichage des exemplaires de la même oeuvre
 
-
+ - ticket #117384 : Magasin de thèmes : kiosque embarqués dans un article : correction de la visibilité en modes "mur" et "grille"
+ 
 
 15/09/2020 - v8.0.78
 
@@ -661,6 +703,7 @@
 
  - ticket #79915 : Moissonnage OAI / Dublin Core / Numel : récupération des données de géolocalisation. Les carousels peuvent maintenant afficher des données géolocalisées sous forme de carte (pour les notices et articles). Prise en compte des données de latitude & longitude en zone 123. Amélioration notable des performances de rendu des thèmes du magasin
 
+ - ticket #92566 : Cosmogramme : dédoublonnage des périodiques par ISSN
 
 
 
@@ -688,6 +731,8 @@
 
  - ticket #109030 : Formulaire de recherche avancée : Suppression de la mention 'non selectionné'
 
+ - ticket #114294 : Cosmogramme / SIGB PMB : amélioration décodage ISO2709
+
 
 
 06/07/2020 - v8.0.69
diff --git a/application/modules/admin/controllers/ModoController.php b/application/modules/admin/controllers/ModoController.php
index 1f8d973c1049ed1b7ed4ad60545d5831bd8e1197..79487999154a256f8c9beda7b52c1e8264513fcb 100644
--- a/application/modules/admin/controllers/ModoController.php
+++ b/application/modules/admin/controllers/ModoController.php
@@ -115,6 +115,7 @@ class Admin_ModoController extends ZendAfi_Controller_Action {
     $this->_stayOnPage();
   }
 
+
   public function editavisnoticeAction() {
     if (!$avis = Class_AvisNotice::find((int)$this->_request->getParam('id'))) {
       $this->_redirectToAvisNotice();
@@ -130,9 +131,14 @@ class Admin_ModoController extends ZendAfi_Controller_Action {
       return;
     }
 
+    $title = $this->_('Modération de l\'avis "%s"', $avis->getEntete());
+    if ($avis->isOrphan())
+      $title .= ' (' . $avis->getClefOeuvre() . ')';
+
     $this->view->subview = $this->view->partial('modo/editavisnotice.phtml',
                                                 ['form' => $form,
-                                                 'title' => 'Modification de l\'avis "'. $avis->getEntete() .'"']);
+                                                 'title' => $title]);
+
     $this->_forward('index');
   }
 
diff --git a/application/modules/admin/controllers/RecordsController.php b/application/modules/admin/controllers/RecordsController.php
index ef9c8b7096470a58a255e2aadcaac5cd20937ecf..c15d7ff8fa7d501987b0a7c2a646f9f89a12bc09 100644
--- a/application/modules/admin/controllers/RecordsController.php
+++ b/application/modules/admin/controllers/RecordsController.php
@@ -158,4 +158,20 @@ class Admin_RecordsController extends ZendAfi_Controller_Action {
 
     $this->_redirectToReferer();
   }
+
+
+  public function disableTrailerAction() {
+    $this->_helper->notify((new Class_WebService_AllServices())
+      ->disableTrailerForRecord($this->_getParam('id')));
+
+    $this->_redirectToReferer();
+  }
+
+
+  public function enableTrailerAction() {
+    $this->_helper->notify((new Class_WebService_AllServices())
+                           ->enableTrailerForRecord($this->_getParam('id')));
+
+    $this->_redirectToReferer();
+  }
 }
diff --git a/application/modules/opac/controllers/AbonneController.php b/application/modules/opac/controllers/AbonneController.php
index c92a7962ae31a136552720f8d540ffb17180848f..17fdf620b5b9815f65286b6135958836f03a611d 100644
--- a/application/modules/opac/controllers/AbonneController.php
+++ b/application/modules/opac/controllers/AbonneController.php
@@ -25,7 +25,8 @@ class AbonneController extends ZendAfi_Controller_Action {
 
   public function getPlugins() {
     return ['ZendAfi_Controller_Plugin_Printer_Loans',
-            'ZendAfi_Controller_Plugin_Manager_AbonneSessionActivity'];
+            'ZendAfi_Controller_Plugin_Manager_AbonneSessionActivity',
+            'ZendAfi_Controller_Plugin_Template_Borrower'];
   }
 
 
@@ -43,6 +44,12 @@ class AbonneController extends ZendAfi_Controller_Action {
   }
 
 
+  public function acceptVisitor($plugin) {
+    parent::acceptVisitor($plugin);
+    $plugin->visitUser($this->_user);
+  }
+
+
   public function preDispatch() {
     parent::preDispatch();
 
@@ -1537,343 +1544,6 @@ class AbonneController extends ZendAfi_Controller_Action {
   }
 
 
-  public function changeImageAction() {
-    $this->view->user = $this->_user;
-  }
-
-
-  public function pickImageAction() {
-    Class_FileManager::beOpenBar();
-
-    if (!$image = Class_FileManager::find($this->_getParam('id'))) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite. Votre image de profil n\'a pas été modifiée.'));
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if ($this->_user->canAccessBackend()) {
-      (new Class_User_Settings($this->_user))->setProfileImage($image->getPath());
-      $this->_user->save();
-
-      $this->_helper->notify($this->_('Votre image de profil a bien été modifiée.'));
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    $allowed_paths = explode(';', Class_AdminVar::getValueOrDefault('USER_PROFILE_IMAGES'));
-    $allowed_images = [];
-
-    foreach ($allowed_paths as $path)
-      if ($image_var = Class_FileManager::find($path))
-        $allowed_images [] = $image_var;
-
-    foreach ($allowed_images as $allowed_image) {
-      if ($image->getId() == $allowed_image->getId()) {
-        (new Class_User_Settings($this->_user))->setProfileImage($image->getPath());
-        $this->_user->save();
-
-        $this->_helper->notify($this->_('Votre image de profil a bien été modifiée.'));
-        return $this->_redirectClose($this->_getReferer());
-      }
-    }
-
-    $this->_helper->notify($this->_('Une erreur c\'est produite. Votre image de profil n\'a pas été modifiée.'));
-    return $this->_redirectClose($this->_getReferer());
-  }
-
-
-  public function informationsAction() {
-  }
-
-
-  public function selectionsAction() {
-  }
-
-
-  public function suggestionsAction() {
-  }
-
-
-  public function selectionAction() {
-    if (!$this->view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
-      return $this->_redirectClose($this->_getReferer());
-    }
-  }
-
-
-  public function ajouterALaSelectionAction() {
-    if (!$this->view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->selection->isMine()) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    $params = $this->_request->getParams();
-    unset($params['by_work']);
-    $this->view->criteria =
-      (new Class_CriteresRecherche())
-      ->setParams(array_merge($params,
-                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
-  }
-
-
-  public function ajouterLeDocumentALaSelectionAction() {
-    if(!$this->_user)
-      return $this->_forward('popup-login', 'auth', 'opac');
-
-    $selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
-                  ? $selection
-                  : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
-                                                     'libelle' => $this->_getParam('selection_label')]));
-    if (!$selection) {
-      $this->getHelper('ViewRenderer')->setNoRender();
-      return $this->_response->setHttpResponseCode(520);
-    }
-
-    if (!$selection->isMine()) {
-      $this->getHelper('ViewRenderer')->setNoRender();
-      return $this->_response->setHttpResponseCode(520);
-    }
-
-    if (!$record = Class_Notice::find($this->_getParam('record_id'))) {
-      $this->getHelper('ViewRenderer')->setNoRender();
-      return $this->_response->setHttpResponseCode(520);
-    }
-
-    $selection->addNotice($record);
-    $selection->save();
-
-    if ($this->isPopupRequest())
-      return $this->_redirectClose($this->_getReferer());
-
-    $this->_helper->json(['anchor' => $this->view->tagAction((new Intonation_Library_Selection)
-                                                             ->setView($this->view)
-                                                             ->setUser($this->_user)
-                                                             ->setRecord($record)
-                                                             ->setTitle($this->_getParam('selection_label'))
-                                                             ->setImage($this->_getParam('image'))
-                                                             ->setRevertImage($this->_getParam('revert-image'))
-                                                             ->getAction())]);
-  }
-
-
-  public function supprimerDeLaSelectionAction() {
-    $this->view->titre = $this->_('Retirer un document');
-
-    $this->view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
-                  ? $selection
-                  : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
-                                                     'libelle' => $this->_getParam('selection_label')]));
-
-    if (!$this->view->selection) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->selection->isMine()) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->record = Class_Notice::find($this->_getParam('record_id'))) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if ($this->_getParam('ajax')) {
-      $this->view->selection
-        ->removeNotice($this->view->record)
-        ->save();
-
-      return $this->_helper->json(['anchor' =>
-                                   $this->view->tagAction((new Intonation_Library_Selection)
-                                                          ->setView($this->view)
-                                                          ->setUser($this->_user)
-                                                          ->setRecord($this->view->record)
-                                                          ->setTitle($this->_getParam('selection_label'))
-                                                          ->setImage($this->_getParam('image'))
-                                                          ->setRevertImage($this->_getParam('revert-image'))
-                                                          ->getAction())]);
-    }
-
-    if ($this->_getParam('delete')) {
-      $this->view->selection
-        ->removeNotice($this->view->record)
-        ->save();
-      $this->_helper->notify($this->_('Le document a bien été retiré'),
-                             ['status' => 'delete']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-  }
-
-
-  public function creerSelectionAction() {
-    $this->view->titre = $this->view->_('Créer une sélection');
-    $this->view->form = new ZendAfi_Form();
-    $this->view->form
-      ->addElement('text',
-                   'libelle',
-                   ['placeholder' => 'Titre de la sélection'])
-      ->addUniqDisplayGroup('group')
-      ->populate($this->_request->getParams())
-      ->setAction($this->view->url());
-
-    if(!$this->_request->isPost())
-      return;
-
-    $selection = Class_PanierNotice::newForUser($this->_user);
-    $selection->setLibelle($this->_request->getPost('libelle'));
-
-    if(!$this->view->form->isValidModelAndArray($selection, $this->_request->getPost()))
-      return;
-
-    $selection->save();
-    $selection->index();
-    return $this->_redirectClose($this->_getReferer());
-  }
-
-
-  public function supprimerLaSelectionAction() {
-    $this->view->titre = $this->_('Supprimer une sélection');
-
-    $this->view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
-                  ? $selection
-                  : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
-                                                     'libelle' => $this->_getParam('selection_label')]));
-
-    if (!$this->view->selection) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->selection->isMine()) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if ($this->_getParam('delete')) {
-      $this->view->selection->delete();
-      $this->_helper->notify($this->_('La sélection a bien été supprimée'),
-                             ['status' => 'delete']);
-      return $this->_redirectClose($this->view->url(['action' => 'selections']));
-    }
-  }
-
-
-  public function renommerLaSelectionAction() {
-    $this->view->titre = $this->_('Renommer une sélection');
-
-    $this->view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
-                              ? $selection
-                              : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
-                                                                 'libelle' => $this->_getParam('selection_label')]));
-
-    if (!$this->view->selection) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->selection->isMine()) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    $this->view->form = new ZendAfi_Form();
-    $this->view->form
-      ->addElement('text',
-                   'libelle',
-                   ['value' => $this->view->selection->getLibelle(),
-                    'placeholder' => 'Titre de la sélection'])
-      ->addUniqDisplayGroup('group')
-      ->setAction($this->view->url());
-
-    if(!$this->_request->isPost())
-      return;
-
-    $this->view->selection->setLibelle($this->_request->getPost('libelle'));
-
-    if(!$this->view->form->isValidModelAndArray($selection, $this->_request->getPost()))
-      return;
-
-    $selection->save();
-    $selection->index();
-
-    $this->_helper->notify($this->_('La sélection a bien été renommée.'), ['status' => 'success']);
-    return $this->_redirectClose($this->_getReferer());
-  }
-
-
-  public function mesAvisAction() { }
-
-  public function cardsAction() { }
-
-  public function configurationsAction() { }
-
-  public function agendaAction() { }
-
-
-  public function donnerDesAvisAction() {
-    $params = $this->_request->getParams();
-    unset($params['by_work']);
-    $this->view->criteria =
-      (new Class_CriteresRecherche())
-      ->setParams(array_merge($params,
-                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
-  }
-
-
-  public function suivreUneRechercheAction() {
-    $params = $this->_request->getParams();
-    unset($params['by_work']);
-    $this->view->criteria =
-      (new Class_CriteresRecherche())
-      ->setParams(array_merge($params,
-                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
-  }
-
-
-  public function exporterLaSelectionAction() {
-    $this->view->titre = $this->_('Exporter la sélection');
-
-    if (!$this->view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$this->view->selection->isMine()) {
-      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
-      return $this->_redirectClose($this->_getReferer());
-    }
-
-    if (!$format = $this->_getParam('format'))
-      return;
-
-    $data = ('unimarc' == $format)
-      ? $this->view->selection->toUnimarcIso2709()
-      : $this->view->selection->toTabbedList();
-
-    $this->getHelper('ViewRenderer')->setNoRender();
-
-    $name = sprintf('%s_id_%s_%s',
-                    (new Class_TimeSource)->dateYmd(),
-                    $this->view->selection->getId(),
-                    str_replace(' ', '_', $this->view->selection->getLibelle()));
-
-    $response = $this->_response;
-    $response->clearAllHeaders();
-    $response->setHeader('Content-Type', 'text/plain; name="' . $name . '.txt"', true);
-    $response->setHeader('Content-Disposition', 'attachment; filename="' . $name . '.txt"', true);
-    $response->setHeader('Content-Transfer-Encoding', 'base64', true);
-    $response->setHeader('Expires', '0');
-    $response->setHeader('Cache-Control', 'no-cache, must-revalidate');
-    $response->setHeader('Pragma', 'no-cache');
-    $response->setHeader('Access-Control-Allow-Origin', '*');
-    $response->setBody($data);
-  }
-
-
   public function reserverAction() {
     session_write_close();
 
@@ -1919,53 +1589,9 @@ class AbonneController extends ZendAfi_Controller_Action {
   }
 
 
-  public function ajaxLoansAction() {
-    session_write_close();
-
-    $callback = function() {
-      return $this->view->abonne_AjaxLoans($this->_user);
-    };
-    return $this->_helper->ajax($callback);
-  }
-
-
-  public function modifierAction() {
-    if (Class_AdminVar::get('INTERDIRE_MODIF_FICHE_ABONNE')) {
-      $this->_helper->notify($this->_('Vos informations ne peuvent pas être modifiées.'), ['status' => 'error']);
-     return  $this->_redirectClose($this->view->absoluteUrl(['action' => 'informations']));
-    }
-
-    if ((new Class_User_EditFormHelper($this->_user,
-                                       $this->view,
-                                       $this->_request))
-        ->beEditUser()
-        ->proceed()) {
-      $this->_helper->notify($this->_('Vos informations ont bien été modifiées.'), ['status' => 'success']);
-      $this->_redirectClose($this->view->absoluteUrl(['action' => 'informations']));
-    }
-  }
-
-
-  public function changerMonMotDePasseAction() {
-    if (Class_AdminVar::get('INTERDIRE_MODIF_FICHE_ABONNE')) {
-      $this->_helper->notify($this->_('Vos informations ne peuvent pas être modifiées.'), ['status' => 'error']);
-      return  $this->_redirectClose($this->view->absoluteUrl(['action' => 'informations']));
-    }
-
-    if ((new Class_User_EditFormHelper($this->_user,
-                                       $this->view,
-                                       $this->_request))
-        ->beChangePassword()
-        ->proceed()) {
-      $this->_helper->notify($this->_('Votre mot de passe à bien été changé.'), ['status' => 'success']);
-      $this->_redirectClose($this->view->absoluteUrl(['action' => 'informations']));
-    }
-  }
-
-
   public function allDatasCsvAction() {
     $this->getHelper('ViewRenderer')->setNoRender();
-    $this->_helper->csv('Mes-donnees-Bokeh.csv', $this->view->userDatasCsv(Class_Users::getIdentity()));
-
+    $this->_helper->csv('Mes-donnees-Bokeh.csv',
+                        $this->view->userDatasCsv(Class_Users::getIdentity()));
   }
 }
diff --git a/application/modules/opac/controllers/BlogController.php b/application/modules/opac/controllers/BlogController.php
index 6c08aceb656f0529924fe225fe5ef319b3251f35..1b7e0931405ca9fd4668d7013459432d284dc28b 100644
--- a/application/modules/opac/controllers/BlogController.php
+++ b/application/modules/opac/controllers/BlogController.php
@@ -180,10 +180,7 @@ class BlogController extends ZendAfi_Controller_Action {
     if (!$review = Class_AvisNotice::find($this->_getParam('id', 0)))
       return $this->_redirectToReferer();
 
-    $this->view->titre = $this->_('Avis donné par : %s sur le document %s',
-                                  $review->getAuthorName(),
-                                  $review->getFirstNotice()->getTitrePrincipal(' '));
-
     $this->view->content = $review->getAvis();
+    $this->view->titre = $review->getLabel();
   }
 }
\ No newline at end of file
diff --git a/application/modules/opac/controllers/PanierController.php b/application/modules/opac/controllers/PanierController.php
index d32ce2de1e9e11f7f34387b53c7ad02ddc87deb9..730b75931d9f9086f17b52ee88298f9098185520 100644
--- a/application/modules/opac/controllers/PanierController.php
+++ b/application/modules/opac/controllers/PanierController.php
@@ -90,9 +90,7 @@ class PanierController extends ZendAfi_Controller_Action {
       return;
 
     $this->_helper->notify($this->_('Panier %s sauvegardé', $this->view->tag('b', $this->_user->getPanierCourantLibelle())));
-    return $this->_redirect($this->view->absoluteUrl(['action' => 'index',
-                                                      'render' => null,
-                                                      'id_panier' => null]));
+    return $this->_redirectClose($this->_getReferer());
   }
 
 
diff --git a/application/modules/opac/controllers/WidgetController.php b/application/modules/opac/controllers/WidgetController.php
index e2125405fb8361861d8c945ec09ed6806293495a..b23efb2f43d0083846344e68509d7947ad5fe9bb 100644
--- a/application/modules/opac/controllers/WidgetController.php
+++ b/application/modules/opac/controllers/WidgetController.php
@@ -35,6 +35,7 @@ class WidgetController extends ZendAfi_Controller_Action {
       if ($widget = ((new Class_Systeme_Widget_Widget)
                      ->setId($id)
                      ->setProfileId($profile_id)
+                     ->setAnchorTarget($this->_getParam('anchor_target'))
                      ->load())) {
         return Class_Template::current()->renderWidget($widget, $this->view);
       }
diff --git a/application/modules/opac/views/scripts/abonne/exporter-la-selection.phtml b/application/modules/opac/views/scripts/abonne/exporter-la-selection.phtml
index 3f32279db50d1573b163ee951a80e61ad4bd0ed1..420feec78b71970396385170561e25c707ec48fc 100644
--- a/application/modules/opac/views/scripts/abonne/exporter-la-selection.phtml
+++ b/application/modules/opac/views/scripts/abonne/exporter-la-selection.phtml
@@ -1,4 +1,17 @@
 <?php
+
+$buttons = [$this->Button_Back(new Class_Entity(['Attribs' => ['class' => 'btn btn-light']])),
+
+            $this->tagAnchor(['format' => 'unimarc',
+                              'render' => null],
+                             $this->_('UNIMARC'),
+                             ['class' => 'btn btn-info']),
+
+            $this->tagAnchor(['format' => 'list',
+                              'render' => null],
+                             $this->_('Liste'),
+                             ['class' => 'btn btn-warning'])];
+
 $html = [$this->div(['class' => 'col-10'],
                     $this->tag('h3',
                                $this->_('Exporter la sélection %s',
@@ -7,16 +20,6 @@ $html = [$this->div(['class' => 'col-10'],
 
          $this->div(['class' => 'col-10'],
                     $this->div(['class' => 'm-2 p-2 btn-group text-white'],
-                               $this->Button_Back(new Class_Entity(['Attribs' => ['class' => 'btn btn-info']]))
-
-                                                                                           . $this->tagAnchor(['format' => 'unimarc',
-                                                                                                               'render' => null],
-                                                  $this->_('UNIMARC'),
-                                                  ['class' => 'btn btn-success'])
-
-                                                            . $this->tagAnchor(['format' => 'list',
-                                                                                'render' => null],
-                                                  $this->_('Liste'),
-                                                  ['class' => 'btn btn-warning'])))];
+                               implode($buttons)))];
 
-echo $this->grid(implode($html), [], ['class' => 'justify-content-center text-center']);
+echo $this->grid($html, [], ['class' => 'justify-content-center text-center']);
diff --git a/application/modules/opac/views/scripts/abonne/selections-dans-les-domaines.phtml b/application/modules/opac/views/scripts/abonne/selections-dans-les-domaines.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..427eff573126d1e76742bca7e2a829e68c5b5a2b
--- /dev/null
+++ b/application/modules/opac/views/scripts/abonne/selections-dans-les-domaines.phtml
@@ -0,0 +1,2 @@
+<?php
+echo $this->abonne_SelectionsInDomainsBoard($this->user);
diff --git a/application/modules/opac/views/scripts/abonne/selections-des-professionnels.phtml b/application/modules/opac/views/scripts/abonne/selections-des-professionnels.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..cee0fb0039f219ec5c42d252220526121cc0e05b
--- /dev/null
+++ b/application/modules/opac/views/scripts/abonne/selections-des-professionnels.phtml
@@ -0,0 +1,2 @@
+<?php
+echo $this->abonne_SelectionsOfProBoard($this->user);
diff --git a/cosmogramme/FEATURES/117242 b/cosmogramme/FEATURES/117242
deleted file mode 100644
index 0cdf946590307d8a8d83399be1deadf1a3516ba0..0000000000000000000000000000000000000000
--- a/cosmogramme/FEATURES/117242
+++ /dev/null
@@ -1,10 +0,0 @@
-        '117242' =>
-            ['Label' => $this->_('MusicMe : Ajout de l\'adresse Mail de l\'usager'),
-             'Desc' => $this->_('Ajouter dans la réponse envoyé au service d\'authenfication, de l\'adresse Mail de l\'usager, pour permettre un lien avec son ancien compte'),
-             'Image' => '',
-             'Video' => '',
-             'Category' => $this->_('Resources numériques'),
-             'Right' => function($feature_description, $user) {return true;},
-             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=MusicMe',
-             'Test' => '',
-             'Date' => '2021-02-03'],
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/108289 b/cosmogramme/VERSIONS_HOTLINE/108289
deleted file mode 100644
index 5bd32c6dc3e6ec054e215b291aab33a8103b16ac..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/108289
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #108289 : Correction de l'indexation full text des mots du genres qui pouvaient être concaténés 
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/114294 b/cosmogramme/VERSIONS_HOTLINE/114294
deleted file mode 100644
index 6516b833818bbde68e8aab8be269b4bf85fd218f..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/114294
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #114294 : Cosmogramme / SIGB PMB : amélioration décodage ISO2709
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/117384 b/cosmogramme/VERSIONS_HOTLINE/117384
deleted file mode 100644
index b160d9a239dc3ab69b88bfdf4a1867e39f7ee4d8..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/117384
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #117384 : Magasin de thèmes : kiosque embarqués dans un article : correction de la visibilité en modes "mur" et "grille"
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/118254 b/cosmogramme/VERSIONS_HOTLINE/118254
deleted file mode 100644
index f1e165e84220ae526bacc2fd3ac4434233d7b30a..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/118254
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #118254 : MusicMe : mise en conformité à l'évolution du calcul de l'url de la jacquette
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/119199 b/cosmogramme/VERSIONS_HOTLINE/119199
deleted file mode 100644
index 0a6f07fc8bf4fa46915ad6b6e366d00306d45333..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/119199
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #119199 : Un article ne s'affichait pas le jour de la date de fin de publication.
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_HOTLINE/123249 b/cosmogramme/VERSIONS_HOTLINE/123249
deleted file mode 100644
index a62cacb418e25b70efcb7372a07d75b4f0ed5eb5..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_HOTLINE/123249
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #123249 : Impossible de paramétrer le champ nouveauté en 995$0 dans cosmogramme
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_WIP/117242 b/cosmogramme/VERSIONS_WIP/117242
deleted file mode 100644
index e081429626c4cbb80eae38a2050b3297b6d75911..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_WIP/117242
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #117242 : MusicMe : Ajout de l'adresse Mail de l'usager dans la réponse envoyé au service d'authentification
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_WIP/92566 b/cosmogramme/VERSIONS_WIP/92566
deleted file mode 100644
index a9cac28650b65b714477dd15585f4dd6a1a83574..0000000000000000000000000000000000000000
--- a/cosmogramme/VERSIONS_WIP/92566
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #92566 : Cosmogramme : dédoublonnage des périodiques par ISSN
\ No newline at end of file
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index a45aca75c5f6325b60af0f47b6f68baa4d5c413f..23e5a19d1624a5fd8c345a4633fe5b9881310ce7 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -324,6 +324,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
             'WORKFLOW_TEXT_MAIL_ARTICLE_REFUSED' => Class_AdminVar_Meta::newDefault($this->_('Contenu de l\'email de notification de refus d\'un article à valider. Termes substitués: TITRE_ARTICLE, URL_ARTICLE, AUTHOR_ARTICLE, SAVED_BY_ARTICLE')),
             'WORKFLOW_TEXT_MAIL_ARTICLE_VALIDATED' => Class_AdminVar_Meta::newDefault($this->_('Contenu de l\'email de notification de validation d\'un article. Termes substitués: TITRE_ARTICLE, URL_ARTICLE, AUTHOR_ARTICLE, SAVED_BY_ARTICLE')),
             'CUSTOM_GENRE_ICON' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'interface de personnalisation des icones des genres')),
+            'DISABLE_BLOCKS_SORTING' => Class_AdminVar_Meta::newOnOff($this->_('Désactivation de la possibilité de déplacer les boîtes par glisser/déposer en front')),
     ];
   }
 
@@ -1228,6 +1229,16 @@ Pour vous désabonner de la lettre d\'information, merci de cliquer sur le lien
   public function isActivityAllowSubscriptionsForAllEnabled() {
     return Class_AdminVar::isModuleEnabled('ACTIVITY_ALLOW_SUBSCRIPTIONS_FOR_ALL');
   }
+
+
+  public function isBlockSortingDisabled() {
+    return Class_AdminVar::isModuleEnabled('DISABLE_BLOCKS_SORTING');
+  }
+
+
+  public function isBlockSortingEnabled() {
+    return !Class_AdminVar::isBlockSortingDisabled();
+  }
 }
 
 
diff --git a/library/Class/AnchorTarget.php b/library/Class/AnchorTarget.php
new file mode 100644
index 0000000000000000000000000000000000000000..11523cf297ba60ae3d5fe8f46a1aa39b5900de3f
--- /dev/null
+++ b/library/Class/AnchorTarget.php
@@ -0,0 +1,40 @@
+<?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_AnchorTarget {
+
+  protected static $_target = '';
+
+
+  public static function setTarget($target) {
+    return static::$_target = $target;
+  }
+
+
+  public function injectIn($attribs) {
+    if ( ! static::$_target)
+      return $attribs;
+
+    $attribs ['target'] = static::$_target;
+    return $attribs;
+  }
+}
diff --git a/library/Class/AvisNotice.php b/library/Class/AvisNotice.php
index 7ee8600d5d59a43e109aa266c483da181c36293e..95a64839518f5e55a8d0d2bf7818b3fe9dcc938e 100644
--- a/library/Class/AvisNotice.php
+++ b/library/Class/AvisNotice.php
@@ -412,6 +412,13 @@ class Class_AvisNotice  extends Storm_Model_Abstract {
   }
 
 
+  public function getRecordTitle() {
+    return ($record = $this->getFirstNotice())
+      ? $record->getTitrePrincipal(' ')
+      : '';
+  }
+
+
   public function setNotices($notices) {
     $this->_notices = $notices;
     return $this;
@@ -587,4 +594,21 @@ class Class_AvisNotice  extends Storm_Model_Abstract {
       ? (new Class_FederationReview_RecordKey($record))->value()
       : '';
   }
+
+
+  public function getLabel() {
+    $title = $this->getRecordTitle();
+    $author = $this->getAuthorName();
+
+    if ($title && $author)
+      return $this->_('Avis donné par : %s sur le document %s', $author, $title);
+
+    if ($title)
+      return $this->_('Avis sur le document %s', $title);
+
+    if ($author)
+      return $this->_('Avis donné par : %s', $author);
+
+    return $this->_('Avis');
+  }
 }
\ No newline at end of file
diff --git a/library/Class/CriteresRecherche.php b/library/Class/CriteresRecherche.php
index 10a3a34bd7ea3678635a9e77500cd49b22a18d9b..fd87650d4ead04a84e0b0b5db189745b6f81dcd5 100644
--- a/library/Class/CriteresRecherche.php
+++ b/library/Class/CriteresRecherche.php
@@ -481,6 +481,7 @@ class Class_CriteresRecherche extends Class_CriteresRecherche_Abstract {
     $text_inputs = array_merge(['titres',
                                 'auteurs',
                                 'matieres',
+                                'editeur',
                                 'dewey',
                                 'collection']);
 
@@ -490,9 +491,6 @@ class Class_CriteresRecherche extends Class_CriteresRecherche_Abstract {
         $visitor->visitTextInput($name, $operateur, $type_recherche, $value,$this->getPertinence());
     }
 
-    if ($editor = $this->getParam('rech_editeur'))
-      $visitor->visitEditor($editor);
-
     if ($type_doc = $this->getTypeDoc())
       $visitor->visitTypeDoc($type_doc);
 
diff --git a/library/Class/Exemplaire.php b/library/Class/Exemplaire.php
index d536a0c9dcdd408b2b7f77137f9fe642c6845313..8fc0a97f611102d16bf3161dedf3d1f66c9069ea 100644
--- a/library/Class/Exemplaire.php
+++ b/library/Class/Exemplaire.php
@@ -186,7 +186,11 @@ class Class_Exemplaire extends Storm_Model_Abstract {
 
   public function getBibLibelle() {
     return $this->hasBib() ? $this->getBib()->getLibelle() : '';
+  }
+
 
+  public function getLibraryUrl() {
+    return $this->hasBib() ? $this->getBib()->getUrl() : '';
   }
 
 
diff --git a/library/Class/Notice.php b/library/Class/Notice.php
index ce8ed1dcddedb31ff9a5acdf83f978e922e267fc..0aba5991233abb258d31aa3d19099cf73ec2449d 100644
--- a/library/Class/Notice.php
+++ b/library/Class/Notice.php
@@ -215,7 +215,8 @@ class Class_Notice extends Storm_Model_Abstract {
     $_langueCodes,
     $_first_exemplaire,
     $_data_map,
-    $_collation;
+    $_collation,
+    $_main_author_cache;
 
   protected $_default_attribute_values = ['type_doc' => 0,
                                           'annee' => null,
@@ -1989,4 +1990,20 @@ class Class_Notice extends Storm_Model_Abstract {
   public function getClefAlpha() {
     return $this->_get('clef_alpha');
   }
+
+
+  public function getMainAuthorFromCodif() {
+    if ($this->_main_author_cache)
+      return $this->_main_author_cache;
+
+    if ( ! $main_author = $this->getAuteurPrincipal())
+      return '';
+
+    if ( ! $codif = Class_CodifAuteur::findWithFullName($main_author))
+      return $main_author;
+
+    $author_field = new Class_Notice_FieldAuthor($codif);
+
+    return $this->_main_author_cache = $author_field->getLabel();
+  }
 }
diff --git a/library/Class/Notice/Thumbnail/ProviderThirdParty.php b/library/Class/Notice/Thumbnail/ProviderThirdParty.php
index 677a8b8a638e47f26c889868e8679d1faa2edc07..b9784172abebdd2d3b720b00b720da3d9e335269 100644
--- a/library/Class/Notice/Thumbnail/ProviderThirdParty.php
+++ b/library/Class/Notice/Thumbnail/ProviderThirdParty.php
@@ -31,28 +31,8 @@ class Class_Notice_Thumbnail_ProviderThirdParty
 
 
   protected function _getThirdPartyUrl() {
-    $thumbnail = Class_Profil::getCurrentProfil()
-      ->getModulePreference('recherche',
-                            'viewnotice' . $this->_record->getTypeDoc(),
-                            'thumbnail_fields');
-    $datas = explode(';', $thumbnail);
-    foreach ($datas as $data) {
-      list($zone, $field) = explode('-', $data);
-      $subfields = $this->_record->get_subfield($zone, $field);
-      $image = empty($subfields) ? null : $this->_getFirstImage($subfields);
-      if ($image)
-        return $image;
-    }
-  }
-
-
-  protected function _getFirstImage($links) {
-    $validator = new ZendAfi_Validate_VignetteUrl();
-    foreach ($links as $link)
-      if ($validator->isValid($link))
-        return $link;
-
-    return '';
+    return (new Class_Notice_ThumbnailFields(Class_Profil::getCurrentProfil()))
+      ->firstImageIn($this->_record);
   }
 
 
diff --git a/library/Class/Notice/Thumbnail/UpdateFromFields.php b/library/Class/Notice/Thumbnail/UpdateFromFields.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccb18b81e4581ecb5a94044e97e0b55f8391630e
--- /dev/null
+++ b/library/Class/Notice/Thumbnail/UpdateFromFields.php
@@ -0,0 +1,82 @@
+<?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_Notice_Thumbnail_UpdateFromFields {
+  use Trait_MemoryCleaner;
+  protected
+    $_fields,
+    $_page,
+    $_updated;
+
+  public function runWith($profile_id) {
+    if (!$profile_id || !($profile = Class_Profil::find((int)$profile_id))) {
+      echo "\n No profile or invalid profile \n";
+      return;
+    }
+
+    $this->_fields = new Class_Notice_ThumbnailFields($profile);
+    $this->_page = 1;
+    $this->_updated = 0;
+    $last_id = 0;
+
+    while ($records = Class_Notice::findAllBy(['type' => Class_Notice::TYPE_BIBLIOGRAPHIC,
+                                               'where' => 'id_notice > ' . $last_id,
+                                               'order' => 'id_notice',
+                                               'limit' => 1000]))
+      $last_id = $this->_runPage($records);
+
+    printf("\n\n %d record(s) updated\n\n", $this->_updated);
+  }
+
+
+  protected function _runPage($records) {
+    printf("\n==== Page %d ====\n", $this->_page);
+
+    foreach($records as $record) {
+      $this->_runRecord($record);
+      $last_id = $record->getId();
+    }
+
+    $this->_page++;
+    $this->_cleanMemory();
+    return $last_id;
+  }
+
+
+  protected function _runRecord($record) {
+    echo ".";
+
+    if (!$image = $this->_fields->firstImageIn($record))
+      return $this;
+
+    if ($record->getUrlVignette() != $image
+        || $record->getUrlImage() != $image) {
+      $record
+        ->setUrlVignette($image)
+        ->setUrlImage($image)
+        ->save();
+      $this->_updated++;
+    }
+
+    return $this;
+  }
+}
diff --git a/library/Class/Notice/ThumbnailFields.php b/library/Class/Notice/ThumbnailFields.php
new file mode 100644
index 0000000000000000000000000000000000000000..a67abe00fcb4e6b6622a098b30f881d6ab7d836b
--- /dev/null
+++ b/library/Class/Notice/ThumbnailFields.php
@@ -0,0 +1,69 @@
+<?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_Notice_ThumbnailFields {
+  protected
+    $_profile,
+    $_validator,
+    $_definition_by_type_cache = [];
+
+
+  public function __construct($profile) {
+    $this->_validator = new ZendAfi_Validate_VignetteUrl();
+    $this->_profile = $profile;
+  }
+
+
+  public function firstImageIn($record) {
+    $fields = $this->_definitionFor($record);
+
+    foreach ($fields as $field) {
+      list($zone, $subfield) = explode('-', $field);
+      $values = $record->get_subfield($zone, $subfield);
+      if ($image = $this->_firstImageInFields($values))
+        return $image;
+    }
+  }
+
+
+  protected function _definitionFor($record) {
+    $key = $record->getTypeDoc();
+    if (isset($this->_definition_by_type_cache[$key]))
+      return $this->_definition_by_type_cache[$key];
+
+    return $this->_definition_by_type_cache[$key] =
+      explode(';',
+              $this->_profile->getModulePreference('recherche',
+                                                   'viewnotice' . $record->getTypeDoc(),
+                                                   'thumbnail_fields'));
+  }
+
+
+  protected function _firstImageInFields($values) {
+    if (empty($values))
+      return;
+
+    foreach ($values as $value)
+      if ($this->_validator->isValid($value))
+        return $value;
+  }
+}
diff --git a/library/Class/Onglet.php b/library/Class/Onglet.php
index 27ab290e84f7fb5d63802e676ad4d12487737c49..c7f9da3a73a20aaeb28ab10a1ae5b872601ebee6 100644
--- a/library/Class/Onglet.php
+++ b/library/Class/Onglet.php
@@ -22,21 +22,21 @@
 
 class Class_Onglet {
   use Trait_Singleton, Trait_Translator;
-
-  protected $libelle;
-  protected $function_name = 'alwaysDisplay';
-
-  /** Mode d'affichage 0=aucun 1=bloc déplié 2=bloc fermé 3=dans 1 onglet */
-  protected $display=0;
-
-  /** Largeur de l'onglet 0=répartition auto en pourcentage */
-  protected $largeur=0;
-
-  protected $order=0;
-
+  const
+    HIDDEN = 0,
+    OPEN = 1,
+    CLOSED = 2,
+    TAB = 3;
+
+  protected
+    $libelle,
+    $function_name = 'alwaysDisplay',
+    $display = 0,
+    $largeur = 0,
+    $order = 0;
 
   public static function forLibelleAndType($libelle, $type) {
-    return new self($libelle, $type);
+    return new static($libelle, $type);
   }
 
 
@@ -86,7 +86,7 @@ class Class_Onglet {
 
 
   public function callDisplayMethod($param) {
-    return ($this->{$this->function_name}($param));
+    return call_user_func([$this, $this->function_name], $param);
   }
 
 
@@ -149,4 +149,29 @@ class Class_Onglet {
     $this->order = $order;
     return $this;
   }
+
+
+  public function isBlock() {
+    return $this->isOpenBlock() || $this->isClosedBlock();
+  }
+
+
+  public function isOpenBlock() {
+    return static::OPEN == $this->display;
+  }
+
+
+  public function isClosedBlock() {
+    return static::CLOSED == $this->display;
+  }
+
+
+  public function isTab() {
+    return static::TAB == $this->display;
+  }
+
+
+  public function isHidden() {
+    return static::HIDDEN == $this->display;
+  }
 }
\ No newline at end of file
diff --git a/library/Class/PanierNotice.php b/library/Class/PanierNotice.php
index bfcfa1465ee5124c56e2a93293811e584013b262..3a1e8334824ed6ed2a4f6f6a6a0f9f62608aecf8 100644
--- a/library/Class/PanierNotice.php
+++ b/library/Class/PanierNotice.php
@@ -23,7 +23,9 @@
 class PanierNoticeLoader extends Storm_Model_Loader {
   use Trait_Translator, Trait_TimeSource;
 
-  protected $_by_label_and_user_cache = [];
+  protected
+    $_by_label_and_user_cache = [],
+    $_all_ids_with_catalogue_cache = [];
 
 
   public function newForUser($user) {
@@ -61,6 +63,9 @@ class PanierNoticeLoader extends Storm_Model_Loader {
 
 
   public function findAllIdsWithCatalogue() {
+    if ($this->_all_ids_with_catalogue_cache)
+      return $this->_all_ids_with_catalogue_cache;
+
     $panier_notice_catalogue = Class_PanierNoticeCatalogue::findAll();
     $ids_with_catalogue = array_map(function($association)
                                     {
@@ -68,7 +73,7 @@ class PanierNoticeLoader extends Storm_Model_Loader {
                                     },
                                     $panier_notice_catalogue);
 
-    return array_filter(array_unique($ids_with_catalogue));
+    return $this->_all_ids_with_catalogue_cache = array_filter(array_unique($ids_with_catalogue));
   }
 
 
@@ -544,10 +549,27 @@ class Class_PanierNotice extends Storm_Model_Abstract {
   }
 
 
-  public function isMine() {
+  public function canBeEditedByMe() {
     if (!$user = Class_Users::getIdentity())
       return false;
 
-    return  $user == $this->getUser();
+    return $this->isMine($user)
+      ? true
+      : $user->canModifyPanier($this);
+  }
+
+
+  public function isMine($user = null) {
+    if ( ! $user)
+      $user = Class_Users::getIdentity();
+
+    return $user
+      ? $user == $this->getUser()
+      : false;
+  }
+
+
+  public function numberOfDomains() {
+    return count($this->getDomainIdsAsArray());
   }
 }
\ No newline at end of file
diff --git a/library/Class/Profil.php b/library/Class/Profil.php
index b9a545fcbb73b7a686a0b987bd080be8f08247c7..131554e57fe290e938dcd54578e3a9b261e9d40e 100644
--- a/library/Class/Profil.php
+++ b/library/Class/Profil.php
@@ -2173,6 +2173,9 @@ class Class_Profil extends Storm_Model_Abstract {
   public function moveModuleOldDivPosNewDivPos($old_div, $old_pos, $new_div, $new_pos) {
     $cfg_accueil = $this->getCfgAccueilAsArray();
     $id = $this->_getIdModuleAtDivPosInCfg($old_div, $old_pos, $cfg_accueil);
+    if (!isset($cfg_accueil['modules'][$id]))
+      return $this;
+
     $moved_module = $cfg_accueil['modules'][$id];
     $moved_module['division'] = $new_div;
     unset($cfg_accueil['modules'][$id]);
diff --git a/library/Class/User/EditFormHelper.php b/library/Class/User/EditFormHelper.php
index 317c9e281e29f5e38c8125be8295f814613703b7..4df06bf4c88f8ed6c72bb9877fcb07cf08d21678 100644
--- a/library/Class/User/EditFormHelper.php
+++ b/library/Class/User/EditFormHelper.php
@@ -31,7 +31,8 @@ class Class_User_EditFormHelper {
     $_web_request,
     $_form,
     $_old_password,
-    $_callback;
+    $_callback,
+    $_fields_to_show;
 
 
   public function __construct($user, $view, $request) {
@@ -84,59 +85,41 @@ class Class_User_EditFormHelper {
 
   public function beEditUser() {
     $this->_form = new ZendAfi_Form;
-
-    $textfields = ['nom' => $this->_('Nom'),
-                   'prenom' => $this->_('Prénom'),
-                   'pseudo' => $this->_('Pseudo'),
-                   'adresse' => $this->_('Adresse'),
-                   'code_postal' => $this->_('Code postal'),
-                   'ville' => $this->_('Ville'),
-                   'mail' => $this->_('E-Mail'),
-                   'telephone' => $this->_('Téléphone'),
-                   'mobile' => $this->_('Téléphone mobile')];
-
-    foreach($textfields as $field => $label) {
-
-      $element = $this->_form
-        ->createElement('text', $field)
-        ->setLabel($label)
-        ->setAttrib('size', 30);
-
-      $this->_form->addElement($element);
-    }
-
-    if ($mail_element = $this->_form->getElement('mail'))
-      $mail_element->addValidator(new ZendAfi_Validate_EmailAddress());
-
-    if ($phone_element = $this->_form->getElement('telephone'))
-      $phone_element->addValidator(new ZendAfi_Validate_PhoneNumber());
-
-    if ($mobile_element = $this->_form->getElement('mobile'))
-      $mobile_element->addValidator(new ZendAfi_Validate_PhoneNumber());
-
-    $this->_form->addDisplayGroup(['nom', 'prenom', 'pseudo'],
-                                  'identification',
-                                  ['legend' => 'Identification']);
-
-    $this->_form->addDisplayGroup(['adresse', 'code_postal', 'ville'],
-                                  'coordonnees',
-                                  ['legend' => 'Coordonnées']);
-
-    $this->_form->addDisplayGroup(['mail', 'telephone', 'mobile'],
-                                  'contact',
-                                  ['legend' => 'Contact']);
-
-    $fields_to_show = Class_AdminVar::getChampsFicheUtilisateur();
-    if (in_array('mode_contact', $fields_to_show)) {
-      $this->_form->addElement('radio',
-                               'mode_contact',
-                               ['label' => '',
-                                'multiOptions' => [Class_Users::MODE_CONTACT_LETTER => $this->_(' par courrier postal'),
-                                                   Class_Users::MODE_CONTACT_MAIL => $this->_(' par E-Mail'),
-                                                   Class_Users::MODE_CONTACT_SMS => $this->_(' par SMS')],
-                                'value' => $user->getModeContact()]);
-      $this->_form->addDisplayGroup(['mode_contact'], 'mode_de_contact', ['legend' => 'Recevoir mes notificactions de réservation et de rappel']);
-    }
+    $this->_fields_to_show = Class_AdminVar::getChampsFicheUtilisateur();
+    $this
+      ->_filteredDisplayGroup(['nom' => $this->_('Nom'),
+                               'prenom' => $this->_('Prénom'),
+                               'pseudo' => $this->_('Pseudo')],
+                              'identification',
+                              $this->_('Identification'))
+
+      ->_filteredDisplayGroup(['adresse' => $this->_('Adresse'),
+                               'code_postal' => $this->_('Code postal'),
+                               'ville' => $this->_('Ville')],
+                              'coordonnees',
+                              $this->_('Coordonnées'))
+
+      ->_filteredDisplayGroup(['mail' => $this->_createElement('email', 'mail',
+                                                               ['label' => $this->_('E-Mail'),
+                                                                'size' => 30]),
+                               'telephone' => $this->_createElement('tel', 'telephone',
+                                                                    ['label' => $this->_('Téléphone'),
+                                                                     'size' => 30]),
+                               'mobile' => $this->_createElement('tel', 'mobile',
+                                                                 ['label' => $this->_('Téléphone mobile'),
+                                                                  'size' => 30])],
+                              'contact',
+                              $this->_('Contact'))
+
+      ->_filteredDisplayGroup(['mode_contact' => $this
+                               ->_createElement('radio', 'mode_contact',
+                                                ['label' => '',
+                                                 'multiOptions' => [Class_Users::MODE_CONTACT_LETTER => $this->_(' par courrier postal'),
+                                                                    Class_Users::MODE_CONTACT_MAIL => $this->_(' par E-Mail'),
+                                                                    Class_Users::MODE_CONTACT_SMS => $this->_(' par SMS')]])],
+                              'mode_de_contact',
+                              $this->_('Recevoir mes notifications de réservation et de rappel'))
+      ;
 
     $this->_form
       ->populate($this->_user->toArray());
@@ -145,7 +128,7 @@ class Class_User_EditFormHelper {
       $password = $this->_user->getPassword();
 
       $this->_user
-      ->updateAttributes($this->_web_request->getPost())
+      ->updateAttributes($this->_form->getValues())
       ->setPassword($password);
     };
 
@@ -153,6 +136,29 @@ class Class_User_EditFormHelper {
   }
 
 
+  protected function _createElement($type, $name, $options) {
+    return $this->_form->createElement($type, $name, $options);
+  }
+
+
+  protected function _filteredDisplayGroup($fields, $group_name, $group_label) {
+    if (!$fields = array_intersect_key($fields,
+                                       array_fill_keys($this->_fields_to_show, 1)))
+      return $this;
+
+    foreach($fields as $field => $label_or_element) {
+      is_object($label_or_element)
+        ? $this->_form->addElement($label_or_element)
+        : $this->_form->addElement('text', $field, ['label' => $label_or_element, 'size' => 30]);
+    }
+
+    $this->_form->addDisplayGroup(array_keys($fields),
+                                  $group_name,
+                                  ['legend' => $group_label]);
+
+    return $this;
+  }
+
 
   public function proceed() {
     $this->_form
@@ -175,6 +181,7 @@ class Class_User_EditFormHelper {
       $this->_form->addDecorator('Errors');
       foreach($this->_user->getErrors() as $error)
         $this->_form->addError($error);
+
       return false;
     }
 
diff --git a/library/Class/Users.php b/library/Class/Users.php
index 35b7caaa737937409ad0840bcb8dfb486f690e90..d8e029645dcd550af4f755a79e3a81749ce4d335 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -580,6 +580,13 @@ class Class_Users extends Storm_Model_Abstract {
   }
 
 
+  public function getLibraryUrl() {
+    return ($library = $this->getBib())
+      ? $library->getUrl()
+      : '';
+  }
+
+
   public function getLibraryCode() {
     return $this->getEmprunteur()->getLibraryCode();
   }
@@ -647,22 +654,14 @@ class Class_Users extends Storm_Model_Abstract {
   }
 
 
-  /**
-   * @param mode string : 'is_contact_mail' | 'is_contact_sms' | 'is_contact_letter'
-   */
   public function setModeContact($mode_contact) {
-    switch ($mode_contact) {
-      case self::MODE_CONTACT_MAIL:
-        $this->setIsContactMail(true)->setIsContactSms(false); break;
-
-      case self::MODE_CONTACT_SMS:
-        $this->setIsContactMail(false)->setIsContactSms(true); break;
+    if (static::MODE_CONTACT_MAIL == $mode_contact)
+      return $this->setIsContactMail(true)->setIsContactSms(false);
 
-      default:
-        $this->setIsContactMail(false)->setIsContactSms(false);
-    }
+    if (static::MODE_CONTACT_SMS == $mode_contact)
+      return $this->setIsContactMail(false)->setIsContactSms(true);
 
-    return $this;
+    return $this->setIsContactMail(false)->setIsContactSms(false);
   }
 
 
diff --git a/library/Class/WebService/AllServices.php b/library/Class/WebService/AllServices.php
index 5337721effa14d07e5973a0b1feef09c2dd0d587..dfacdf30677ffeeed1440653631e5f657b4ee6e3 100644
--- a/library/Class/WebService/AllServices.php
+++ b/library/Class/WebService/AllServices.php
@@ -34,7 +34,11 @@ class Class_WebService_AllServices {
     SVC_SET_TRAILER = 14,
     SVC_GET_THUMBNAIL = 10,
     BIO_ENABLED = 'enabled',
-    BIO_DISABLED = 'disabled';
+    BIO_DISABLED = 'disabled',
+
+    DISABLE_TRAILER = 'disable_trailer',
+    TRAILER_ENABLED = 0,
+    TRAILER_DISABLED = 1;
 
   private static $_http_client;
 
@@ -201,7 +205,6 @@ class Class_WebService_AllServices {
   }
 
 
-
   public function uploadTrailerForRecord($url, $id) {
     if (!$notice = Class_Notice::find($id))
       return $this->_('Notice non trouvée');
@@ -218,7 +221,6 @@ class Class_WebService_AllServices {
   }
 
 
-
   public static function httpGet($url, $args) {
     if (!isset(static::$_http_client))
       static::$_http_client = new Class_WebService_SimpleWebClient();
@@ -306,4 +308,34 @@ class Class_WebService_AllServices {
   public function getServices() {
     return $this->services;
   }
+
+
+  public function disableTrailerForRecord($id) {
+    return $this->_toggelTrailerForRecord($id, $this->_('La bande-annonce a bien été désactivée.'), static::TRAILER_DISABLED);
+  }
+
+
+  public function enableTrailerForRecord($id) {
+    return $this->_toggelTrailerForRecord($id, $this->_('La bande-annonce a bien été activée.'), static::TRAILER_ENABLED);
+  }
+
+
+  protected function _toggelTrailerForRecord($id, $message, $flag) {
+    if ( ! $record = Class_Notice::find($id))
+      return $this->_('Notice non trouvée');
+
+    $args = array_filter(['clef_oeuvre' => Class_Notice_WorkKey::legacy()->keyString($record),
+                          'type_doc' => $record->getFamilleId()]);
+
+    $args [static::DISABLE_TRAILER] = $flag;
+
+    $result = static::runServiceAfi(static::SVC_SET_TRAILER, $args);
+
+    if (!isset($result['statut_recherche']))
+      return $this->_('Le service n\'a pas répondu');
+
+    return static::RETOUR_SERVICE_OK == $result['statut_recherche']
+      ? $message
+      : $result['message'];
+  }
 }
\ No newline at end of file
diff --git a/library/ZendAfi/Acl/AdminControllerGroup.php b/library/ZendAfi/Acl/AdminControllerGroup.php
index 50ef2e27cc82dc548797b4b597cb6adb7eda0a57..7175658046e55c8a1ff6494f5f9e5595c2990284 100644
--- a/library/ZendAfi/Acl/AdminControllerGroup.php
+++ b/library/ZendAfi/Acl/AdminControllerGroup.php
@@ -106,6 +106,7 @@ class ZendAfi_Acl_AdminControllerGroup {
                           'systeme/webservices' => Class_AdminVar::isWebserviceTestEnabled(),
                           'systeme/importavisopac2' => Class_AdminVar::isImportAvisOpac2Enabled(),
                           'profil/genres' => Class_AdminVar::isCustomGenreIconEnabled(),
+                          'profil/module-sort' => Class_AdminVar::isBlockSortingEnabled(),
                           'custom-fields-report' => Class_AdminVar::isCustomFieldsReportEnabled(),
                           'usergroup-agenda' => Class_AdminVar::isRendezVousEnabled(),
                           'rendez-vous' => Class_AdminVar::isRendezVousEnabled(),
diff --git a/library/ZendAfi/Controller/Plugin/Abstract.php b/library/ZendAfi/Controller/Plugin/Abstract.php
index 2b6ad1faa359edcfbdaa10f1bf1ed3a10dc293fd..5f98c568cd7133be5f11930fc7a5f6796270a79b 100644
--- a/library/ZendAfi/Controller/Plugin/Abstract.php
+++ b/library/ZendAfi/Controller/Plugin/Abstract.php
@@ -270,4 +270,9 @@ abstract class ZendAfi_Controller_Plugin_Abstract {
   public function renderScript($script) {
     $this->_controller->renderScript($script);
   }
+
+
+  public function isPopupRequest() {
+    return $this->_controller->isPopupRequest();
+  }
 }
diff --git a/library/ZendAfi/Controller/Plugin/Template/Borrower.php b/library/ZendAfi/Controller/Plugin/Template/Borrower.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fb454de5b34added37f8db751949f597ce18926
--- /dev/null
+++ b/library/ZendAfi/Controller/Plugin/Template/Borrower.php
@@ -0,0 +1,435 @@
+<?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 ZendAfi_Controller_Plugin_Template_Borrower extends ZendAfi_Controller_Plugin_Abstract {
+
+  protected $_user;
+
+
+  public function visitUser($user) {
+    $this->_user = $user;
+  }
+
+
+  public function informationsAction() {
+  }
+
+
+  public function selectionsAction() {
+  }
+
+
+  public function suggestionsAction() {
+  }
+
+
+  public function selectionAction() {
+    if (!$this->_view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
+      return $this->_redirectClose($this->_getReferer());
+    }
+  }
+
+
+  public function ajouterALaSelectionAction() {
+    if (!$this->_view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->selection->canBeEditedByMe()) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    $params = $this->_request->getParams();
+    unset($params['by_work']);
+    $this->_view->criteria =
+      (new Class_CriteresRecherche())
+      ->setParams(array_merge($params,
+                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
+  }
+
+
+  public function ajouterLeDocumentALaSelectionAction() {
+    if(!$this->_user)
+      return $this->_forward('popup-login', 'auth', 'opac');
+
+    $selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
+                  ? $selection
+                  : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
+                                                     'libelle' => $this->_getParam('selection_label')]));
+    if (!$selection) {
+      $this->getHelper('ViewRenderer')->setNoRender();
+      return $this->_response->setHttpResponseCode(520);
+    }
+
+    if (!$selection->canBeEditedByMe()) {
+      $this->getHelper('ViewRenderer')->setNoRender();
+      return $this->_response->setHttpResponseCode(520);
+    }
+
+    if (!$record = Class_Notice::find($this->_getParam('record_id'))) {
+      $this->getHelper('ViewRenderer')->setNoRender();
+      return $this->_response->setHttpResponseCode(520);
+    }
+
+    $selection->addNotice($record);
+    $selection->save();
+
+    if ($this->isPopupRequest())
+      return $this->_redirectClose($this->_getReferer());
+
+    $this->_helper->json(['anchor' => $this->_view->tagAction((new Intonation_Library_Selection)
+                                                             ->setView($this->_view)
+                                                             ->setUser($this->_user)
+                                                             ->setRecord($record)
+                                                             ->setTitle($this->_getParam('selection_label'))
+                                                             ->setImage($this->_getParam('image'))
+                                                             ->setRevertImage($this->_getParam('revert-image'))
+                                                             ->getAction())]);
+  }
+
+
+  public function supprimerDeLaSelectionAction() {
+    $this->_view->titre = $this->_('Retirer un document');
+
+    $this->_view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
+                              ? $selection
+                              : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
+                                                                 'libelle' => $this->_getParam('selection_label')]));
+
+    if (!$this->_view->selection) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->selection->canBeEditedByMe()) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->record = Class_Notice::find($this->_getParam('record_id'))) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if ($this->_getParam('ajax')) {
+      $this->_view->selection
+        ->removeNotice($this->_view->record)
+        ->save();
+
+      return $this->_helper->json(['anchor' =>
+                                   $this->_view->tagAction((new Intonation_Library_Selection)
+                                                          ->setView($this->_view)
+                                                          ->setUser($this->_user)
+                                                          ->setRecord($this->_view->record)
+                                                          ->setTitle($this->_getParam('selection_label'))
+                                                          ->setImage($this->_getParam('image'))
+                                                          ->setRevertImage($this->_getParam('revert-image'))
+                                                          ->getAction())]);
+    }
+
+    if ($this->_getParam('delete')) {
+      $this->_view->selection
+        ->removeNotice($this->_view->record)
+        ->save();
+      $this->_helper->notify($this->_('Le document a bien été retiré'),
+                             ['status' => 'delete']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+  }
+
+
+  public function creerSelectionAction() {
+    $this->_view->titre = $this->_view->_('Créer une sélection');
+    $this->_view->form = new ZendAfi_Form();
+    $this->_view->form
+      ->addElement('text',
+                   'libelle',
+                   ['placeholder' => 'Titre de la sélection'])
+      ->addUniqDisplayGroup('group')
+      ->populate($this->_request->getParams())
+      ->setAction($this->_view->url());
+
+    if(!$this->_request->isPost())
+      return;
+
+    $selection = Class_PanierNotice::newForUser($this->_user);
+    $selection->setLibelle($this->_request->getPost('libelle'));
+
+    if(!$this->_view->form->isValidModelAndArray($selection, $this->_request->getPost()))
+      return;
+
+    $selection->save();
+    $selection->index();
+    return $this->_redirectClose($this->_getReferer());
+  }
+
+
+  public function supprimerLaSelectionAction() {
+    $this->_view->titre = $this->_('Supprimer une sélection');
+
+    $this->_view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
+                              ? $selection
+                              : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
+                                                                 'libelle' => $this->_getParam('selection_label')]));
+
+    if (!$this->_view->selection) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->selection->canBeEditedByMe()) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if ($this->_getParam('delete')) {
+      $this->_view->selection->delete();
+      $this->_helper->notify($this->_('La sélection a bien été supprimée'),
+                             ['status' => 'delete']);
+      return $this->_redirectClose($this->_view->url(['action' => 'selections']));
+    }
+  }
+
+
+  public function renommerLaSelectionAction() {
+    $this->_view->titre = $this->_('Renommer une sélection');
+
+    $this->_view->selection = (($selection = Class_PanierNotice::find($this->_getParam('selection_id')))
+                              ? $selection
+                              : Class_PanierNotice::findFirstBy(['id_user' => $this->_user->getId(),
+                                                                 'libelle' => $this->_getParam('selection_label')]));
+
+    if (!$this->_view->selection) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->selection->canBeEditedByMe()) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    $this->_view->form = new ZendAfi_Form();
+    $this->_view->form
+      ->addElement('text',
+                   'libelle',
+                   ['value' => $this->_view->selection->getLibelle(),
+                    'placeholder' => 'Titre de la sélection'])
+      ->addUniqDisplayGroup('group')
+      ->setAction($this->_view->url());
+
+    if(!$this->_request->isPost())
+      return;
+
+    $this->_view->selection->setLibelle($this->_request->getPost('libelle'));
+
+    if(!$this->_view->form->isValidModelAndArray($selection, $this->_request->getPost()))
+      return;
+
+    $selection->save();
+    $selection->index();
+
+    $this->_helper->notify($this->_('La sélection a bien été renommée.'), ['status' => 'success']);
+    return $this->_redirectClose($this->_getReferer());
+  }
+
+
+  public function mesAvisAction() { }
+
+  public function cardsAction() { }
+
+  public function configurationsAction() { }
+
+  public function agendaAction() { }
+
+
+  public function donnerDesAvisAction() {
+    $params = $this->_request->getParams();
+    unset($params['by_work']);
+    $this->_view->criteria =
+      (new Class_CriteresRecherche())
+      ->setParams(array_merge($params,
+                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
+  }
+
+
+  public function suivreUneRechercheAction() {
+    $params = $this->_request->getParams();
+    unset($params['by_work']);
+    $this->_view->criteria =
+      (new Class_CriteresRecherche())
+      ->setParams(array_merge($params,
+                              ['tri' => Class_CriteresRecherche::SORT_PUBLICATION_DESC]));
+  }
+
+
+  public function exporterLaSelectionAction() {
+    $this->_view->titre = $this->_('Exporter la sélection');
+
+    if (!$this->_view->selection = Class_PanierNotice::find($this->_getParam('selection_id'))) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$this->_view->selection->canBeEditedByMe()) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if (!$format = $this->_getParam('format'))
+      return;
+
+    $data = ('unimarc' == $format)
+      ? $this->_view->selection->toUnimarcIso2709()
+      : $this->_view->selection->toTabbedList();
+
+    $this->getHelper('ViewRenderer')->setNoRender();
+
+    $name = sprintf('%s_id_%s_%s',
+                    (new Class_TimeSource)->dateYmd(),
+                    $this->_view->selection->getId(),
+                    str_replace(' ', '_', $this->_view->selection->getLibelle()));
+
+    $response = $this->_response;
+    $response->clearAllHeaders();
+    $response->setHeader('Content-Type', 'text/plain; name="' . $name . '.txt"', true);
+    $response->setHeader('Content-Disposition', 'attachment; filename="' . $name . '.txt"', true);
+    $response->setHeader('Content-Transfer-Encoding', 'base64', true);
+    $response->setHeader('Expires', '0');
+    $response->setHeader('Cache-Control', 'no-cache, must-revalidate');
+    $response->setHeader('Pragma', 'no-cache');
+    $response->setHeader('Access-Control-Allow-Origin', '*');
+    $response->setBody($data);
+  }
+
+
+  public function changerMonMotDePasseAction() {
+    if (Class_AdminVar::get('INTERDIRE_MODIF_FICHE_ABONNE')) {
+      $this->_helper->notify($this->_('Vos informations ne peuvent pas être modifiées.'),
+                             ['status' => 'error']);
+      return $this->_redirectClose($this->_view->absoluteUrl(['action' => 'informations']));
+    }
+
+    if ((new Class_User_EditFormHelper($this->_user,
+                                       $this->_view,
+                                       $this->_request))
+        ->beChangePassword()
+        ->proceed()) {
+      $this->_helper->notify($this->_('Votre mot de passe à bien été changé.'),
+                             ['status' => 'success']);
+      $this->_redirectClose($this->_view->absoluteUrl(['action' => 'informations']));
+    }
+  }
+
+
+  public function ajaxLoansAction() {
+    session_write_close();
+
+    $callback = function() {
+      return $this->_view->abonne_AjaxLoans($this->_user);
+    };
+    return $this->_helper->ajax($callback);
+  }
+
+
+  public function modifierAction() {
+    if (Class_AdminVar::get('INTERDIRE_MODIF_FICHE_ABONNE')) {
+      $this->_helper->notify($this->_('Vos informations ne peuvent pas être modifiées.'),
+                             ['status' => 'error']);
+      return $this->_redirectClose($this->_view->absoluteUrl(['action' => 'informations']));
+    }
+
+    if ((new Class_User_EditFormHelper($this->_user,
+                                       $this->_view,
+                                       $this->_request))
+        ->beEditUser()
+        ->proceed()) {
+      $this->_helper->notify($this->_('Vos informations ont bien été modifiées.'),
+                             ['status' => 'success']);
+      $this->_redirectClose($this->_view->absoluteUrl(['action' => 'informations']));
+    }
+  }
+
+
+  public function selectionsDansLesDomainesAction() {
+    $this->_checkAdmin();
+  }
+
+
+  public function selectionsDesProfessionnelsAction() {
+    $this->_checkAdmin();
+  }
+
+
+  protected function _checkAdmin() {
+    if ( ! Class_Users::isCurrentUserAdmin()) {
+      $this->_helper->notify($this->_('Vous n\'avez pas le droit d\'accéder à cette page.'), ['status' => 'error']);
+      return $this->_redirectClose($this->_getReferer());
+    }
+  }
+
+
+  public function changeImageAction() {
+    $this->_view->user = $this->_user;
+  }
+
+
+  public function pickImageAction() {
+    Class_FileManager::beOpenBar();
+
+    if (!$image = Class_FileManager::find($this->_getParam('id'))) {
+      $this->_helper->notify($this->_('Une erreur c\'est produite. Votre image de profil n\'a pas été modifiée.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    if ($this->_user->canAccessBackend()) {
+      (new Class_User_Settings($this->_user))->setProfileImage($image->getPath());
+      $this->_user->save();
+
+      $this->_helper->notify($this->_('Votre image de profil a bien été modifiée.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    $allowed_paths = explode(';', Class_AdminVar::getValueOrDefault('USER_PROFILE_IMAGES'));
+    $allowed_images = [];
+
+    foreach ($allowed_paths as $path)
+      if ($image_var = Class_FileManager::find($path))
+        $allowed_images [] = $image_var;
+
+    foreach ($allowed_images as $allowed_image) {
+      if ($image->getId() == $allowed_image->getId()) {
+        (new Class_User_Settings($this->_user))->setProfileImage($image->getPath());
+        $this->_user->save();
+
+        $this->_helper->notify($this->_('Votre image de profil a bien été modifiée.'));
+        return $this->_redirectClose($this->_getReferer());
+      }
+    }
+
+    $this->_helper->notify($this->_('Une erreur c\'est produite. Votre image de profil n\'a pas été modifiée.'));
+    return $this->_redirectClose($this->_getReferer());
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Admin/EditAvis.php b/library/ZendAfi/Form/Admin/EditAvis.php
index c7556b835ac62190d5d6bcabf1db6133f5cce4e2..cf8d191e23cea46d37f06639b9260a9100f131d6 100644
--- a/library/ZendAfi/Form/Admin/EditAvis.php
+++ b/library/ZendAfi/Form/Admin/EditAvis.php
@@ -33,7 +33,9 @@ class ZendAfi_Form_Admin_EditAvis extends ZendAfi_Form {
                                       'allowEmpty' => false])
       ->addElement('textarea', 'avis', ['label' => $this->_('Contenu de l\'avis'),
                                         'required' => true,
-                                        'allowEmpty' => false]);
+                                        'allowEmpty' => false,
+                                        'rows' => 15])
+      ->addUniqDisplayGroup('default');
   }
 
 
@@ -48,12 +50,13 @@ class ZendAfi_Form_Admin_EditAvis extends ZendAfi_Form {
 
 
   public function addPermalinkFor($avis) {
-    if($avis->isAvisNotice()) {
+    if ($avis->isAvisNotice()) {
       $this->addElement('localRecordUrl', 'url',
-                        ['label' => 'Permalien de la notice concernée',
+                        ['label' => $this->_('Permalien de la notice concernée'),
                          'size' => '100']);
+      $this->addToDisplayGroup(['url'], 'default');
     }
+
     return $this;
   }
 }
-?>
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Element/Tel.php b/library/ZendAfi/Form/Element/Tel.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d9720d823b36aff2e8574e139559a4ed8b22b07
--- /dev/null
+++ b/library/ZendAfi/Form/Element/Tel.php
@@ -0,0 +1,29 @@
+<?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 ZendAfi_Form_Element_Tel extends Zend_Form_Element_Text {
+  public $helper = 'formTel';
+
+  public function init() {
+    $this->addValidator(new ZendAfi_Validate_PhoneNumber());
+  }
+}
diff --git a/library/ZendAfi/Validate/VignetteUrl.php b/library/ZendAfi/Validate/VignetteUrl.php
index e4dcacf60d4f7a4f6e9c096127d5d39ce6264a2d..97bf110a54f8d63b3d3c52e3fd314aed783ed9f4 100644
--- a/library/ZendAfi/Validate/VignetteUrl.php
+++ b/library/ZendAfi/Validate/VignetteUrl.php
@@ -34,10 +34,11 @@ class ZendAfi_Validate_VignetteUrl extends Zend_Validate_Abstract {
 
 
   public function isValid($value) {
-    foreach($this->_valid_url_patterns as $pattern) {
+    $this->_setValue($value);
+
+    foreach($this->_valid_url_patterns as $pattern)
       if (preg_match('/' . $pattern . '/i', $value))
         return true;
-    }
 
     $this->_error(self::INVALID_URL_FORMAT);
     return false;
diff --git a/library/ZendAfi/View/Helper/Avis.php b/library/ZendAfi/View/Helper/Avis.php
index 2cfe418a97a7aa306bd77b4567201cb772a24d49..d1222add11911d7892957d440b7228959b2aecef 100644
--- a/library/ZendAfi/View/Helper/Avis.php
+++ b/library/ZendAfi/View/Helper/Avis.php
@@ -101,6 +101,8 @@ class ZendAfi_View_Helper_Avis extends ZendAfi_View_Helper_BaseHelper {
   protected function avisNotice($avis) {
     $url_vignette = URL_ADMIN_IMG . 'supports/vignette_vide.gif';
     $title = $this->_('Oeuvre non trouvée');
+    if (Class_Users::isCurrentUserAdmin())
+      $title .= ' (' . $avis->getClefOeuvre() . ')';
 
     $type_doc_id = 0;
     $type_doc_label = $this->_('Inconnu');
diff --git a/library/ZendAfi/View/Helper/BaseHelper.php b/library/ZendAfi/View/Helper/BaseHelper.php
index 5bd29407eea03d8462832a60858a550af5d14021..e6d92f6709cc906fa17fc73fbc1bf345cc5d471d 100644
--- a/library/ZendAfi/View/Helper/BaseHelper.php
+++ b/library/ZendAfi/View/Helper/BaseHelper.php
@@ -94,4 +94,9 @@ class ZendAfi_View_Helper_BaseHelper extends Zend_View_Helper_HtmlElement {
   protected function _renderTable($description, $models) {
     return call_user_func_array([$this->view, 'renderTable'], func_get_args());
   }
+
+
+  protected function _templateIco() {
+    return call_user_func_array([$this->view, 'templateIco'], func_get_args());
+  }
 }
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/FormTel.php b/library/ZendAfi/View/Helper/FormTel.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2ed13b8b6427560e12f2ecb3e2e1d89d9848203
--- /dev/null
+++ b/library/ZendAfi/View/Helper/FormTel.php
@@ -0,0 +1,32 @@
+<?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 ZendAfi_View_Helper_FormTel extends ZendAfi_View_Helper_FormHTML5 {
+  public function formTel($name, $value = null, $attribs = null) {
+    return $this->renderElement($name, $value, $attribs);
+  }
+
+
+  public function inputType() {
+    return 'tel';
+  }
+}
diff --git a/library/ZendAfi/View/Helper/Notice/Abstract.php b/library/ZendAfi/View/Helper/Notice/Abstract.php
index b183a0d6c724a9eb8920db987abeb47b2a910f8f..49ac6eee764b8d3c9c52e8b47da607bce235fb79 100644
--- a/library/ZendAfi/View/Helper/Notice/Abstract.php
+++ b/library/ZendAfi/View/Helper/Notice/Abstract.php
@@ -18,9 +18,7 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-abstract class ZendAfi_View_Helper_Notice_Abstract extends Zend_View_Helper_HtmlElement {
-  use Trait_Translator;
-
+abstract class ZendAfi_View_Helper_Notice_Abstract extends ZendAfi_View_Helper_BaseHelper {
   const BLOC_DEPLIE = 1;
   const BLOC_FERME = 2;
   const ONGLET = 3;
@@ -47,12 +45,15 @@ abstract class ZendAfi_View_Helper_Notice_Abstract extends Zend_View_Helper_Html
 
 
   public function selectOngletsFromPreferences($preferences, $aff_values, $notice) {
-    $onglets = [];
-
-    foreach($preferences['onglets'] as $nom => $config) {
-      if (!in_array((int)$config['aff'], $aff_values))
-        continue;
+    if (! $preferences = array_filter($preferences['onglets'],
+                                      function($pref) use($aff_values)
+                                      {
+                                        return in_array((int)$pref['aff'], $aff_values);
+                                      }))
+      return [];
 
+    $onglets = [];
+    foreach($preferences as $nom => $config) {
       $onglet = Class_Codification::getInstance()->getOnglet($nom);
       if ($config['titre'])
         $onglet->setLibelle($config['titre']);
@@ -64,11 +65,18 @@ abstract class ZendAfi_View_Helper_Notice_Abstract extends Zend_View_Helper_Html
     }
 
     uasort($onglets,
-           function($cfg_a, $cfg_b)
+           function($a, $b)
            {
-             return (int)$cfg_a->getOrder() - (int)$cfg_b->getOrder();
+             return (int)$a->getOrder() - (int)$b->getOrder();
            });
 
     return $onglets;
   }
+
+
+  protected function _selectBlocksFromPreferences($preferences, $record) {
+    return $this->selectOngletsFromPreferences($preferences,
+                                               [static::BLOC_DEPLIE, static::BLOC_FERME],
+                                               $record);
+  }
 }
diff --git a/library/ZendAfi/View/Helper/Notice/Blocs.php b/library/ZendAfi/View/Helper/Notice/Blocs.php
index e5936181eb69004d1bab2d6cc969774905a63395..7f9b76b4f7f143ec6e3b0409f6f079e10f2e58cc 100644
--- a/library/ZendAfi/View/Helper/Notice/Blocs.php
+++ b/library/ZendAfi/View/Helper/Notice/Blocs.php
@@ -18,60 +18,69 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
+
+
 class ZendAfi_View_Helper_Notice_Blocs extends ZendAfi_View_Helper_Notice_Abstract {
+  protected
+    $_record,
+    $_preferences;
+
   public function notice_Blocs($notice, $preferences) {
-    if (!array_isset('onglets', $preferences))
+    if (!array_isset('onglets', $preferences) || !$notice)
       return '';
 
-    $blocs = $this->getBlocsFromPreferences($preferences, $notice);
+    $this->_record = $notice;
+    $this->_preferences = $preferences;
 
-    return $this->renderBlocsForNotice($blocs, $notice);
+    return $this->_renderBlocsForNotice();
   }
 
 
-  public function getBlocsFromPreferences($preferences, $notice) {
-    return $this->selectOngletsFromPreferences($preferences,
-                                               [self::BLOC_DEPLIE, self::BLOC_FERME],
-                                               $notice);
-  }
-
-
-  public function renderBlocsForNotice($blocs, $notice) {
-    $id = $notice->getId();
-    $isbn = $notice->getIsbn();
-
+  protected function _renderBlocsForNotice() {
+    $id = $this->_record->getId();
     $html = '';
-
-    // Blocs
     $i = 0;
-    foreach($blocs as $bloc)  {
-      $type = $bloc->getType();
-      $id_bloc="bloc_".$id."_".$i++;
-      $js ='infos_bloc'.$this->getOnclick($type, $notice, $id_bloc);
-
-      if ((int)$bloc->getDisplayMode()==1)
-        Class_ScriptLoader::getInstance()
-          ->addJQueryReady('infos_bloc'. str_replace('this.id',
-                                                     '"'.$id_bloc.'"',
-                                                     $this->getOnclick($type, $notice, $id_bloc)));
-
-        Class_ScriptLoader::getInstance()
-        ->addJQueryReady('$("#'.$id_bloc.'").click(function(){'.$js.'})');
-
-      // Titre
-        $html.='<div  class="'.$type.' block_info_notice">';
-        $html.='<div id="'.$id_bloc.'" class="notice_bloc_titre"><img id="I'.$id_bloc.'" src="'.URL_IMG.'bouton/plus_carre.gif"  alt="Déplier"  /><h2>'.$bloc->getLibelle().'</h2></div>';
-        $html.='<div id="'.$id_bloc.'_contenu_row"></div>';
-        $html.=  '<div id="'.$id_bloc.'_contenu" class="notice_bloc">';
-        $html.=  '<div class="notice_patience" ><img src="'.URL_IMG.'patience.gif" alt="'.$this->_('Chargement en cours').'" />';
-        $html.=     $this->_translate->_('Veuillez patienter : lecture en cours...');
-        $html.=  '</div>';
-        $html.='</div>';
-        $html.='</div>';
+    foreach($this->_selectBlocksFromPreferences($this->_preferences, $this->_record)
+            as $bloc) {
+      $id_bloc = "bloc_" . $id . "_" . $i++;
+      $html .= $this->_renderBloc($bloc, $id_bloc);
     }
 
     return $html;
   }
 
+
+  protected function _renderBloc($bloc, $id_bloc) {
+    $type = $bloc->getType();
+    $js = 'infos_bloc' . $this->getOnclick($type, $this->_record, $id_bloc) . ';';
+
+    if ($bloc->isOpenBlock()) {
+      Class_ScriptLoader::getInstance()
+        ->addJQueryReady('$("#'.$id_bloc.'_contenu").hide();' // needed by toggling logic in infos_bloc
+                         . str_replace('this.id', '"'.$id_bloc.'"', $js));
+    }
+
+    Class_ScriptLoader::getInstance()
+      ->addJQueryReady('$("#'.$id_bloc.'").click(function(){' . $js .'});');
+
+    $profil = Class_Profil::getCurrentProfil();
+
+    return $this
+      ->_div(['class' => $type . ' block_info_notice'],
+             $this->_div(['id' => $id_bloc,
+                          'class' => 'notice_bloc_titre'],
+                         $this->view->tagImg($profil->getUrlImage('bouton/plus_carre.gif'),
+                                             ['id' => 'I'.$id_bloc,
+                                              'alt' => $this->_('Déplier')])
+                         . $this->_tag('h2', $bloc->getLibelle()))
+
+             . $this->_div(['id' => $id_bloc . '_contenu_row'], '')
+
+             . $this->_div(['id' => $id_bloc . '_contenu',
+                            'class' => 'notice_bloc' . ($bloc->isOpenBlock() ? ' notice_bloc_open' : '')],
+                           $this->_div(['class' => 'notice_patience'],
+                                       $this->view->tagImg($profil->getUrlImage('patience.gif'))
+                                       . $this->_('Veuillez patienter : lecture en cours...')))
+      );
+  }
 }
-?>
diff --git a/library/ZendAfi/View/Helper/TagAnchor.php b/library/ZendAfi/View/Helper/TagAnchor.php
index dafa86b4392822dbf25d06e313dcef5f05337e34..a5f4e29cbd227c60e324aa053069f48b5a2822ae 100644
--- a/library/ZendAfi/View/Helper/TagAnchor.php
+++ b/library/ZendAfi/View/Helper/TagAnchor.php
@@ -33,7 +33,13 @@ class ZendAfi_View_Helper_TagAnchor extends ZendAfi_View_Helper_BaseHelper {
     if (is_array($url))
       $url = $this->view->url($url);
 
-    return $this->_tag('a', $text , array_merge(['href' => $url],
-                                                $attribs));
+    $attribs = array_merge(['href' => $url],
+                           $attribs);
+
+    $attribs = (new Class_AnchorTarget)->injectIn($attribs);
+
+    return $this->_tag('a',
+                       $text ,
+                       $attribs);
   }
 }
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/TagCriteresRecherche.php b/library/ZendAfi/View/Helper/TagCriteresRecherche.php
index 2408cfb1f57fc8ed3e00e7954a8064bf5f52f1e9..2f733248583ea35794f0c4d6bc5b926c2b5d4f02 100644
--- a/library/ZendAfi/View/Helper/TagCriteresRecherche.php
+++ b/library/ZendAfi/View/Helper/TagCriteresRecherche.php
@@ -51,12 +51,15 @@ class ZendAfi_View_Helper_TagCriteresRecherche extends ZendAfi_View_Helper_BaseH
                                   'or' => $this->_(' ou '),
                                   'and not' => $this->_(' sauf ')];
 
-    $this->_libelles_criteres = [
-      'titres' => $this->_('Titre'),
-      'auteurs' => ($libelle = Class_AdminVar::get('FACETTE_AUTEUR_LIBELLE')) ? $libelle : $this->_('Auteur'),
-      'matieres' => ($libelle = Class_AdminVar::get('FACETTE_MATIERE_LIBELLE')) ? $libelle : $this->_('Sujet'),
-      'dewey' => ($libelle = Class_AdminVar::get('FACETTE_DEWEY_LIBELLE')) ? $libelle :$this->_('Dewey / pcdm4'),
-      'collection' => $this->_('Collection') ];
+    $this->_libelles_criteres =
+      [
+       'titres' => $this->_('Titre'),
+       'auteurs' => ($libelle = Class_AdminVar::get('FACETTE_AUTEUR_LIBELLE')) ? $libelle : $this->_('Auteur'),
+       'matieres' => ($libelle = Class_AdminVar::get('FACETTE_MATIERE_LIBELLE')) ? $libelle : $this->_('Sujet'),
+       'editeur' => $this->_('Éditeur'),
+       'dewey' => ($libelle = Class_AdminVar::get('FACETTE_DEWEY_LIBELLE')) ? $libelle :$this->_('Dewey / pcdm4'),
+       'collection' => $this->_('Collection'),
+      ];
 
     $criteres_recherche->acceptVisitor($this);
   }
@@ -68,7 +71,8 @@ class ZendAfi_View_Helper_TagCriteresRecherche extends ZendAfi_View_Helper_BaseH
       $this->_libelles_criteres[$name] . ' ' .
       $this->_($type_recherche == 'commence' ? 'commence par': ':') .' ' . $value;
 
-    $url = $this->_criteres_recherche->getUrlCriteresWithoutElement('rech_'.$name);
+    $url = $this->_criteres_recherche->getUrlCriteresWithoutElements(['rech_' . $name,
+                                                                      'operateur_' . $name]);
 
     $this->htmlAppend($this->getSuppressionImgUrlForLibelle($libelle, $url));
   }
diff --git a/library/startup.php b/library/startup.php
index 3d21621457ef7f318941f9cc591a883398b6fc0a..e88b5d1d518dad8c4e9a5ae97c720ba2fabc4144 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -84,7 +84,7 @@ class Bokeh_Engine {
 
   function setupConstants() {
     defineConstant('BOKEH_MAJOR_VERSION','8.0');
-    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.114');
+    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.115');
 
     defineConstant('BOKEH_REMOTE_FILES', 'https://git.afi-sa.net/afi/opacce/');
 
diff --git a/library/templates/Intonation/Assets/css/intonation.css b/library/templates/Intonation/Assets/css/intonation.css
index b10019b02a1eca046555442c32db4e6805888b58..db900d60ff5746b632000244c517a0a4293d0afb 100644
--- a/library/templates/Intonation/Assets/css/intonation.css
+++ b/library/templates/Intonation/Assets/css/intonation.css
@@ -87,6 +87,7 @@ div:hover > .img_as_background {
     height: 100%;
 }
 
+.card-link img:first-child,
 i:first-child,
 .widget.rech_simple .dropdown-menu i:first-child,
 .widget.rech_simple .criteres_recherche i:first-child,
diff --git a/library/templates/Intonation/Library/LastFm.php b/library/templates/Intonation/Library/LastFm.php
index a1b01683fa5793b8c2c3117f59ab730c8e580c3f..5a4abf1d8ced69668513f7c9e37942a58bc7d0a6 100644
--- a/library/templates/Intonation/Library/LastFm.php
+++ b/library/templates/Intonation/Library/LastFm.php
@@ -39,12 +39,12 @@ class Intonation_Library_LastFm extends Class_Entity {
 
 
   public function getAlbumPictures() {
-    return $this->_getPictures($this->getModel()->getAuteurPrincipal());
+    return $this->_getPictures($this->getModel()->getMainAuthorFromCodif());
   }
 
 
   public function getAlbumTracks() {
-    return $this->_getTracks($this->getModel()->getAuteurPrincipal(), $this->getModel()->getTitrePrincipal());
+    return $this->_getTracks($this->getModel()->getMainAuthorFromCodif(), $this->getModel()->getTitrePrincipal(' '));
   }
 
 
@@ -101,8 +101,8 @@ class Intonation_Library_LastFm extends Class_Entity {
                                        return $elements;
 
                                      $title = $this->_('Illustration du document %s de %s',
-                                                       $this->getModel()->getTitrePrincipal(),
-                                                       $this->getModel()->getAuteurPrincipal(' '));
+                                                       $this->getModel()->getTitrePrincipal(' '),
+                                                       $this->getModel()->getMainAuthorFromCodif());
 
                                      $elements [] =
                                        new Intonation_Library_Picture(['Src' => $url,
diff --git a/library/templates/Intonation/Library/Trailer.php b/library/templates/Intonation/Library/Trailer.php
index a9ab5190ffbe4e58ca070eb225ff02c889ddc126..cc5695dc397203640649bcbb703caac9f8344058 100644
--- a/library/templates/Intonation/Library/Trailer.php
+++ b/library/templates/Intonation/Library/Trailer.php
@@ -20,4 +20,9 @@
  */
 
 
-class Intonation_Library_Trailer extends Class_Entity {}
+class Intonation_Library_Trailer extends Class_Entity {
+
+  public function isDisabled() {
+    return (bool) $this->get('Disabled');
+  }
+}
diff --git a/library/templates/Intonation/Library/Trailers.php b/library/templates/Intonation/Library/Trailers.php
index 884b2d56d1aa1c14b81bdcfb8f0e1f3edb4c133d..740b2b5831385fa64e70204f3ec120f705db3364 100644
--- a/library/templates/Intonation/Library/Trailers.php
+++ b/library/templates/Intonation/Library/Trailers.php
@@ -40,12 +40,13 @@ class Intonation_Library_Trailers {
 
   public function trailers() {
     $args = ['titre' => $this->_model->getRecordTitle(),
-             'auteur' => $this->_model->getAuteurPrincipal(),
+             'auteur' => $this->_model->getMainAuthorFromCodif(),
              'clef_oeuvre' => $this->_model->getClefOeuvre(),
              'type_doc' => $this->_model->getFamilleId()];
 
     $trailer_service = (new Class_WebService_AllServices);
     $data = $trailer_service->runServiceAfi($trailer_service::SVC_GET_TRAILER, $args);
+
     if ( empty($data) )
       return [];
 
@@ -59,8 +60,12 @@ class Intonation_Library_Trailers {
     if ( !$url = $match[1])
       return [];
 
+    $disabled = isset($data[Class_WebService_AllServices::DISABLE_TRAILER])
+      && ($data[Class_WebService_AllServices::DISABLE_TRAILER] == Class_WebService_AllServices::TRAILER_DISABLED);
+
     return [(new Intonation_Library_Trailer(['Source' => $data['source'],
                                              'Model' => $this->_model,
-                                             'Url' => $url]))];
+                                             'Url' => $url,
+                                             'Disabled' => $disabled]))];
   }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php b/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
index 2cba3fb1d6c7ba814b3f0d862ab2ceb62533ca10..9b779be5e818c8a20b2b0865bdad59c9e15a4d6b 100644
--- a/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
+++ b/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
@@ -135,9 +135,7 @@ class Intonation_Library_View_Wrapper_ActivitySession
     if ($library = $this->_model->getLibrary())
       $location
         ->setTag('a')
-        ->setUrl($this->_view->url(['controller' => 'bib',
-                                    'action' => 'en-lire-plus',
-                                    'id' => $library->getId()]));
+        ->setUrl($library->getUrl());
 
     $badges [] = $location;
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/Article.php b/library/templates/Intonation/Library/View/Wrapper/Article.php
index ad0435f67e05b2da32dfb1cd94d4d6b075e772b5..88b34994f594b2b281d41e3af48cf833584fc1f1 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Article.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Article.php
@@ -122,9 +122,7 @@ class Intonation_Library_View_Wrapper_Article extends Intonation_Library_View_Wr
     if (($library = $this->_model->getBib()) && !$library->isPortail())
       $badges [] = ((new Intonation_Library_Badge)
                     ->setTag('a')
-                    ->setUrl($this->_view->url(['controller' => 'bib',
-                                                'action' => 'en-lire-plus',
-                                                'id' => $library->getId()]))
+                    ->setUrl($library->getUrl())
                     ->setClass('badge_article_library')
                     ->setImage(Class_Template::current()
                                ->getIco($this->_view,
diff --git a/library/templates/Intonation/Library/View/Wrapper/Item.php b/library/templates/Intonation/Library/View/Wrapper/Item.php
index af115b63d903b913f6cb8cf39bdffff3f0b1ea2e..71afb82246d1d7d419832993aa36a6e71ffdd92e 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Item.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Item.php
@@ -70,9 +70,7 @@ class Intonation_Library_View_Wrapper_Item extends Intonation_Library_View_Wrapp
     if (!$this->_isCheckedInSettings('bib'))
       return null;
 
-    return new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'bib',
-                                                                    'action' => 'en-lire-plus',
-                                                                    'id' => $this->_model->getIdBib()]),
+    return new Intonation_Library_Link(['Url' => $this->_model->getLibraryUrl(),
                                         'Image' => Class_Template::current()->getIco($this->_view,
                                                                                      'library',
                                                                                      'library'),
diff --git a/library/templates/Intonation/Library/View/Wrapper/Pro.php b/library/templates/Intonation/Library/View/Wrapper/Pro.php
index 443d64944eaec3cfeb232a3c17b41ae54ef2d681..9d882731bdea19e4c58a2f247a8d36f2e3aa6c14 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Pro.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Pro.php
@@ -63,9 +63,7 @@ class Intonation_Library_View_Wrapper_Pro extends Intonation_Library_View_Wrappe
     if ($user_library = $this->_model->getLibelleBib())
       $badges [] = ((new Intonation_Library_Badge)
                     ->setTag('a')
-                    ->setUrl($this->_view->url(['controller' => 'bib',
-                                                'action' => 'en-lire-plus',
-                                                'id' => $this->_model->getIdSite()]))
+                    ->setUrl($this->_model->getLibraryUrl())
                     ->setClass('badge-secondary')
                     ->setImage(Class_Template::current()
                                ->getIco($this->_view,
diff --git a/library/templates/Intonation/Library/View/Wrapper/Record.php b/library/templates/Intonation/Library/View/Wrapper/Record.php
index 4b7f437edd8317acb0b08019bff4834bc684e0f5..79fd42d18a82ed138827c45f86a77096083f95d6 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Record.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Record.php
@@ -38,7 +38,7 @@ class Intonation_Library_View_Wrapper_Record extends Intonation_Library_View_Wra
 
 
   public function getSecondaryTitle() {
-    return $this->_model->getAuteurPrincipal();
+    return $this->_model->getMainAuthorFromCodif();
   }
 
 
@@ -137,7 +137,7 @@ class Intonation_Library_View_Wrapper_Record extends Intonation_Library_View_Wra
 
     return new Intonation_Library_Link(['Url' => $author_field->getUrlForLink(),
                                         'Image' => $this->getSecondaryIco(),
-                                        'Text' =>  $author,
+                                        'Text' =>  $author_field->getLabel(),
                                         'Title' => $author_field->getTitle()]);
   }
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/Record/RichContent/Author.php b/library/templates/Intonation/Library/View/Wrapper/Record/RichContent/Author.php
index a817e2af266c0aa109355d285e095c9da645dfca..e18013dab1517a983af817fa941eb2f1bb1bcf0f 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Record/RichContent/Author.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Record/RichContent/Author.php
@@ -27,7 +27,7 @@ class Intonation_Library_View_Wrapper_Record_RichContent_Author extends Intonati
 
   public function getTitle() {
     return $this->_('Auteur principal : %s',
-                    $this->_model->getAuteurPrincipal());
+                    $this->_model->getMainAuthorFromCodif());
   }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/RendezVous.php b/library/templates/Intonation/Library/View/Wrapper/RendezVous.php
index a7ef0ef40f35cfcefbaa548cbad329c02539bdff..3e76cfb92a3072d98288a205a686d8f786b7aee2 100644
--- a/library/templates/Intonation/Library/View/Wrapper/RendezVous.php
+++ b/library/templates/Intonation/Library/View/Wrapper/RendezVous.php
@@ -95,9 +95,7 @@ class Intonation_Library_View_Wrapper_RendezVous extends Intonation_Library_View
     if ($library = $this->_model->getLibrary())
       $location
         ->setTag('a')
-        ->setUrl($this->_view->url(['controller' => 'bib',
-                                    'action' => 'en-lire-plus',
-                                    'id' => $library->getId()]));
+        ->setUrl($library->getUrl());
 
     $badges [] = $location;
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/RssItem.php b/library/templates/Intonation/Library/View/Wrapper/RssItem.php
index aa8db757d0303a364864108ab5f8a4e7c2c40673..cd3587c6f59721319c4c495c715e11b4d5b79817 100644
--- a/library/templates/Intonation/Library/View/Wrapper/RssItem.php
+++ b/library/templates/Intonation/Library/View/Wrapper/RssItem.php
@@ -22,13 +22,48 @@
 
 class Intonation_Library_View_Wrapper_RssItem extends Intonation_Library_View_Wrapper_Abstract {
 
+
+  protected $_title,
+    $_link,
+    $_picture,
+    $_description,
+    $_pub_date;
+
+
+  public function __sleep() {
+    $this->_title = $this->getMainTitle();
+    $this->_link = $this->_getLink();
+    $this->_first_image_url = $this->getPicture();
+    $this->_pub_date = $this->_getPubDate();
+    $this->_description = $this->getDescription();
+
+    return array_merge(parent::__sleep(),
+                       ['_title',
+                        '_link',
+                        '_picture',
+                        '_description',
+                        '_pub_date']);
+  }
+
+
   public function getMainTitle() {
-    return $this->_model->getTitle();
+    if ($this->_title)
+      return $this->_title;
+
+    return $this->_title = $this->_model->getTitle();
+  }
+
+
+  protected function _getLink() {
+    if ($this->_link)
+      return $this->_link;
+
+    return $this->_link = $this->_model->getLink();
   }
 
 
   public function getMainLink() {
-    return new Intonation_Library_Link(['Url' => $this->_model->getLink(),
+    return new Intonation_Library_Link(['Url' => $this->_getLink(),
                                         'Text' => $this->_view->_('Accéder au contenu'),
                                         'Title' => $this->_view->_('Accéder au contenu de %s', $this->getMainTitle()),
                                         'Image' => Class_Template::current()->getIco($this->_view,
@@ -38,7 +73,10 @@ class Intonation_Library_View_Wrapper_RssItem extends Intonation_Library_View_Wr
 
 
   public function getPicture() {
-    return $this->_model->getFirstImageURL();
+    if ($this->_picture)
+      return $this->_picture;
+
+    return $this->_picture =  $this->_model->getFirstImageURL();
   }
 
 
@@ -64,7 +102,11 @@ class Intonation_Library_View_Wrapper_RssItem extends Intonation_Library_View_Wr
 
 
   public function getDescription() {
-    return
+    if ($this->_description)
+      return $this->_description;
+
+
+    return $this->_description =
       $this->getBadges()
       . $this->_model->getDescription();
   }
@@ -83,13 +125,21 @@ class Intonation_Library_View_Wrapper_RssItem extends Intonation_Library_View_Wr
     $badges = [(new Intonation_Library_Badge)
                ->setTag('span')
                ->setClass('badge-info')
-               ->setText($this->_model->getPubDate())
+               ->setText($this->_getPubDate())
                ->setTitle($this->_('Date de diffusion de %s', $this->getMainTitle()))];
 
     return $this->_view->renderBadges($badges, $this);
   }
 
 
+  protected function _getPubDate() {
+    if ($this->_pub_date)
+      return $this->_pub_date;
+
+    return $this->_pub_date = $this->_model->getPubDate();
+  }
+
+
   public function getDocType() {
   }
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/Selection.php b/library/templates/Intonation/Library/View/Wrapper/Selection.php
index a02ee344e1e12d94eeeb0a3c159aa5e6d44cd55b..fcdf8a6de6a40218718b5c6d1716ad66a8d076df 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Selection.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Selection.php
@@ -134,11 +134,38 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                 ->setTitle($this->_('La date de dernière mise à jour de la sélection %s',
                                     $this->_model->getLibelle())))];
 
+    if ( $number_of_domains = $this->_model->numberOfDomains())
+      $badges [] =
+        ((new Intonation_Library_Badge)
+         ->setTag('span')
+         ->setClass('badge-success')
+         ->setText($this->_plural($number_of_domains,
+                                  'aucun domaine lié',
+                                  '1 domaine lié',
+                                  '%s domaines liés',
+                                  $number_of_domains))
+         ->setTitle($this->_('Le nombre de domaines liés à la sélection %s', $this->_model->getLibelle())));
+
     return $this->_view->renderBadges($badges, $this);
   }
 
 
   public function getActions() {
+    $actions = $this->_getActions();
+    $button = new Intonation_Library_Link(['Text' => $this->_('Plus'),
+                                           'Attribs' => ['class' => 'more_action'],
+                                           'Title' => $this->_('Voir plus d\'actions pour la sélection %s',
+                                                               $this->_model->getLibelle()),
+                                           'Identifier' => 'view_more_record_actions',
+                                           'Image' => Class_Template::current()->getIco($this->_view,
+                                                                                        'more',
+                                                                                        'utils')]);
+
+    return [$this->_view->renderDropdown($this->_view->renderActions($actions), $button)];
+  }
+
+
+  protected function _getActions() {
     $actions = [new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
                                                                          'action' => 'exporter-la-selection',
                                                                          'selection_id' => $this->_model->getId()]),
@@ -154,7 +181,7 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                                                                          'action' => 'ajouter-a-la-selection',
                                                                          'selection_id' => $this->_model->getId()]),
                                              'Text' => $this->_('Ajouter'),
-                                             'Right' => $this->_model->isMine(),
+                                             'Right' => $this->_model->canBeEditedByMe(),
                                              'Title' => $this->_('Ajouter un document à la sélection %s',
                                                                  $this->_model->getLibelle()),
                                              'Image' => Class_Template::current()->getIco($this->_view,
@@ -165,7 +192,7 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                                                                          'action' => 'selection',
                                                                          'selection_id' => $this->_model->getId()]),
                                              'Text' => $this->_('Gérer'),
-                                             'Right' => $this->_model->isMine(),
+                                             'Right' => $this->_model->canBeEditedByMe(),
 
                                              'Title' => $this->_('Gérer la sélection'),
                                              'Image' => Class_Template::current()->getIco($this->_view,
@@ -181,7 +208,7 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                                                                                  'action' => 'renommer-la-selection',
                                                                                  'selection_id' => $this->_model->getId()]),
                                                      'Text' => $this->_('Renommer'),
-                                                     'Right' => $this->_model->isMine(),
+                                                     'Right' => $this->_model->canBeEditedByMe(),
                                                      'Title' => $this->_('Renommer la sélection %s',
                                                                          $this->_model->getLibelle()),
                                                      'Popup' => 'true',
@@ -189,12 +216,24 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                                                                                                   'rename',
                                                                                                   'utils')]),
 
+                        new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'panier',
+                                                                                 'action' => 'edit',
+                                                                                 'id_panier' => $this->_model->getId()]),
+                                                     'Text' => $this->_('Domaines liés'),
+                                                     'Right' => $this->_model->canBeEditedByMe(),
+                                                     'Title' => $this->_('Lier la sélection %s à un domaine',
+                                                                         $this->_model->getLibelle()),
+                                                     'Popup' => 'true',
+                                                     'Image' => Class_Template::current()->getIco($this->_view,
+                                                                                                  'external-link',
+                                                                                                  'utils')]),
+
                         new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
                                                                                  'action' => 'supprimer-la-selection',
                                                                                  'selection_id' => $this->_model->getId()]),
                                                      'Class' => 'text-danger',
                                                      'Text' => $this->_('Supprimer'),
-                                                     'Right' => $this->_model->isMine(),
+                                                     'Right' => $this->_model->canBeEditedByMe(),
                                                      'Title' => $this->_('Supprimer la sélection %s',
                                                                          $this->_model->getLibelle()),
                                                      'Popup' => 'true',
diff --git a/library/templates/Intonation/Library/View/Wrapper/User.php b/library/templates/Intonation/Library/View/Wrapper/User.php
index bd9fc93455948674275f703002af52efef733735..8d9bdcca9a58d891d39007c4df0535e7dfe9b7e5 100644
--- a/library/templates/Intonation/Library/View/Wrapper/User.php
+++ b/library/templates/Intonation/Library/View/Wrapper/User.php
@@ -105,9 +105,8 @@ class Intonation_Library_View_Wrapper_User extends Intonation_Library_View_Wrapp
       $badges [] = ((new Intonation_Library_Badge)
                     ->setTag('a')
                     ->setUrl((($library = $this->_model->getLibrary())
-                              ? $this->_view->url(['controller' => 'bib',
-                                                   'action' => 'en-lire-plus',
-                                                   'id' => $library->getId()]) : ''))
+                              ? $library->getUrl()
+                              : ''))
                     ->setClass('badge-secondary')
                     ->setImage(Class_Template::current()
                                ->getIco($this->_view,
diff --git a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsInDomains.php b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsInDomains.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8f9e583c7df44cc4f11997fbf7c37a90d5e7c18
--- /dev/null
+++ b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsInDomains.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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 Intonation_Library_View_Wrapper_User_RichContent_SelectionsInDomains extends Intonation_Library_View_Wrapper_User_RichContent_Selections {
+
+  public function getTitle() {
+    return $this->_('Sélections dans les domaines');
+  }
+
+
+  public function getContent() {
+    $actions = [
+                new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
+                                                                         'action' => 'selections']),
+                                             'Class' => 'btn btn-sm btn-secondary',
+                                             'Text' => $this->_('Mes sélections'),
+                                             'Title' => $this->_('Voir mes sélections'),
+                                             'Image' => $this->_view->abonne_MyImage($this->_model)]),
+
+
+                new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
+                                                                         'action' => 'selections-des-professionnels']),
+                                             'Class' => 'btn btn-sm btn-warning',
+                                             'Text' => $this->_('Sélections des professionnels'),
+                                             'Title' => $this->_('Voir les sélections des professionnels'),
+                                             'Image' => $this->_view->templateIco('team',
+                                                                                  'library')])];
+
+    $selections = [];
+    foreach(Class_PanierNotice::findAllWithCatalogue() as $selection)
+      $selections [] = new Intonation_Library_View_Wrapper_Selection($selection, $this->_view);
+
+    $html = [$this->_view->div(['class' => 'col-12'],
+                               $this->_view->renderCollection(new Storm_Collection($selections),
+                                                              $actions))];
+
+    return $this->_view->grid($html);
+  }
+}
diff --git a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsOfPro.php b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsOfPro.php
new file mode 100644
index 0000000000000000000000000000000000000000..17a0ff5712c73ab7fd53b46f01868ac736778261
--- /dev/null
+++ b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/SelectionsOfPro.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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 Intonation_Library_View_Wrapper_User_RichContent_SelectionsOfPro extends Intonation_Library_View_Wrapper_User_RichContent_Selections {
+
+  public function getTitle() {
+    return $this->_('Sélections des professionnels');
+  }
+
+
+  public function getContent() {
+    $actions = [
+                new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
+                                                                         'action' => 'selections']),
+                                             'Class' => 'btn btn-sm btn-secondary',
+                                             'Text' => $this->_('Mes sélections'),
+                                             'Title' => $this->_('Voir mes sélections'),
+                                             'Image' => $this->_view->abonne_MyImage($this->_model)]),
+
+
+                new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
+                                                                         'action' => 'selections-dans-les-domaines']),
+                                             'Class' => 'btn btn-sm btn-info',
+                                             'Text' => $this->_('Sélections dans les domaines'),
+                                             'Title' => $this->_('Voir les sélections rangées dans les domaines'),
+                                             'Image' => $this->_view->templateIco('search_more',
+                                                                                  'library')])];
+
+    $selections = [];
+    foreach(Class_PanierNotice::findAllBelongsToAdmin() as $selection)
+      $selections [] = new Intonation_Library_View_Wrapper_Selection($selection, $this->_view);
+
+    $html = [$this->_view->div(['class' => 'col-12'],
+                               $this->_view->renderCollection(new Storm_Collection($selections),
+                                                              $actions))];
+
+    return $this->_view->grid($html);
+  }
+}
diff --git a/library/templates/Intonation/Library/View/Wrapper/Work/RichContent/Author.php b/library/templates/Intonation/Library/View/Wrapper/Work/RichContent/Author.php
index 908822ac76d4c3d85ccdadfb36eb52a42d967a56..3a0bd2d3f7ca45734cdaaeeebee206b9d8373829 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Work/RichContent/Author.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Work/RichContent/Author.php
@@ -28,7 +28,7 @@ class Intonation_Library_View_Wrapper_Work_RichContent_Author
 
   public function getTitle() {
     return $this->_('Auteur principal : %s',
-                    $this->_model->getAuteurPrincipal());
+                    $this->_model->getMainAuthorFromCodif());
   }
 
 
diff --git a/library/templates/Intonation/Library/Widget/Carousel/View.php b/library/templates/Intonation/Library/Widget/Carousel/View.php
index bb4d48d969628423665e4029e8c121f9fb0b4731..f0df50deb8d4739ce5dedef77425b0f322a73279 100644
--- a/library/templates/Intonation/Library/Widget/Carousel/View.php
+++ b/library/templates/Intonation/Library/Widget/Carousel/View.php
@@ -135,7 +135,8 @@ abstract class Intonation_Library_Widget_Carousel_View extends Zendafi_View_Help
                                                                                                          'controller' => 'widget',
                                                                                                          'action' => 'render',
                                                                                                          'widget_id' => $this->getId(),
-                                                                                                         'profile_id' => Class_Profil::getCurrentProfil()->getId()],
+                                                                                                         'profile_id' => Class_Profil::getCurrentProfil()->getId(),
+                                                                                                         'anchor_target' => '_blank',],
                                                                                                         null,
                                                                                                         true)]),
                                                          ['class' => 'text-monospace embeded_code']),
diff --git a/library/templates/Intonation/Template.php b/library/templates/Intonation/Template.php
index 3033fac6fd7c2ad7e796f0cb72327de017931514..60bb1b8730eece51bf96784401f2a6a36afdfb0c 100644
--- a/library/templates/Intonation/Template.php
+++ b/library/templates/Intonation/Template.php
@@ -53,6 +53,7 @@ class Intonation_Template extends Class_Template {
 
 
   public function renderWidget($widget, $view) {
+    Class_AnchorTarget::setTarget($widget->getAnchorTarget());
     $widget->setSection(new Class_Entity);
     return $this->renderWidgetOn((new Intonation_System_Widget)->setSettings($widget), $view);
   }
diff --git a/library/templates/Intonation/View/Abonne/MyImage.php b/library/templates/Intonation/View/Abonne/MyImage.php
new file mode 100644
index 0000000000000000000000000000000000000000..142b41d9629f5813babda17c6ff73eeab0c46d74
--- /dev/null
+++ b/library/templates/Intonation/View/Abonne/MyImage.php
@@ -0,0 +1,34 @@
+<?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 Intonation_View_Abonne_MyImage extends ZendAfi_View_Helper_BaseHelper {
+
+  public function abonne_MyImage($me) {
+    $wrapper = (new Intonation_Library_View_Wrapper_User)
+      ->setModel($me)
+      ->setView($this->view);
+
+    return ($image = $wrapper->getPicture())
+      ? $this->view->tagImg($image, ['class' => 'user_ico'])
+      : '';
+  }
+}
diff --git a/library/templates/Intonation/View/Abonne/Name.php b/library/templates/Intonation/View/Abonne/Name.php
index 9048e5e1fe88535bd8c5e4cb916cd339b34104de..aa88dad8aaf34e02ea4e1c0dc041683f30d1faff 100644
--- a/library/templates/Intonation/View/Abonne/Name.php
+++ b/library/templates/Intonation/View/Abonne/Name.php
@@ -59,12 +59,6 @@ class Intonation_View_Abonne_Name extends ZendAfi_View_Helper_BaseHelper {
 
 
   protected function _getImage($user) {
-    $wrapper = (new Intonation_Library_View_Wrapper_User)
-      ->setModel($user)
-      ->setView($this->view);
-
-    return ($image = $wrapper->getPicture())
-      ? $this->view->tagImg($image, ['class' => 'user_ico'])
-      : '';
+    return $this->view->abonne_MyImage($user);
   }
 }
diff --git a/library/templates/Intonation/View/Abonne/Selections.php b/library/templates/Intonation/View/Abonne/Selections.php
index dcd801d6ccc5d08dc8b655b0437ff9e5572b6c67..06df0ca96813a6c9607494f628a394175155c476 100644
--- a/library/templates/Intonation/View/Abonne/Selections.php
+++ b/library/templates/Intonation/View/Abonne/Selections.php
@@ -41,6 +41,33 @@ class Intonation_View_Abonne_Selections extends ZendAfi_View_Helper_BaseHelper {
                                                                                           'add',
                                                                                           'utils')])];
 
+    $actions = $this->_addAdminActions($actions);
+
     return $this->view->renderCollection($collection, $actions, null, 20);
   }
+
+
+  protected function _addAdminActions($actions) {
+    if ( ! Class_Users::isCurrentUserAdmin())
+      return $actions;
+
+    $actions [] =
+      new Intonation_Library_Link(['Url' => $this->view->url(['controller' => 'abonne',
+                                                              'action' => 'selections-dans-les-domaines']),
+                                   'Class' => 'btn btn-sm btn-info',
+                                   'Text' => $this->_('Sélections dans les domaines'),
+                                   'Title' => $this->_('Voir les sélections rangés dans les domaines'),
+                                   'Image' => $this->_templateIco('search_more', 'library')]);
+
+    $actions [] =
+      new Intonation_Library_Link(['Url' => $this->view->url(['controller' => 'abonne',
+                                                              'action' => 'selections-des-professionnels']),
+                                   'Class' => 'btn btn-sm btn-warning',
+                                   'Text' => $this->_('Sélections des professionnels'),
+                                   'Title' => $this->_('Voir les sélections des professionnels'),
+                                   'Image' => $this->_templateIco('team',
+                                                                  'library')]);
+
+    return $actions;
+  }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/View/Abonne/SelectionsInDomainsBoard.php b/library/templates/Intonation/View/Abonne/SelectionsInDomainsBoard.php
new file mode 100644
index 0000000000000000000000000000000000000000..1635dd4f109316c2a52da43d35f80b27472bb2a3
--- /dev/null
+++ b/library/templates/Intonation/View/Abonne/SelectionsInDomainsBoard.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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 Intonation_View_Abonne_SelectionsInDomainsBoard extends Intonation_View_Abonne_Tab {
+
+  public function abonne_SelectionsInDomainsBoard($user) {
+    return $this->_initTab($user);
+  }
+
+
+  protected function _hookOn($rich_content) {
+    $sections = $rich_content->getSections();
+
+    $sections [Intonation_Library_View_Wrapper_User_RichContent::SELECTIONS] = (new Intonation_Library_View_Wrapper_User_RichContent_SelectionsInDomains($rich_content->getWrapperInstance()))
+      ->beActive()
+      ->beVisible();
+
+    $rich_content->setSections($sections);
+  }
+
+
+  protected function _tabTitle() {
+    return $this->_('Sélections dans les domaines');
+  }
+}
\ No newline at end of file
diff --git a/library/templates/Intonation/View/Abonne/SelectionsOfProBoard.php b/library/templates/Intonation/View/Abonne/SelectionsOfProBoard.php
new file mode 100644
index 0000000000000000000000000000000000000000..edfe1fa31d4d75b364c463dc8f952402af81c8d5
--- /dev/null
+++ b/library/templates/Intonation/View/Abonne/SelectionsOfProBoard.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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 Intonation_View_Abonne_SelectionsOfProBoard extends Intonation_View_Abonne_Tab {
+
+  public function abonne_SelectionsOfProBoard($user) {
+    return $this->_initTab($user);
+  }
+
+
+  protected function _hookOn($rich_content) {
+    $sections = $rich_content->getSections();
+
+    $sections [Intonation_Library_View_Wrapper_User_RichContent::SELECTIONS] = (new Intonation_Library_View_Wrapper_User_RichContent_SelectionsOfPro($rich_content->getWrapperInstance()))
+      ->beActive()
+      ->beVisible();
+
+    $rich_content->setSections($sections);
+  }
+
+
+  protected function _tabTitle() {
+    return $this->_('Sélections des professionnels');
+  }
+}
\ No newline at end of file
diff --git a/library/templates/Intonation/View/RenderAjaxPaginatedList.php b/library/templates/Intonation/View/RenderAjaxPaginatedList.php
index a079ed05442840846232eab458db94468dc61948..8287817c6ea89502f7ff7950db7395f77e5477a7 100644
--- a/library/templates/Intonation/View/RenderAjaxPaginatedList.php
+++ b/library/templates/Intonation/View/RenderAjaxPaginatedList.php
@@ -80,6 +80,7 @@ class Intonation_View_RenderAjaxPaginatedList extends ZendAfi_View_Helper_BaseHe
                             implode([$this->view->tagAnchor(['controller' => 'index',
                                                              'action' => 'ajax-paginated-list',
                                                              'id' => $this->_id,
+                                                             'size' => $helper->getPageSize(),
                                                              'page' => $current_page -1],
                                                             $this->_tag('i','',['class' => 'fas fa-chevron-left m-0']),
                                                             array_filter(['title' => $this->_('page précedente'),
@@ -95,6 +96,7 @@ class Intonation_View_RenderAjaxPaginatedList extends ZendAfi_View_Helper_BaseHe
                                                              'action' => 'ajax-paginated-list',
                                                              'id_profil' => Class_Profil::getCurrentProfil()->getId(),
                                                              'id' => $this->_id,
+                                                             'size' => $helper->getPageSize(),
                                                              'page' => $current_page +1],
                                                             $this->_tag('i','',['class' => 'fas fa-chevron-right m-0']),
                                                             array_filter(['title' => $this->_('page suivante'),
diff --git a/library/templates/Intonation/View/RenderCollection.php b/library/templates/Intonation/View/RenderCollection.php
index 511d3f4b92de8b1f525b4f49c5b9cf8d665a12c5..82fe8f4a86d22c205c4cbd8f8e526fbc6064b085 100644
--- a/library/templates/Intonation/View/RenderCollection.php
+++ b/library/templates/Intonation/View/RenderCollection.php
@@ -29,11 +29,13 @@ class Intonation_View_RenderCollection extends ZendAfi_View_Helper_BaseHelper {
           return $this->view->cardifyHorizontal($item);
         });
 
-    foreach ($actions as $action) {
-      $action->setClass('btn btn-sm btn-success');
-    }
+    foreach ($actions as $action)
+      if ( ! $action->getClass())
+        $action->setClass('btn btn-sm btn-success');
 
-    $html = [$this->view->div(['class' => 'col-12'], $this->view->renderActions($actions)),
+    $html = [$this->view->div(['class' => 'col-12'],
+                              $this->view->renderActions($actions,
+                                                         ['class' => 'col-auto p-1 m-1 card_action'])),
              $this->view->div(['class' => 'col-12'],
                               $this->view->renderTruncateList($collection,
                                                               $callback,
diff --git a/library/templates/Intonation/View/RenderTrailers.php b/library/templates/Intonation/View/RenderTrailers.php
index 8cec4ad5d9671b96283d265878f2bd3696443ed3..1f33cd63052edf4812c83c60f0a55c526300e503 100644
--- a/library/templates/Intonation/View/RenderTrailers.php
+++ b/library/templates/Intonation/View/RenderTrailers.php
@@ -29,14 +29,22 @@ class Intonation_View_RenderTrailers extends ZendAfi_View_Helper_BaseHelper {
 
     foreach ($trailers as $trailer) {
       $html [] = $this->_renderEdit($trailer)
-        . $this->view->renderEmbed($trailer->getSource(),
-                                   $trailer->getUrl());
+        . $this->_toggleTrailer($trailer)
+        . $this->_renderTrailer($trailer);
     }
 
     return $this->_renderTrailers($html);
   }
 
 
+  protected function _renderTrailer($trailer) {
+    return $trailer->isDisabled()
+      ? ''
+      : $this->view->renderEmbed($trailer->getSource(),
+                                 $trailer->getUrl());
+  }
+
+
   protected function _renderTrailers($html) {
     return implode($html);
   }
@@ -46,6 +54,9 @@ class Intonation_View_RenderTrailers extends ZendAfi_View_Helper_BaseHelper {
     if (!Class_Users::isCurrentUserCanAccesBackend())
       return '';
 
+    if ($trailer->isDisabled())
+      return '';
+
     $url = $this->view->url(['module' => 'admin',
                              'controller' => 'records',
                              'action' => 'trailer',
@@ -59,4 +70,46 @@ class Intonation_View_RenderTrailers extends ZendAfi_View_Helper_BaseHelper {
                      ->setImage(Class_Admin_Skin::current()
                                 ->renderButtonIconOn('configuration', $this->view)));
   }
+
+
+  protected function _toggleTrailer($trailer) {
+    if (!Class_Users::isCurrentUserCanAccesBackend())
+      return '';
+
+    return $trailer->isDisabled()
+      ? $this->_renderEnableTrailer($trailer)
+      : $this->_renderDisableTrailer($trailer);
+  }
+
+
+  protected function _renderDisableTrailer($trailer) {
+    $url = $this->view->url(['module' => 'admin',
+                             'controller' => 'records',
+                             'action' => 'disable-trailer',
+                             'id' => $trailer->getModel()->getId()]);
+
+    return $this->view
+      ->Admin_Button((new Class_Entity())
+                     ->setText($this->_('Désactiver la bande-annonce'))
+                     ->setUrl($url)
+                     ->setAttribs(['data-popup' => 'true'])
+                     ->setImage(Class_Admin_Skin::current()
+                                ->renderButtonIconOn('inactive', $this->view)));
+  }
+
+
+  protected function _renderEnableTrailer($trailer) {
+    $url = $this->view->url(['module' => 'admin',
+                             'controller' => 'records',
+                             'action' => 'enable-trailer',
+                             'id' => $trailer->getModel()->getId()]);
+
+    return $this->view
+      ->Admin_Button((new Class_Entity())
+                     ->setText($this->_('Activer la bande-annonce'))
+                     ->setUrl($url)
+                     ->setAttribs(['data-popup' => 'true'])
+                     ->setImage(Class_Admin_Skin::current()
+                                ->renderButtonIconOn('active', $this->view)));
+  }
 }
diff --git a/library/templates/Intonation/View/Rss/RenderItems.php b/library/templates/Intonation/View/Rss/RenderItems.php
index ff83349624118cfefeb9f9622f7a19a4bb65df0a..ade171476313d6a4e98e764b19d4b622a895cb57 100644
--- a/library/templates/Intonation/View/Rss/RenderItems.php
+++ b/library/templates/Intonation/View/Rss/RenderItems.php
@@ -26,16 +26,14 @@ class Intonation_View_Rss_RenderItems extends ZendAfi_View_Helper_BaseHelper {
 
     $items = array_map(function($element)
                           {
-                            return (new Intonation_Library_View_Wrapper_RssItem)
-                              ->setView($this->view)
-                              ->setModel($element);
+                            return new Intonation_Library_View_Wrapper_RssItem($element, $this->view);
                           }, $items);
 
     $items = new Storm_Collection($items);
 
-    return $this->view->renderTruncateList($items, function($element)
-                                       {
-                                         return $this->view->cardify($element);
-                                       });
+    return $this->view->renderCollection($items, [], function($rss)
+                                         {
+                                           return $this->view->cardify($rss);
+                                         });
   }
 }
diff --git a/library/templates/Intonation/View/User/Informations.php b/library/templates/Intonation/View/User/Informations.php
index f708c9e4d0bbc49a5c43bf07332aa061f569b4b4..2ca8268dcc6f63ee88dbc613315ef5a5d8cb0506 100644
--- a/library/templates/Intonation/View/User/Informations.php
+++ b/library/templates/Intonation/View/User/Informations.php
@@ -120,9 +120,7 @@ class Intonation_View_User_Informations extends ZendAfi_View_Helper_BaseHelper {
     if (!$library = $user->getLibrary())
       return '';
 
-    return $this->view->tagAnchor($this->view->url(['controller' => 'bib',
-                                                    'action' => 'en-lire-plus',
-                                                    'id' => $library->getId()]),
+    return $this->view->tagAnchor($library->getUrl(),
                                   $library->getLibelle(),
                                   ['title' => $this->_('En lire plus sur %s',
                                                        $library->getLibelle())]);
diff --git a/scripts/thumbnails_from_marc.php b/scripts/thumbnails_from_marc.php
new file mode 100644
index 0000000000000000000000000000000000000000..026f63ea53fa9b36c11704327120e4d9721b5653
--- /dev/null
+++ b/scripts/thumbnails_from_marc.php
@@ -0,0 +1,28 @@
+<?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
+ */
+
+
+error_reporting(E_ALL^E_DEPRECATED);
+require __DIR__ . '/../console.php';
+
+echo "\nusage : php thumbnails_from_marc.php profile_id\n";
+
+(new Class_Notice_Thumbnail_UpdateFromFields())->runWith($argv[1]);
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/ProfilControllerModuleSortTest.php b/tests/application/modules/admin/controllers/ProfilControllerModuleSortTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..fe80ecc532d59511d3457942d52b164dac737196
--- /dev/null
+++ b/tests/application/modules/admin/controllers/ProfilControllerModuleSortTest.php
@@ -0,0 +1,47 @@
+<?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 ProfilControllerModuleSortTest extends Admin_AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+    $this->fixture(Class_Profil::class, ['id' => 12]);
+    $this->onLoaderOfModel(Class_Profil::class);
+  }
+
+
+  /** @test */
+  public function whenDisabledShouldNotSaveProfile() {
+    Class_AdminVar::set('DISABLE_BLOCKS_SORTING', 1);
+    $this->dispatch('/admin/profil/module-sort/profil/12/fromDivision/2/fromPosition/1/toDivision/2/toPosition/2');
+    $this->assertTrue(Class_Profil::methodHasNotBeenCalled('save'));
+  }
+
+
+  /** @test */
+  public function whenEnabledShouldSaveProfile() {
+    Class_AdminVar::set('DISABLE_BLOCKS_SORTING', 0);
+    $this->dispatch('/admin/profil/module-sort/profil/12/fromDivision/2/fromPosition/1/toDivision/2/toPosition/2');
+    $this->assertTrue(Class_Profil::methodHasBeenCalled('save'));
+  }
+}
diff --git a/tests/application/modules/admin/controllers/RecordsControllerTest.php b/tests/application/modules/admin/controllers/RecordsControllerTest.php
index bf80c951a2d6ebbeb172e7d95d8cb6b3ab4eae7d..fe075f21ffc1d39e53f42ea8650a66ef669aa489 100644
--- a/tests/application/modules/admin/controllers/RecordsControllerTest.php
+++ b/tests/application/modules/admin/controllers/RecordsControllerTest.php
@@ -683,4 +683,52 @@ class RecordsControllerDeleteInexistingItemTest extends RecordsControllerTestCas
   public function responseShouldNotifyDeletion() {
     $this->assertFlashMessengerContentContains('Exemplaire non trouvé');
   }
+}
+
+
+
+
+class RecordsControllerTrailerAdministrationTest extends RecordsControllerTestCase {
+
+  /** @test */
+  public function disableAcionshouldRedirectWithTrailerDisabledNotification() {
+    $this->_http_client
+      ->whenCalled('open_url')
+      ->with('http://cache.org'
+             .'?clef_oeuvre=HARRYPOTTER--ROWLINGJ-'
+             .'&type_doc=1'
+             .'&disable_trailer=1'
+             .'&src='.Class_WebService_AllServices::createSecurityKey()
+             .'&api=2.0'
+             .'&action=14')
+      ->answers(json_encode(['url_trailer' => 'https://youtu.be/BEPO2',
+                             'trailer_disabled' => 1,
+                             'statut_recherche' => 2]))
+      ->beStrict();
+
+    $this->dispatch('/admin/records/disable-trailer/id/12345');
+    $this->assertFlashMessengerContentContains('La bande-annonce a bien été désactivée.');
+  }
+
+
+    /** @test */
+  public function enableAcionshouldRedirectWithTrailerEnabledNotification() {
+    $this->_http_client
+
+      ->whenCalled('open_url')
+      ->with('http://cache.org'
+             .'?clef_oeuvre=HARRYPOTTER--ROWLINGJ-'
+             .'&type_doc=1'
+             .'&disable_trailer=0'
+             .'&src='.Class_WebService_AllServices::createSecurityKey()
+             .'&api=2.0'
+             .'&action=14')
+      ->answers(json_encode(['url_trailer' => 'https://youtu.be/BEPO2',
+                             'trailer_disabled' => 0,
+                             'statut_recherche' => 2]))
+      ->beStrict();
+
+    $this->dispatch('/admin/records/enable-trailer/id/12345');
+    $this->assertFlashMessengerContentContains('La bande-annonce a bien été activée.');
+  }
 }
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/IndexControllerTest.php b/tests/application/modules/opac/controllers/IndexControllerTest.php
index 000f64168c2d180f3ad08fb5bc280a11aa74def1..af1e3716e1ec014bde55d87786c55f850ab56628 100644
--- a/tests/application/modules/opac/controllers/IndexControllerTest.php
+++ b/tests/application/modules/opac/controllers/IndexControllerTest.php
@@ -987,6 +987,43 @@ class IndexControllerWithSuperAdminLoggedTest extends AbstractControllerTestCase
   public function scriptToShowAmberIdeShouldBePresent() {
     $this->assertXPathContentContains('//script', 'showAmberIDE()');
   }
+
+
+  /** @test */
+  public function linkToEnableBlockSortingShouldBePresent() {
+    $this->assertXPathContentContains('//a', 'Déplacement des boites');
+  }
+}
+
+
+
+
+class IndexControllerWithSuperAdminLoggedAndBlockSortingDisabledTest
+  extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+    $joe = $this->fixture('Class_Users',
+                          ['id' => 23,
+                           'login' => 'Joe',
+                           'password' => 'unsupermotdepasse',
+                           'id_site' => 1,
+                           'settings' => Class_User_Settings::serializeSettings(['show_admin_icons' => '1',
+                                                                                 'amber_ide' => 1]),
+                           'role_level' => ZendAfi_Acl_AdminControllerRoles::SUPER_ADMIN]);
+
+    ZendAfi_Auth::getInstance()->logUser($joe);
+    Class_AdminVar::set('DISABLE_BLOCKS_SORTING', 1);
+
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function linkToEnableBlockSortingShouldNotBePresent() {
+    $this->assertNotXPathContentContains('//a', 'Déplacement des boites');
+  }
 }
 
 
diff --git a/tests/application/modules/opac/controllers/RechercheControllerTest.php b/tests/application/modules/opac/controllers/RechercheControllerTest.php
index ac3eb0d78b16cdabd16d8b5b0a9dc18919fb39c3..83748d2c5f1cabbdb87da6182faf32355691fb23 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerTest.php
@@ -2114,15 +2114,16 @@ class RechercheControllerFacetteSelectedTest extends AbstractControllerTestCase
 
 class RechercheControllerSimpleActionLibelleFacetteTest extends RechercheControllerNoticeTestCase {
   protected $_liste_format = Class_Systeme_ModulesAppli::LISTE_FORMAT_MUR;
+
   public function setup() {
     parent::setup();
-    Class_AdminVar::newInstanceWithId('FACETTE_AUTEUR_LIBELLE', ['valeur' => 'Les super auteurs']);
-    Class_AdminVar::newInstanceWithId('FACETTE_DEWEY_LIBELLE', ['valeur' => 'DEWEY ?!@']);
+    Class_AdminVar::set('FACETTE_AUTEUR_LIBELLE', 'Les super auteurs');
+    Class_AdminVar::set('FACETTE_DEWEY_LIBELLE', 'DEWEY ?!@');
+
     $this->fixture('Class_CodifGenre',
                    ['id' => 19,
                     'libelle' => 'Manga']);
 
-
     $this->dispatch('/recherche/simple/?'.
                     'operateur_titres=and'.
                     '&rech_titres='.
@@ -2143,7 +2144,7 @@ class RechercheControllerSimpleActionLibelleFacetteTest extends RechercheControl
                     '&nouveaute='.
                     '&annexe='.
                     '&genre=1%3B19'.
-                    '&section=', true);
+                    '&section=');
   }
 
 
@@ -2162,8 +2163,8 @@ class RechercheControllerSimpleActionLibelleFacetteTest extends RechercheControl
 
   /** @test */
   public function criteresRechercheShouldContainsLEcole() {
-    $this->assertXPathContentContains('//div[@class="criteres_recherche"]//div//a[@href="/recherche/simple/operateur_auteurs/and/rech_auteurs/musso/operateur_dewey/and/rech_dewey/pomme/operateur_editeur/and/type_recherche/fulltext/tri/%2A/genre/1%3B19"]',
-                                      'Editeur : l\'ecole');
+    $this->assertXPathContentContains('//div[@class="criteres_recherche"]//div//a[@href="/recherche/simple/operateur_auteurs/and/rech_auteurs/musso/operateur_dewey/and/rech_dewey/pomme/type_recherche/fulltext/tri/%2A/genre/1%3B19"]',
+                                      'Éditeur : l\'ecole');
   }
 
 
diff --git a/tests/application/modules/opac/controllers/RechercheControllerViewnoticeTest.php b/tests/application/modules/opac/controllers/RechercheControllerViewnoticeTest.php
index 77f94e0f84270c5324283898267a525c11f39517..f3f5ee0280abe4f91080b9c02c958372d89ef4f1 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerViewnoticeTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerViewnoticeTest.php
@@ -250,6 +250,8 @@ class RechercheControllerViewnoticeVignetteFromUnimarcTest
 
 /** @see http://forge.afi-sa.fr/issues/125382 */
 class RechercheControllerViewnoticeTitleWithChapeauTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
   /** @test */
   public function h1ShouldContainsASpaceBetweenChapeauAndTitle() {
     $record = (new Class_NoticeUnimarc_Fluent)
@@ -271,4 +273,51 @@ class RechercheControllerViewnoticeTitleWithChapeauTest extends AbstractControll
     $this->assertXPathContentContains('//h1',
                                       'The promised Neverland n° 14 Retrouvailles inattendues');
   }
+}
+
+
+
+
+/** @see http://forge.afi-sa.fr/issues/126267 */
+class RechercheControllerViewnoticeOpenBlockTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+
+    $record = (new Class_NoticeUnimarc_Fluent)
+      ->zoneWithContent('001', '25203')
+      ->zoneWithChildren('200', ['a' => 'Liseuse Vivlio Touch Lux 5'])
+      ;
+
+    $this->fixture(Class_Notice::class,
+                   ['id' => 12,
+                    'type_doc' => 11,
+                    'unimarc' => $record->render()
+                   ]);
+
+    Class_Profil::getCurrentProfil()
+      ->setModulePreference('recherche', 'viewnotice11', 'onglets',
+                            ['detail' => ['aff' => Class_Onglet::OPEN,
+                                          'ordre' => 1,
+                                          'titre' => 'Desc'],
+                             'exemplaires' => ['aff' => Class_Onglet::CLOSED,
+                                               'ordre' => 3,
+                                               'titre' => 'Items']]);
+
+    $this->dispatch('/recherche/viewnotice/id/12');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsScriptHideThenToggleDescBlock() {
+    $this->assertXPathContentContains('//script',
+                                      '$("#bloc_12_0_contenu").hide();infos_bloc("bloc_12_0",');
+  }
+
+
+  /** @test */
+  public function pageShouldNotContainsScriptHideItemsBlock() {
+    $this->assertNotXPathContentContains('//script', '$("#bloc_12_2_contenu").hide()');
+  }
 }
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/RssControllerTest.php b/tests/application/modules/opac/controllers/RssControllerTest.php
index 7cec394d888ac8983f297eea1f4a535b51b7f8db..b41e837def4f749cfa3087cad529e366122efa30 100644
--- a/tests/application/modules/opac/controllers/RssControllerTest.php
+++ b/tests/application/modules/opac/controllers/RssControllerTest.php
@@ -18,6 +18,11 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
+
+
+require_once 'tests/fixtures/LemondeRss.php';
+
+
 abstract class RssControllerTestCase extends AbstractControllerTestCase {
   protected $_storm_default_to_volatile = true;
 
@@ -442,158 +447,4 @@ class RssControllerCritiquesTest extends AbstractControllerTestCase {
   public function rssItemFirstImageShouldBeRawThumbnail() {
     $this->assertContains('recherche/raw-thumbnail/id/1"/><media:content', $this->_response->getBody());
   }
-}
-
-
-
-
-class RssFixtures {
-  public static function lemondeRSS() {
-    return
-      '<?xml version=\'1.0\' encoding=\'UTF-8\'?>
-       <?xml-stylesheet type=\'text/xsl\' href=\'http://rss.lemonde.fr/xsl/fr/rss.xsl\'?>
-       <rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" version="2.0">
-         <channel>
-           <title>Le Monde.fr : à la Une
-           </title>
-           <link>http://www.lemonde.fr
-           </link>
-           <description>Toute l\'actualité au moment de la connexion
-           </description>
-           <language>en
-           </language>
-           <copyright>Copyright Le Monde.fr
-           </copyright>
-           <pubDate>Tue, 07 Jun 2011 09:16:52 GMT
-           </pubDate>
-           <lastBuildDate>Tue, 07 Jun 2011 09:16:52 GMT
-           </lastBuildDate>
-           <ttl>15
-           </ttl>
-           <image>
-             <title />
-             <url>http://medias.lemonde.fr/mmpub/img/lgo/lemondefr_rss.gif
-             </url>
-             <link>http://www.lemonde.fr
-             </link>
-           </image>
-           <item>
-             <title>Hirsch a "six minutes pour sauver le RSA"
-             </title>
-             <link>http://bigbrowser.blog.lemonde.fr/2011/06/07/speed-dating-hirsch-a-six-minutes-pour-sauver-le-rsa/#xtor=RSS-32280322#xtor=RSS-3208
-             </link>
-             <description>"Six minutes, c\'est rapide sur un sujet qui concerne, aujourd\'hui, près de deux millions de foyers et qui fait l\'objet de tant de contre-vérités", déplore Martin Hirsch.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b495be/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471368109/u/192/f/3050/c/205/s/15b495be/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471368109/u/192/f/3050/c/205/s/15b495be/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s1.lemde.fr/image/2009/10/29/87x0/1260149_7_8929_six-minutes-c-est-rapide-sur-un-sujet-qui.jpg" length="2133" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 09:14:02 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://bigbrowser.blog.lemonde.fr/2011/06/07/speed-dating-hirsch-a-six-minutes-pour-sauver-le-rsa/#xtor=RSS-32280322#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Blog - Délinquance des mineurs : le septième rapport en sept ans
-             </title>
-             <link>http://insecurite.blog.lemonde.fr/2011/06/07/delinquance-des-mineurs-le-septieme-rapport-en-sept-ans/#xtor=RSS-3208
-             </link>
-             <description>On ne compte plus ces dernières années les rapports consacrés à la délinquance et à la justice des mineurs. Ou plutôt si, comptons-les pour voir. Nous avions déjà les rapports Bénisti (celui de 2011, pas le premier de 2004), Klarsfeld ... Continuer la lecture ?&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b495bf/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471368108/u/192/f/3050/c/205/s/15b495bf/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471368108/u/192/f/3050/c/205/s/15b495bf/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532863_7_41d9_yvan-lachaud-depute-nouveau-centre-est.jpg" length="2315" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 09:01:19 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://insecurite.blog.lemonde.fr/2011/06/07/delinquance-des-mineurs-le-septieme-rapport-en-sept-ans/#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Blog - Le premier train solaire roule en Belgique
-             </title>
-             <link>http://ecologie.blog.lemonde.fr/2011/06/07/le-premier-train-solaire-roule-en-belgique/#xtor=RSS-3208
-             </link>
-             <description>A bord, rien ne le distingue d\'un autre convoi. Mais à l\'extérieur, ce sont des wagons d\'un genre nouveau, seulement alimentés par les rayons du soleil et non à l\'électricité issue des centrales nucléaires ou au gaz. Pour la première ... Continuer la lecture ?&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48416/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364868/u/192/f/3050/c/205/s/15b48416/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364868/u/192/f/3050/c/205/s/15b48416/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s1.lemde.fr/image/2011/06/07/87x0/1532842_7_fe11_des-panneaux-solaires-sont-installes-sur-le.jpg" length="2514" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 08:27:33 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://ecologie.blog.lemonde.fr/2011/06/07/le-premier-train-solaire-roule-en-belgique/#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Paris veut faire condamner la Syrie par l\'ONU
-             </title>
-             <link>http://www.lemonde.fr/proche-orient/article/2011/06/07/paris-veut-faire-condamner-la-syrie-par-l-onu_1532779_3218.html#xtor=RSS-3208
-             </link>
-             <description>Malgré la menace d\'un veto russe, Paris veut faire adopter un projet de résolution qui exige la fin immédiate des violences contre les manifestants.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48417/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364867/u/192/f/3050/c/205/s/15b48417/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364867/u/192/f/3050/c/205/s/15b48417/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532785_7_345a_manifestants-a-banias-face-aux-forces-de.jpg" length="2054" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 08:20:15 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/proche-orient/article/2011/06/07/paris-veut-faire-condamner-la-syrie-par-l-onu_1532779_3218.html#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Lisbonne veut rassurer les marchés en créant une autorité budgétaire
-             </title>
-             <link>http://www.lemonde.fr/europe/article/2011/06/07/lisbonne-veut-rassurer-les-marches-en-creant-une-autorite-budgetaire_1532774_3214.html#xtor=RSS-3208
-             </link>
-             <description>Le nouveau gouvernement portugais va créer une autorité budgétaire indépendante "aux pouvoirs larges" pour rassurer les marchés financiers, déclare le futur fremier ministre portugais, Pedro Passos Coelho, dans une interview au quotidien "Les Echos" de mardi.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48418/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364866/u/192/f/3050/c/205/s/15b48418/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364866/u/192/f/3050/c/205/s/15b48418/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <pubDate>Tue, 07 Jun 2011 07:40:37 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/europe/article/2011/06/07/lisbonne-veut-rassurer-les-marches-en-creant-une-autorite-budgetaire_1532774_3214.html#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Luxe : PPR se préparerait à une acquisition majeure
-             </title>
-             <link>http://www.lemonde.fr/economie/article/2011/06/07/luxe-ppr-se-preparerait-a-une-acquisition-majeure_1532770_3234.html#xtor=RSS-3208
-             </link>
-             <description>Selon "La Tribune" plusieurs "cibles" pourraient intéresser le groupe, dont Hugo Boss, Burberry, Ralph Lauren et Armani, mais l\'italien Prada semble le plus coivoité.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48419/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364865/u/192/f/3050/c/205/s/15b48419/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364865/u/192/f/3050/c/205/s/15b48419/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s1.lemde.fr/image/2011/06/07/87x0/1532772_7_f14e_francois-henri-pinault-lors-de-la-presentation.jpg" length="1951" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 07:10:04 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/economie/article/2011/06/07/luxe-ppr-se-preparerait-a-une-acquisition-majeure_1532770_3234.html#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Microsoft intègre la reconnaissance vocale à son système de jeu Kinect
-             </title>
-             <link>http://www.lemonde.fr/technologies/article/2011/06/07/microsoft-integre-la-reconnaissance-vocale-a-son-systeme-de-jeu-kinect_1532757_651865.html#ens_id=1514468&amp;#38;xtor=RSS-3208
-             </link>
-             <description>Microsoft a ouvert, lundi, le bal des conférences de presse du Salon E3 à Los Angeles. Un show millimétré, rythmé par les images des prochains jeux défilant sur des écrans géants, au cours duquel le constructeur a présenté les nouvelles fonctionnalités de son système Kinect.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3bbe8/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104456619640/u/192/f/3050/c/205/s/15b3bbe8/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104456619640/u/192/f/3050/c/205/s/15b3bbe8/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532765_7_53d1_presentation-du-jeu-star-wars-sur-kinect.jpg" length="2500" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 07:04:13 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/technologies/article/2011/06/07/microsoft-integre-la-reconnaissance-vocale-a-son-systeme-de-jeu-kinect_1532757_651865.html#ens_id=1514468&amp;#38;xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>Egalité des sexes : un rapport suggère d\'allonger le congé paternité
-             </title>
-             <link>http://www.lemonde.fr/societe/article/2011/06/07/egalite-des-sexes-un-rapport-suggere-d-allonger-le-conge-paternite_1532763_3224.html#xtor=RSS-3208
-             </link>
-             <description>Brigitte Grésy, inspectrice générale des affaires sociales, doit rendre mardi à Roselyne Bachelot, ministre des solidarités, son rapport sur "la participation des hommes aux responsabilités parentales".&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3bbe2/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104456619639/u/192/f/3050/c/205/s/15b3bbe2/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104456619639/u/192/f/3050/c/205/s/15b3bbe2/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s2.lemde.fr/image/2011/03/23/87x0/1497164_7_d1d0_l-allongement-du-conge-de-paternite-permettrait.jpg" length="2391" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 06:47:20 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/societe/article/2011/06/07/egalite-des-sexes-un-rapport-suggere-d-allonger-le-conge-paternite_1532763_3224.html#xtor=RSS-3208
-             </guid>
-           </item>
-           <item>
-             <title>"Le mastère spécialisé apporte surtout la méthodologie d\'une école"
-             </title>
-             <link>http://www.lemonde.fr/orientation-scolaire/article/2011/06/07/le-mastere-specialise-apporte-surtout-la-methodologie-d-une-ecole_1531169_1473696.html#xtor=RSS-3208
-             </link>
-             <description>Eric Parlebas, président de la commission d\'accréditation des diplômes de la Conférence des grandes écoles, explique ce qu\'on peut attendre d\'un mastère spécialisé.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3a6c1/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471352231/u/192/f/3050/c/205/s/15b3a6c1/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471352231/u/192/f/3050/c/205/s/15b3a6c1/a2.img" border="0"/&gt;&lt;/a&gt;
-             </description>
-             <enclosure url="http://s1.lemde.fr/image/2011/06/02/87x0/1531170_7_1da1_eric-parlebas.jpg" length="2048" type="image/jpeg" />
-             <pubDate>Tue, 07 Jun 2011 06:45:33 GMT
-             </pubDate>
-             <guid isPermaLink="false">http://www.lemonde.fr/orientation-scolaire/article/2011/06/07/le-mastere-specialise-apporte-surtout-la-methodologie-d-une-ecole_1531169_1473696.html#xtor=RSS-3208
-             </guid>
-           </item>
-         </channel>
-       </rss>';
-  }
-}
+}
\ No newline at end of file
diff --git a/tests/fixtures/LemondeRss.php b/tests/fixtures/LemondeRss.php
new file mode 100644
index 0000000000000000000000000000000000000000..cc07a6271309d2b365953ea52af1cfb3f1d4fc5f
--- /dev/null
+++ b/tests/fixtures/LemondeRss.php
@@ -0,0 +1,172 @@
+<?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 RssFixtures {
+  public static function lemondeRSS() {
+    return
+      '<?xml version=\'1.0\' encoding=\'UTF-8\'?>
+       <?xml-stylesheet type=\'text/xsl\' href=\'http://rss.lemonde.fr/xsl/fr/rss.xsl\'?>
+       <rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" version="2.0">
+         <channel>
+           <title>Le Monde.fr : à la Une
+           </title>
+           <link>http://www.lemonde.fr
+           </link>
+           <description>Toute l\'actualité au moment de la connexion
+           </description>
+           <language>en
+           </language>
+           <copyright>Copyright Le Monde.fr
+           </copyright>
+           <pubDate>Tue, 07 Jun 2011 09:16:52 GMT
+           </pubDate>
+           <lastBuildDate>Tue, 07 Jun 2011 09:16:52 GMT
+           </lastBuildDate>
+           <ttl>15
+           </ttl>
+           <image>
+             <title />
+             <url>http://medias.lemonde.fr/mmpub/img/lgo/lemondefr_rss.gif
+             </url>
+             <link>http://www.lemonde.fr
+             </link>
+           </image>
+           <item>
+             <title>Hirsch a "six minutes pour sauver le RSA"
+             </title>
+             <link>http://bigbrowser.blog.lemonde.fr/2011/06/07/speed-dating-hirsch-a-six-minutes-pour-sauver-le-rsa/#xtor=RSS-32280322#xtor=RSS-3208
+             </link>
+             <description>"Six minutes, c\'est rapide sur un sujet qui concerne, aujourd\'hui, près de deux millions de foyers et qui fait l\'objet de tant de contre-vérités", déplore Martin Hirsch.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b495be/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471368109/u/192/f/3050/c/205/s/15b495be/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471368109/u/192/f/3050/c/205/s/15b495be/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s1.lemde.fr/image/2009/10/29/87x0/1260149_7_8929_six-minutes-c-est-rapide-sur-un-sujet-qui.jpg" length="2133" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 09:14:02 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://bigbrowser.blog.lemonde.fr/2011/06/07/speed-dating-hirsch-a-six-minutes-pour-sauver-le-rsa/#xtor=RSS-32280322#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Blog - Délinquance des mineurs : le septième rapport en sept ans
+             </title>
+             <link>http://insecurite.blog.lemonde.fr/2011/06/07/delinquance-des-mineurs-le-septieme-rapport-en-sept-ans/#xtor=RSS-3208
+             </link>
+             <description>On ne compte plus ces dernières années les rapports consacrés à la délinquance et à la justice des mineurs. Ou plutôt si, comptons-les pour voir. Nous avions déjà les rapports Bénisti (celui de 2011, pas le premier de 2004), Klarsfeld ... Continuer la lecture ?&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b495bf/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471368108/u/192/f/3050/c/205/s/15b495bf/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471368108/u/192/f/3050/c/205/s/15b495bf/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532863_7_41d9_yvan-lachaud-depute-nouveau-centre-est.jpg" length="2315" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 09:01:19 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://insecurite.blog.lemonde.fr/2011/06/07/delinquance-des-mineurs-le-septieme-rapport-en-sept-ans/#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Blog - Le premier train solaire roule en Belgique
+             </title>
+             <link>http://ecologie.blog.lemonde.fr/2011/06/07/le-premier-train-solaire-roule-en-belgique/#xtor=RSS-3208
+             </link>
+             <description>A bord, rien ne le distingue d\'un autre convoi. Mais à l\'extérieur, ce sont des wagons d\'un genre nouveau, seulement alimentés par les rayons du soleil et non à l\'électricité issue des centrales nucléaires ou au gaz. Pour la première ... Continuer la lecture ?&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48416/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364868/u/192/f/3050/c/205/s/15b48416/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364868/u/192/f/3050/c/205/s/15b48416/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s1.lemde.fr/image/2011/06/07/87x0/1532842_7_fe11_des-panneaux-solaires-sont-installes-sur-le.jpg" length="2514" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 08:27:33 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://ecologie.blog.lemonde.fr/2011/06/07/le-premier-train-solaire-roule-en-belgique/#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Paris veut faire condamner la Syrie par l\'ONU
+             </title>
+             <link>http://www.lemonde.fr/proche-orient/article/2011/06/07/paris-veut-faire-condamner-la-syrie-par-l-onu_1532779_3218.html#xtor=RSS-3208
+             </link>
+             <description>Malgré la menace d\'un veto russe, Paris veut faire adopter un projet de résolution qui exige la fin immédiate des violences contre les manifestants.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48417/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364867/u/192/f/3050/c/205/s/15b48417/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364867/u/192/f/3050/c/205/s/15b48417/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532785_7_345a_manifestants-a-banias-face-aux-forces-de.jpg" length="2054" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 08:20:15 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/proche-orient/article/2011/06/07/paris-veut-faire-condamner-la-syrie-par-l-onu_1532779_3218.html#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Lisbonne veut rassurer les marchés en créant une autorité budgétaire
+             </title>
+             <link>http://www.lemonde.fr/europe/article/2011/06/07/lisbonne-veut-rassurer-les-marches-en-creant-une-autorite-budgetaire_1532774_3214.html#xtor=RSS-3208
+             </link>
+             <description>Le nouveau gouvernement portugais va créer une autorité budgétaire indépendante "aux pouvoirs larges" pour rassurer les marchés financiers, déclare le futur fremier ministre portugais, Pedro Passos Coelho, dans une interview au quotidien "Les Echos" de mardi.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48418/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364866/u/192/f/3050/c/205/s/15b48418/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364866/u/192/f/3050/c/205/s/15b48418/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <pubDate>Tue, 07 Jun 2011 07:40:37 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/europe/article/2011/06/07/lisbonne-veut-rassurer-les-marches-en-creant-une-autorite-budgetaire_1532774_3214.html#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Luxe : PPR se préparerait à une acquisition majeure
+             </title>
+             <link>http://www.lemonde.fr/economie/article/2011/06/07/luxe-ppr-se-preparerait-a-une-acquisition-majeure_1532770_3234.html#xtor=RSS-3208
+             </link>
+             <description>Selon "La Tribune" plusieurs "cibles" pourraient intéresser le groupe, dont Hugo Boss, Burberry, Ralph Lauren et Armani, mais l\'italien Prada semble le plus coivoité.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b48419/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471364865/u/192/f/3050/c/205/s/15b48419/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471364865/u/192/f/3050/c/205/s/15b48419/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s1.lemde.fr/image/2011/06/07/87x0/1532772_7_f14e_francois-henri-pinault-lors-de-la-presentation.jpg" length="1951" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 07:10:04 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/economie/article/2011/06/07/luxe-ppr-se-preparerait-a-une-acquisition-majeure_1532770_3234.html#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Microsoft intègre la reconnaissance vocale à son système de jeu Kinect
+             </title>
+             <link>http://www.lemonde.fr/technologies/article/2011/06/07/microsoft-integre-la-reconnaissance-vocale-a-son-systeme-de-jeu-kinect_1532757_651865.html#ens_id=1514468&amp;#38;xtor=RSS-3208
+             </link>
+             <description>Microsoft a ouvert, lundi, le bal des conférences de presse du Salon E3 à Los Angeles. Un show millimétré, rythmé par les images des prochains jeux défilant sur des écrans géants, au cours duquel le constructeur a présenté les nouvelles fonctionnalités de son système Kinect.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3bbe8/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104456619640/u/192/f/3050/c/205/s/15b3bbe8/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104456619640/u/192/f/3050/c/205/s/15b3bbe8/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s2.lemde.fr/image/2011/06/07/87x0/1532765_7_53d1_presentation-du-jeu-star-wars-sur-kinect.jpg" length="2500" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 07:04:13 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/technologies/article/2011/06/07/microsoft-integre-la-reconnaissance-vocale-a-son-systeme-de-jeu-kinect_1532757_651865.html#ens_id=1514468&amp;#38;xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>Egalité des sexes : un rapport suggère d\'allonger le congé paternité
+             </title>
+             <link>http://www.lemonde.fr/societe/article/2011/06/07/egalite-des-sexes-un-rapport-suggere-d-allonger-le-conge-paternite_1532763_3224.html#xtor=RSS-3208
+             </link>
+             <description>Brigitte Grésy, inspectrice générale des affaires sociales, doit rendre mardi à Roselyne Bachelot, ministre des solidarités, son rapport sur "la participation des hommes aux responsabilités parentales".&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3bbe2/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104456619639/u/192/f/3050/c/205/s/15b3bbe2/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104456619639/u/192/f/3050/c/205/s/15b3bbe2/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s2.lemde.fr/image/2011/03/23/87x0/1497164_7_d1d0_l-allongement-du-conge-de-paternite-permettrait.jpg" length="2391" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 06:47:20 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/societe/article/2011/06/07/egalite-des-sexes-un-rapport-suggere-d-allonger-le-conge-paternite_1532763_3224.html#xtor=RSS-3208
+             </guid>
+           </item>
+           <item>
+             <title>"Le mastère spécialisé apporte surtout la méthodologie d\'une école"
+             </title>
+             <link>http://www.lemonde.fr/orientation-scolaire/article/2011/06/07/le-mastere-specialise-apporte-surtout-la-methodologie-d-une-ecole_1531169_1473696.html#xtor=RSS-3208
+             </link>
+             <description>Eric Parlebas, président de la commission d\'accréditation des diplômes de la Conférence des grandes écoles, explique ce qu\'on peut attendre d\'un mastère spécialisé.&lt;img width=\'1\' height=\'1\' src=\'http://rss.lemonde.fr/c/205/f/3050/s/15b3a6c1/mf.gif\' border=\'0\'/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://da.feedsportal.com/r/104471352231/u/192/f/3050/c/205/s/15b3a6c1/a2.htm"&gt;&lt;img src="http://da.feedsportal.com/r/104471352231/u/192/f/3050/c/205/s/15b3a6c1/a2.img" border="0"/&gt;&lt;/a&gt;
+             </description>
+             <enclosure url="http://s1.lemde.fr/image/2011/06/02/87x0/1531170_7_1da1_eric-parlebas.jpg" length="2048" type="image/jpeg" />
+             <pubDate>Tue, 07 Jun 2011 06:45:33 GMT
+             </pubDate>
+             <guid isPermaLink="false">http://www.lemonde.fr/orientation-scolaire/article/2011/06/07/le-mastere-specialise-apporte-surtout-la-methodologie-d-une-ecole_1531169_1473696.html#xtor=RSS-3208
+             </guid>
+           </item>
+         </channel>
+       </rss>';
+  }
+}
diff --git a/tests/library/Class/MoteurRechercheTest.php b/tests/library/Class/MoteurRechercheTest.php
index 8378fc2a87abe7e544e8048f854096ab3c53a7d6..3ea790c985f16bd85f8467247e87bd31b24b42cd 100644
--- a/tests/library/Class/MoteurRechercheTest.php
+++ b/tests/library/Class/MoteurRechercheTest.php
@@ -163,7 +163,7 @@ class MoteurRechercheAvanceeTest extends MoteurRechercheTestCase {
               'type_recherche' => 'nimportequoi',
               'pertinence' => false,
               'tri' => 'alpha_titre'],
-             'req_notices' => $this->listSqlWith("MATCH(editeur) AGAINST('RAISONS D AGIR' IN BOOLEAN MODE) and date_creation >'2012-02-03' and (MATCH(titres) AGAINST('+(NOUVEAU NOUVEAUX NOUVO) +(CHIEN CHIENS CHIN) +(GARDE GARDES GARD)' IN BOOLEAN MODE)) and MATCH(facettes) AGAINST('+(B4)' IN BOOLEAN MODE)",
+             'req_notices' => $this->listSqlWith("date_creation >'2012-02-03' and (MATCH(titres) AGAINST('+(NOUVEAU NOUVEAUX NOUVO) +(CHIEN CHIENS CHIN) +(GARDE GARDES GARD)' IN BOOLEAN MODE) or MATCH(editeur) AGAINST('+(RAISON RAISONS RAISON) +(D00 D00S ) +(AGIR AGIRS AJIR)' IN BOOLEAN MODE)) and MATCH(facettes) AGAINST('+(B4)' IN BOOLEAN MODE)",
                                                  'alpha_titre')],
 
             [['rech_auteurs' => 'Stiegler',
diff --git a/tests/library/Class/Notice/Thumbnail/UpdateFromFieldsTest.php b/tests/library/Class/Notice/Thumbnail/UpdateFromFieldsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ce527568231e3d3c0df71c7d49f0bd1019393f78
--- /dev/null
+++ b/tests/library/Class/Notice/Thumbnail/UpdateFromFieldsTest.php
@@ -0,0 +1,75 @@
+<?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_Notice_Thumbnail_UpdateFromFieldsTest extends ModelTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture(Class_Profil::class,
+                   ['id' => Class_Profil::DEFAULT_PROFIL]);
+
+    $this->fixture(Class_Profil::class, ['id' => 2])
+         ->setModulePreference('recherche', 'viewnotice1', 'thumbnail_fields', '992-v')
+         ->assertSave();
+
+    $marc = (new Class_NoticeUnimarc_Fluent)
+      ->zoneWithChildren('992', ['v' => 'http://url.to/image.jpg']);
+
+    $record = $this->fixture(Class_Notice::class,
+                             ['id' => 123,
+                              'type_doc' => 1,
+                              'unimarc' => $marc->render(),
+                              'url_vignette' => 'NO',
+                              'url_image' => 'NO']);
+
+    $alread_called = false;
+    $this->onLoaderOfModel(Class_Notice::class)
+         ->whenCalled('findAllBy')
+         ->willDo(function() use(&$alread_called, $record)
+                  {
+                    if (!$alread_called) {
+                      $alread_called = true;
+                      return [$record];
+                    }
+
+                    return [];
+                  });
+
+    ob_start();
+    (new Class_Notice_Thumbnail_UpdateFromFields)
+      ->setMemoryCleaner(function(){})
+      ->runWith(2);
+    ob_end_clean();
+  }
+
+
+  /** @test */
+  public function recordUrlVignetteShouldBeUpdated() {
+    $this->assertEquals('http://url.to/image.jpg', Class_Notice::find(123)->getUrlVignette());
+  }
+
+
+  /** @test */
+  public function recordUrlImageShouldBeUpdated() {
+    $this->assertEquals('http://url.to/image.jpg', Class_Notice::find(123)->getUrlImage());
+  }
+}
diff --git a/tests/library/ZendAfi/View/Helper/AvisTest.php b/tests/library/ZendAfi/View/Helper/AvisTest.php
index fec5ed23b50b81a1755cde60d32797e6bee8e5a7..fa055ec6ea874360f9ad49890c2520600d09c7a0 100644
--- a/tests/library/ZendAfi/View/Helper/AvisTest.php
+++ b/tests/library/ZendAfi/View/Helper/AvisTest.php
@@ -415,4 +415,36 @@ class ViewHelperAvisAmazonTestContenuAvisHtml extends ViewHelperTestCase {
 
 
 
-?>
\ No newline at end of file
+
+class ViewHelperAvisWithoutLinkedRecordAdminLoggedTest extends ViewHelperTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+    $avis_millenium = (new Class_AvisNotice())
+      ->setEntete("J'adore")
+      ->setAvis("Suspense intense très
+                 intéressant longue critique")
+      ->setNote(5)
+      ->setDateAvis('2010-03-18 13:00:00')
+      ->setAbonOuBib(0)
+      ->setStatut(0)
+      ->setClefOeuvre('MILLENIUM-----1')
+      ->setSourceAuthor(null)
+      ->setUser(null);
+
+    $helper = new ZendAfi_View_Helper_Avis();
+    $helper->setView($this->view);
+
+    $this->login(ZendAfi_Acl_AdminControllerRoles::ADMIN_PORTAIL);
+
+    $this->html = $helper->avis($avis_millenium);
+  }
+
+
+  /** @test */
+  public function workKeyShouldBePresent() {
+    $this->assertXPathContentContains($this->html,
+                                      '//h2', '(MILLENIUM-----1)');
+  }
+}
\ No newline at end of file
diff --git a/tests/scenarios/SearchByWork/SearchResultByWorkTest.php b/tests/scenarios/SearchByWork/SearchResultByWorkTest.php
index 3ba7a912b400d2f59d1fe95cc428c918c7974085..8ef46a83f7faf8aae09f34a7481a08a541455d17 100644
--- a/tests/scenarios/SearchByWork/SearchResultByWorkTest.php
+++ b/tests/scenarios/SearchByWork/SearchResultByWorkTest.php
@@ -32,6 +32,7 @@ abstract class SearchResultByWorkTestCase extends AbstractControllerTestCase {
     Class_Profil::setCurrentProfil($profil);
 
     Class_CodifAuteur::newInstance(['id' => '2897',
+                                    'libelle' => 'Terry Pratchett',
                                     'code_alpha' => 'PRATCHETTxTERRY',
                                     'youtube_channel_id' => '80937UINT'])
       ->assertSave();
@@ -399,14 +400,14 @@ class SearchResultByWorkAjaxItemsTest extends SearchResultByWorkTestCase {
 
 
   /** @test */
-  public function pageShouldContainsTombouctouItem() {
-    $this->assertXPathContentContains('//a[contains(@href, "/bib/en-lire-plus/expressionRecherche")]', 'Tombouctou');
+  public function shouldContainsAnchorToTombouctouItemLibrary() {
+    $this->assertXPathContentContains('//a[contains(@href, "/bib/en-lire-plus/id/1")]', 'Tombouctou');
   }
 
 
   /** @test */
-  public function pageShouldContainsTotoItem() {
-    $this->assertXPathContentContains('//a[contains(@href, "/bib/en-lire-plus/expressionRecherche")]', 'Cran');
+  public function shouldContainsAnchorToTotoItemLibrary() {
+    $this->assertXPathContentContains('//a[contains(@href, "/bib/en-lire-plus/id/2")]', 'Cran');
   }
 
 
diff --git a/tests/scenarios/Templates/TemplateDigitalResourcesTest.php b/tests/scenarios/Templates/TemplateDigitalResourcesTest.php
index ae31c828753fbdc278c7c2f850016af65271dcf5..47c6fd05e1226e063ac25a18ac705f0877203fe8 100644
--- a/tests/scenarios/Templates/TemplateDigitalResourcesTest.php
+++ b/tests/scenarios/Templates/TemplateDigitalResourcesTest.php
@@ -236,4 +236,157 @@ class TemplateDigitalResourcesDilicomItemTest extends TemplateDigitalResourcesDi
   public function consultLinkShouldBeDisabled() {
     $this->assertXPathContentContains('//a[@data-disabled]', 'Consulter le livre en ligne');
   }
+}
+
+
+
+/** @see http://forge.afi-sa.fr/issues/115415 */
+class TemplateDigitalResourcesWrongIndexationTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_buildTemplateProfil(['id' => 5,
+                                 'template' => 'CHILI']);
+
+    $this->fixture('Class_Notice',
+                   ['id' => 34,
+                    'titre_principal' => 'Psycho',
+                    'type_doc' => Class_TypeDoc::DISQUE,
+                   ])
+         ->setAuteurPrincipal('M.');
+
+    $this->fixture('Class_CodifAuteur',
+                   ['id' => 324,
+                    'libelle' => 'M']);
+
+    $mock = $this->mock()
+
+                 ->whenCalled('getResponse')
+                 ->with('https://www.last.fm/music/M/Psycho')
+                 ->answers((new Class_Testing_HttpResponse)
+                           ->setBody(file_get_contents(ROOT_PATH . 'tests/fixtures/lastfm_bashung_albums.html')))
+
+                 ->whenCalled('getResponse')
+                 ->with('https://www.last.fm/music/M/+images/')
+                 ->answers((new Class_Testing_HttpResponse)->setBody('
+<html lang="fr">
+  <head></head>
+  <body>
+    <ul class="image-list">
+      <li class="image-list-item">
+        <a href="/music/Sh%C5%8D+Aimoto/+images/" class="image-list-link">
+           <img class="image-list-image"
+                src="https://lastfm-img2.akamaized.net/i/u/avatar170s/big_picture_300x300.jpg"
+                alt="None">
+        </a>
+      </li>
+    </ul>
+  </body>
+</html>'))
+
+                 ->beStrict();
+
+    Class_WebService_SimpleWebClient::setInstance($mock);
+
+    $this->dispatch('/noticeajax/media/id/34');
+  }
+
+
+  /** @test */
+  public function authorNameShouldBeMWithoutEndingDot() {
+    $this->assertXPath('//img[@title =  "Illustration du document Psycho de M"]');
+  }
+}
+
+
+
+abstract class TemplateDigitalResourcesTrailerAdministrationTestCase extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_buildTemplateProfil(['id' => 5,
+                                 'template' => 'CHILI']);
+
+    $this->fixture('Class_Notice',
+                   ['id' => 34,
+                    'titre_principal' => 'Psycho',
+                    'type_doc' => Class_TypeDoc::DISQUE,
+                   ])
+         ->setAuteurPrincipal('M.');
+
+    $this->fixture('Class_CodifAuteur',
+                   ['id' => 324,
+                    'libelle' => 'M']);
+
+    Class_CosmoVar::set('url_services', 'https://cache-server.org');
+
+    $mock_web = $this->mock()
+
+                     ->whenCalled('open_url')
+                     ->answers(json_encode(['source' => 'test_bokeh',
+                                            'disable_trailer' => $this->_is_trailer_disabled,
+                                            'player' => '<iframe src="https://www.super-trailers.org/?media=18397590" style="width:500px; height:400px" frameborder="0"></iframe>']));
+
+    Class_WebService_AllServices::setHttpClient($mock_web);
+
+    $this->dispatch('/noticeajax/media/id/34');
+  }
+}
+
+
+
+
+class TemplateDigitalResourcesTrailerEnabledAdministrationTest extends TemplateDigitalResourcesTrailerAdministrationTestCase {
+
+  protected $_is_trailer_disabled = 0;
+
+
+  /** @test */
+  public function linkToEditTrailerShouldBePresent() {
+    $this->assertXPath('//button[@data-url =  "/admin/records/trailer/id/34"]');
+  }
+
+
+  /** @test */
+  public function linkToDisableTrailerShouldBePresent() {
+    $this->assertXPath('//button[@data-url =  "/admin/records/disable-trailer/id/34"]');
+  }
+
+
+  /** @test */
+  public function trailerShouldBeRender() {
+    $this->assertXPath('//iframe[@src="https://www.super-trailers.org/?media=18397590"]');
+  }
+}
+
+
+
+
+class TemplateDigitalResourcesTrailerDisabledAdministrationTest extends TemplateDigitalResourcesTrailerAdministrationTestCase {
+
+  protected $_is_trailer_disabled = 1;
+
+
+  /** @test */
+  public function linkToEditTrailerShouldNotBePresent() {
+    $this->assertNotXPath('//button[@data-url =  "/admin/records/trailer/id/34"]');
+  }
+
+
+  /** @test */
+  public function linkToEnableTrailerShouldBePresent() {
+    $this->assertXPath('//button[@data-url =  "/admin/records/enable-trailer/id/34"]');
+  }
+
+
+  /** @test */
+  public function trailerShouldNotBeRender() {
+    $this->assertNotXPath('//iframe[@src="https://www.super-trailers.org/?media=18397590"]');
+  }
 }
\ No newline at end of file
diff --git a/tests/scenarios/Templates/TemplatesAbonneTest.php b/tests/scenarios/Templates/TemplatesAbonneTest.php
index 64f7bafce7cd4ef4642b6ba57b9d00429b4052fc..02f33a536737506ba2af1bc936601f8220e0ee44 100644
--- a/tests/scenarios/Templates/TemplatesAbonneTest.php
+++ b/tests/scenarios/Templates/TemplatesAbonneTest.php
@@ -176,13 +176,19 @@ abstract class TemplatesIntonationAccountTestCase extends TemplatesIntonationTes
                     'titre_principal' => 'Blacksad',
                     'clef_alpha' => 'BLACKSAD']);
 
-    $this->fixture('Class_PanierNotice',
-                   ['id' => 2,
-                    'id_panier' => 1,
-                    'libelle' => 'Mes BD',
-                    'date_maj' => '10/02/2011',
-                    'notices' => 'COMBAT ORDINAIRE;BLACKSAD',
-                    'user' => $current_user]);
+    $libre_domain = $this->fixture('Class_Catalogue',
+                                   ['id' => 1,
+                                    'libelle' => 'Libre']);
+
+    $mes_bd = $this->fixture('Class_PanierNotice',
+                             ['id' => 2,
+                              'id_panier' => 1,
+                              'libelle' => 'Mes BD',
+                              'date_maj' => '10/02/2011',
+                              'notices' => 'COMBAT ORDINAIRE;BLACKSAD',
+                              'user' => $current_user]);
+
+    $mes_bd->setCatalogues([$libre_domain]);
 
     $criteres_potter = (new Class_CriteresRecherche)
       ->setParams(['expressionRecherche' => 'Harry Potter',
@@ -691,6 +697,12 @@ class TemplatesDispatchAbonneSelectionsTest extends TemplatesIntonationAccountTe
   }
 
 
+  /** @test */
+  public function mesBdShouldHaveBadge1DomainLinked() {
+    $this->assertXPathContentContains('//div//span[contains(@class, "badge")]', '1 domaine lié');
+  }
+
+
   /**
    * @test
    * @see #115804
@@ -769,69 +781,149 @@ class TemplatesDispatchAbonneSuivreUneRechercheTest extends TemplatesIntonationA
 
 
 
-class TemplatesIntonationDispatchAccountEditTest extends TemplatesIntonationAccountTestCase {
-  /** @test */
-  public function editAccountShouldBePresent() {
-    $this->dispatch('/opac/abonne/fiche/id_profil/72');
-    $this->assertXPath('//a[contains(@href, "/abonne/modifier/id_profil/72")]');
+
+class TemplatesAbonneAccountWithInterdireModifTest extends TemplatesIntonationAccountTestCase {
+  public function setUp() {
+    parent::setUp();
+    Class_AdminVar::set('INTERDIRE_MODIF_FICHE_ABONNE', '1');
   }
 
 
   /** @test */
-  public function editPasswordShouldBePresent() {
+  public function editAccountShouldNotBePresent() {
     $this->dispatch('/opac/abonne/fiche/id_profil/72');
-    $this->assertXPath('//a[contains(@href, "/abonne/changer-mon-mot-de-passe/id_profil/72")]');
+    $this->assertNotXPath('//a[contains(@href, "/abonne/modifier/id_profil/72")]');
   }
 
 
   /** @test */
-  public function editAccountShouldContainsFormToAbonneModifier() {
-    $this->dispatch('/opac/abonne/modifier/id_profil/72');
-    $this->assertXPath('//form[contains(@action, "/abonne/modifier/id_profil/72")]');
+  public function changePasswordtShouldNotBePresent() {
+    $this->dispatch('/opac/abonne/fiche/id_profil/72');
+    $this->assertNotXPath('//a[contains(@href, "/abonne/changer-mon-mot-de-passe/id_profil/72")]');
   }
 
 
   /** @test */
-  public function editPasswordShouldContainsFormToAbonneChangerMonMotDePasse() {
+  public function editPasswordShouldRedirectWithErrorMessage() {
     $this->dispatch('/opac/abonne/changer-mon-mot-de-passe/id_profil/72');
-    $this->assertXPath('//form[contains(@action, "/abonne/changer-mon-mot-de-passe/id_profil/72")]');
+    $this->assertRedirect();
+    $this->assertFlashMessengerContentContains('Vos informations ne peuvent pas être modifiées.');
   }
 
 
   /** @test */
-  public function editPasswordNotAllowedShouldRedirectWithErrorMessage() {
-    Class_AdminVar::newInstanceWithId('INTERDIRE_MODIF_FICHE_ABONNE',
-                                      ['valeur' => '1']);
-
-    $this->dispatch('/opac/abonne/changer-mon-mot-de-passe/id_profil/72');
+  public function editAccountRedirectWithErrorMessage() {
+    $this->dispatch('/opac/abonne/modifier/id_profil/72');
+    $this->assertRedirect();
     $this->assertFlashMessengerContentContains('Vos informations ne peuvent pas être modifiées.');
   }
 
 
   /** @test */
-  public function editPasswordShouldContainsLinkToAuthLostpass() {
-    $this->dispatch('/opac/abonne/changer-mon-mot-de-passe/id_profil/72');
-    $this->assertXPath('//div//a[contains(@href, "/auth/lostpass/id_profil/72")]');
+  public function postEditAccountRedirectWithErrorMessage() {
+    $this->postDispatch('/opac/abonne/modifier/id_profil/72', ['nom' => 'Tom']);
+    $this->assertRedirect();
+    $this->assertFlashMessengerContentContains('Vos informations ne peuvent pas être modifiées.');
+  }
+}
+
+
+
+
+class TemplatesAbonneAccountFieldsTest extends TemplatesIntonationAccountTestCase {
+  public function fields() {
+    return [['nom', 'dev'], ['prenom', 'dev'], ['pseudo', 'dev'],
+            ['adresse', '5 av du paradis'], ['code_postal', '89399'], ['ville', 'soucy'],
+            ['mail', 'user@server.com'],
+            ['telephone', '12 34 56 78 90'],
+            ['mobile', '12 34 56 78 90'],
+            ['mode_contact', Class_Users::MODE_CONTACT_SMS]];
+  }
+
+  /**
+   * @test
+   * @dataProvider fields
+   */
+  public function notAllowedFieldsShouldNotCreateInput($field) {
+    Class_AdminVar::set('CHAMPS_FICHE_UTILISATEUR', '');
+    $this->dispatch('/opac/abonne/modifier/id_profil/72');
+    $this->assertNotXPath('//input[@name="' . $field . '"]');
+  }
+
+
+  /**
+   * @test
+   * @dataProvider fields
+   */
+  public function allowedFieldsShouldCreateInput($field) {
+    Class_AdminVar::set('CHAMPS_FICHE_UTILISATEUR', $field);
+    $this->dispatch('/opac/abonne/modifier/id_profil/72');
+    $this->assertXPath('//input[@name="' . $field . '"]');
   }
 
 
+  /**
+   * @test
+   * @dataProvider fields
+   */
+  public function postAllowedFieldValueShouldUpdateUser($field, $value) {
+    Class_AdminVar::set('CHAMPS_FICHE_UTILISATEUR', $field);
+    $this->postDispatch('/opac/abonne/modifier/id_profil/72', [$field => $value]);
+    $this->assertEquals($value, Class_Users::getIdentity()->callGetterByAttributeName($field));
+  }
+
+
+  /**
+   * @test
+   * @dataProvider fields
+   */
+  public function postNotAllowedFieldValueShouldNotUpdateUser($field, $value) {
+    Class_AdminVar::set('CHAMPS_FICHE_UTILISATEUR', '');
+    $this->postDispatch('/opac/abonne/modifier/id_profil/72', [$field => $value]);
+    $this->assertNotEquals($value, Class_Users::getIdentity()->callGetterByAttributeName($field));
+  }
+
+
+  /**
+   * @test
+   */
+  public function postAllowedButInvalidFieldValueShouldNotUpdateUserAndDisplayError() {
+    Class_AdminVar::set('CHAMPS_FICHE_UTILISATEUR', 'mail');
+    $this->postDispatch('/opac/abonne/modifier/id_profil/72', ['mail' => 'toto']);
+    $this->assertNotEquals('toto', Class_Users::getIdentity()->getMail());
+    $this->assertXPathContentContains('//ul[@class="errors"]', 'pas un email valide');
+  }
+}
+
+
+
+
+class TemplatesAbonneAccountEditTest extends TemplatesIntonationAccountTestCase {
   /** @test */
-  public function editAccountPostNameDevShouldUpdateUser() {
-    Class_AdminVar::newInstanceWithId('CHAMPS_FICHE_UTILISATEUR',
-                                      ['valeur' => 'pseudo;nom;password']);
+  public function editAccountLinkShouldBePresent() {
+    $this->dispatch('/opac/abonne/fiche/id_profil/72');
+    $this->assertXPath('//a[contains(@href, "/abonne/modifier/id_profil/72")]');
+  }
+
 
-    $this->postDispatch('/opac/abonne/modifier/id_profil/72', ['nom' => 'Dev']);
-    $this->assertEquals('Dev', Class_Users::getIdentity()->getNom());
+  /** @test */
+  public function editPasswordLinkShouldBePresent() {
+    $this->dispatch('/opac/abonne/fiche/id_profil/72');
+    $this->assertXPath('//a[contains(@href, "/abonne/changer-mon-mot-de-passe/id_profil/72")]');
   }
 
 
   /** @test */
-  public function editAccountNotAllowedShouldRedirectWithErrorMessage() {
-    Class_AdminVar::newInstanceWithId('INTERDIRE_MODIF_FICHE_ABONNE',
-                                      ['valeur' => '1']);
+  public function editPasswordShouldContainsFormToAbonneChangerMonMotDePasse() {
+    $this->dispatch('/opac/abonne/changer-mon-mot-de-passe/id_profil/72');
+    $this->assertXPath('//form[contains(@action, "/abonne/changer-mon-mot-de-passe/id_profil/72")]');
+  }
 
-    $this->postDispatch('/opac/abonne/modifier/id_profil/72', ['nom' => 'Tom']);
-    $this->assertFlashMessengerContentContains('Vos informations ne peuvent pas être modifiées.');
+
+  /** @test */
+  public function editPasswordShouldContainsLinkToAuthLostpass() {
+    $this->dispatch('/opac/abonne/changer-mon-mot-de-passe/id_profil/72');
+    $this->assertXPath('//div//a[contains(@href, "/auth/lostpass/id_profil/72")]');
   }
 
 
@@ -1076,7 +1168,7 @@ class TemplatesIntonationDispatchCreerSelectionTest extends TemplatesIntonationA
   /** @test */
   public function dispatchCreerSelectionShouldRenderForm() {
     $this->dispatch('/opac/abonne/creer-selection/id_profil/72');
-    $this->assertXPathContentContains('//div', 'Titre');
+    $this->assertXPath('//main//div//form');
   }
 
 
@@ -1317,4 +1409,159 @@ class TemplatesAbonneWithPNBHoldsTest extends AbstractControllerTestCase {
   public function actionDeleteHoldShouldPresent() {
     $this->assertXPathContentContains('//div//a[@href="/abonne/delete-pnb-hold/id/6_1"][@class="card-link"]', 'Supprimer');
   }
-}
\ No newline at end of file
+}
+
+
+
+
+class TemplatesAbonneSelectionsTest extends TemplatesIntonationAccountTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/opac/abonne/selections/id_profil/72');
+  }
+
+
+  /** @test */
+  public function dispatchSelectionsShouldContainsCreerSelectionAnchor() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/creer-selection")]','Créer');
+  }
+
+
+
+  /** @test */
+  public function shouldNotContainsVoirLesPaniersRangesDansLesDomaines() {
+    $this->assertNotXPathContentContains('//a[contains(@href, "/abonne/selections-dans-les-domaines")]','Voir les sélections dans les domaines');
+  }
+
+
+  /** @test */
+  public function shouldNotContainsVoirLesPaniersDesProfessionnels() {
+    $this->assertNotXPathContentContains('//a[contains(@href, "/abonne/selections-des-professionnels")]','Voir les sélections des professionnels');
+  }
+}
+
+
+
+
+class TemplatesAbonneSelectionsAsAdminTest extends Admin_AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->_buildTemplateProfil(['id' => 72]);
+    $this->dispatch('/opac/abonne/selections');
+  }
+
+
+  /** @test */
+  public function shouldContainsCreerSelectionAnchor() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/creer-selection")]','Créer');
+  }
+
+
+  /** @test */
+  public function shouldContainsVoirLesSelectionsDansLesDomaines() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections-dans-les-domaines")]','Sélections dans les domaines');
+  }
+
+
+  /** @test */
+  public function shouldContainsVoirLesSelectionsDesProfessionnels() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections-des-professionnels")]','Sélections des professionnels');
+  }
+}
+
+
+
+
+class TemplatesAbonneSelectionsDansLesDomainesAsAdminTest extends Admin_AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->_buildTemplateProfil(['id' => 72]);
+    $this->dispatch('/opac/abonne/selections-dans-les-domaines');
+  }
+
+
+    /** @test */
+  public function shouldContainsVoirMesSelections() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections")]','Mes sélections');
+  }
+
+
+  /** @test */
+  public function shouldContainsVoirLesSelectionsDesProfessionnels() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections-des-professionnels")]','Sélections des professionnels');
+  }
+}
+
+
+
+class TemplatesAbonneSelectionsDansLesDomainesAsBorrowerLoggedTest extends AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->_buildTemplateProfil(['id' => 72]);
+    $francois = $this->fixture('Class_Users',
+                               ['id' => 1213,
+                                'login' => 'François',
+                                'password' => 'auienauiet'
+                               ]);
+    ZendAfi_Auth::getInstance()->logUser($francois);
+    $this->dispatch('/opac/abonne/selections-dans-les-domaines');
+  }
+
+
+    /** @test */
+  public function shouldContainsNotifyMessageNotAllowedToAccessThisPage() {
+    $this->assertFlashMessengerContentContains('Vous n\'avez pas le droit d\'accéder à cette page.');
+  }
+
+
+  /** @test */
+  public function pageShouldRediret() {
+    $this->assertRedirect();
+  }
+}
+
+
+
+
+class TemplatesAbonneSelectionsProfessionnelsAsAdminTest extends Admin_AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+    $this->_buildTemplateProfil(['id' => 72]);
+
+    $this->onLoaderOfModel('Class_PanierNotice')
+         ->whenCalled('findAllBelongsToAdmin')
+         ->answers([]);
+
+    $this->dispatch('/opac/abonne/selections-des-professionnels');
+  }
+
+
+    /** @test */
+  public function shouldContainsVoirMesSelections() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections")]','Mes sélections');
+  }
+
+
+  /** @test */
+  public function shouldContainsVoirLesSelectionsDansLesDomaines() {
+    $this->assertXPathContentContains('//a[contains(@href, "/abonne/selections-dans-les-domaines")]','Sélections dans les domaines');
+  }
+}
diff --git a/tests/scenarios/Templates/TemplatesArticlesTest.php b/tests/scenarios/Templates/TemplatesArticlesTest.php
index 52d82d127066e4ad7395ce31ca4f0ce411a39769..daede6a21c0aeb8f24f98c18a842e0a1a15a59b0 100644
--- a/tests/scenarios/Templates/TemplatesArticlesTest.php
+++ b/tests/scenarios/Templates/TemplatesArticlesTest.php
@@ -690,7 +690,7 @@ class TemplatesArticlesWidgetWithDescriptionLengthTest extends AbstractControlle
                    'size' => 1,
                    'description_length' => 4]);
 
-    $this->dispatch('/opac/widget/render/widget_id/1/profile_id/34');
+    $this->dispatch('/opac/widget/render/widget_id/1/profile_id/34/anchor_target/_blank');
   }
 
 
@@ -699,6 +699,18 @@ class TemplatesArticlesWidgetWithDescriptionLengthTest extends AbstractControlle
     $this->assertXPathContentContains('//div[contains(@class, "news widget")]//p[contains(@class, "model_description")]',
                                       'La description s\'arrête ici …');
   }
+
+
+  /** @test */
+  public function allFourAnchorsWithHrefShouldHaveTargetBlank() {
+    $this->assertXPathCount('//a[@href][@target="_blank"]', '4');
+  }
+
+
+  /** @test */
+  public function anchorsWithNoUrlShouldNotHaveTargetBlank() {
+    $this->assertXPathCount('//a[not(href)][not(@target)]', 2);
+  }
 }
 
 
@@ -966,3 +978,42 @@ class TemplatesArticlesWithWidgetRenderWallLoggedAsAdminTest
     $this->assertNotXPath('//a[contains(@href, "admin/widget/edit-widget/id/no_")]');
   }
 }
+
+
+
+class TemplatesArticlesWithLibraryUrlRewriteTest extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+    $this->_buildTemplateProfil(['id' => 32,
+                                 'template' => 'MYBIBAPP']);
+
+    $this->fixture('Class_Article', [ 'id' => 55,
+                                     'titre' => 'Mes dvds',
+                                     'contenu' => '<img class="bokeh_kiosk" data-code="KIOSQUE" data-form="titre=&amp;style_liste=mur&amp;aleatoire=1&amp;tri=1&amp;boite=&amp;rss=1&amp;MybibappShowFooter=1&amp;">',
+                                     'id_cat' => 5]);
+
+    $this->fixture('Class_ArticleCategorie',
+                   ['id' => 5,
+                    'libelle' => 'DVD',
+                    'id_site' => '23'
+                   ]);
+
+    $this->fixture('Class_Bib',
+                   ['id' => 23,
+                    'libelle' => 'Annecy',
+                    'rewrite_url' => 'annecy'
+                   ]);
+
+    $this->dispatch('/cms/articleview/id/55');
+  }
+
+
+  /** @test */
+  public function linkToAnnecyShouldBeAnnecy() {
+    $this->assertXPathContentContains('//a[contains(@href,"/annecy")][contains(@class,"badge_article_library")]',
+                                      'Annecy',$this->_response->getBody());
+
+  }
+}
diff --git a/tests/scenarios/Templates/TemplatesReviewsTest.php b/tests/scenarios/Templates/TemplatesReviewsTest.php
index 0d247603b10d80e5d1e5d0113a82763b4e47b2bc..1f2911b8aeab2c7d54f5aff4d4b6f9481d8caf82 100644
--- a/tests/scenarios/Templates/TemplatesReviewsTest.php
+++ b/tests/scenarios/Templates/TemplatesReviewsTest.php
@@ -252,10 +252,7 @@ class TemplatesReviewsBlogActionTest extends TemplatesIntonationTestCase {
 
 
 
-class TemplatesReviewsBlogReadMoreActionTest extends TemplatesIntonationTestCase {
-  protected $_storm_default_to_volatile = true;
-
-
+abstract class TemplatesReviewsBlogReadMoreActionTestCase extends TemplatesIntonationTestCase {
   public function setUp() {
     parent::setUp();
 
@@ -268,18 +265,106 @@ class TemplatesReviewsBlogReadMoreActionTest extends TemplatesIntonationTestCase
                     'avis' => 'Le Roi des cons sur son throne',
                     'source_author' => null]);
 
+    if ($user = $this->_prepareUser())
+      Class_AvisNotice::find(4)->setUser($user);
+
+    $this->_prepareRecord();
+
+    $this->dispatch('/blog/read-more/id/4');
+  }
+
+
+  protected function _prepareUser() {}
+
+
+  protected function _prepareRecord() {}
+
+
+  /** @test */
+  public function leRoiShouldBePresent() {
+    $this->assertXPathContentContains('//div', 'Le Roi');
+  }
+}
+
+
+
+
+class TemplatesReviewsBlogReadMoreActionTest extends TemplatesReviewsBlogReadMoreActionTestCase {
+  protected function _prepareUser() {
+    return $this->fixture('Class_Users',
+                          ['id' => 33,
+                           'password' => 'secr3t',
+                           'login' => 'Brasserie',
+                          ]);
+  }
+
+
+   protected function _prepareRecord() {
     $this->fixture('Class_Notice',
                    ['id' => 2,
                     'clef_oeuvre' => 'PSYKO',
+                    'titre_principal' => 'Psyko',
                    ]);
+  }
 
-    $this->dispatch('/blog/read-more/id/4');
+
+  /** @test */
+  public function titleShouldContainAvisDonneParBrasserieSurLeDocumentPsyko() {
+    $this->assertXPathContentContains('//title', 'Avis donné par : Brasserie sur le document Psyko');
+  }
+}
+
+
+
+
+class TemplatesReviewsBlogReadMoreActionWithoutLinkedRecordTest
+  extends TemplatesReviewsBlogReadMoreActionTestCase {
+
+  protected function _prepareUser() {
+    return $this->fixture('Class_Users',
+                          ['id' => 33,
+                           'password' => 'secr3t',
+                           'login' => 'Brasserie',
+                          ]);
   }
 
 
   /** @test */
-  public function leRoiShouldBePresent() {
-    $this->assertXPathContentContains('//div', 'Le Roi');
+  public function titleShouldContainAvisDonneParBrasserie() {
+    $this->assertXPath('//title[text()="Avis donné par : Brasserie"]');
+  }
+}
+
+
+
+
+class TemplatesReviewsBlogReadMoreActionWithoutLinkedUserTest
+  extends TemplatesReviewsBlogReadMoreActionTestCase {
+
+  protected function _prepareRecord() {
+    $this->fixture('Class_Notice',
+                   ['id' => 2,
+                    'clef_oeuvre' => 'PSYKO',
+                    'titre_principal' => 'Psyko',
+                   ]);
+  }
+
+
+  /** @test */
+  public function titleShouldContainAvisSurLeDocumentPsyko() {
+    $this->assertXPath('//title[text()="Avis sur le document Psyko"]');
+  }
+}
+
+
+
+
+class TemplatesReviewsBlogReadMoreActionWithoutLinkedUserNorRecordTest
+  extends TemplatesReviewsBlogReadMoreActionTestCase {
+
+  /** @test */
+  public function titleShouldContainAvis() {
+    $this->assertXPath('//title[text()="Avis"]', $this->_response->getBody());
   }
 }
 
diff --git a/tests/scenarios/Templates/TemplatesRssTest.php b/tests/scenarios/Templates/TemplatesRssTest.php
index b5fe5a5410351667a7cb215aafc9a6ac8017c2ef..e571fd67080592198f685f28e8f64906b6416a52 100644
--- a/tests/scenarios/Templates/TemplatesRssTest.php
+++ b/tests/scenarios/Templates/TemplatesRssTest.php
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
+require_once 'tests/fixtures/LemondeRss.php';
+
 
 class TemplatesRssWidgetWithDescriptionLengthTest extends AbstractControllerTestCase {
 
@@ -46,7 +48,7 @@ class TemplatesRssWidgetWithDescriptionLengthTest extends AbstractControllerTest
                    'description_length' => 4]);
 
 
-        $this->fixture('Class_Rss',
+    $this->fixture('Class_Rss',
                    ['id' => 3,
                     'titre' => 'Flux RSS Bokeh',
                     'url' => 'https://bokeh-library-portal.org/news/rss',
@@ -78,7 +80,7 @@ class TemplatesRssForVolatileKiosqueTest extends AbstractControllerTestCase {
     Storm_Cache::beVolatile();
 
     $profile = $this->_buildTemplateProfil(['id' => 213,
-                                 'TEMPLATE' => 'CHILI']);
+                                            'TEMPLATE' => 'CHILI']);
 
     $profile_patcher = (new Class_Template_ProfilePatcher(null))
       ->setProfile($profile);
@@ -98,7 +100,7 @@ class TemplatesRssForVolatileKiosqueTest extends AbstractControllerTestCase {
                    'id_items' => '9',
                    'rendering' => Intonation_Library_Widget_Carousel_Definition::CARD_DESCRIPTION,
                    'rss' => 1
-                   ]
+                  ]
       );
 
     $widget = (new Class_Systeme_Widget_Widget)
@@ -121,4 +123,76 @@ class TemplatesRssForVolatileKiosqueTest extends AbstractControllerTestCase {
   public function ninNinDansLUniversShouldBePresent() {
     $this->assertXPathContentContains('//item/title', '<![CDATA[NinNin dans l\'Univers, ]]>');
   }
-}
\ No newline at end of file
+}
+
+
+
+
+class TemplatesRssWidgetWithPaginatedListTest extends AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    Class_WebService_Abstract::setHttpClient($this
+                                             ->mock()
+                                             ->whenCalled('open_url')
+                                             ->with('https://rss.lemonde.fr/c/205/f/3050/index.rss')
+                                             ->answers(RssFixtures::lemondeRSS())
+                                             ->beStrict());
+
+
+    Storm_Cache::beVolatile();
+
+    $profile = $this->_buildTemplateProfil(['id' => 2890,
+                                            'template' => 'MUSCLE']);
+
+    $params = ['rendering' => 'card',
+               'order' => Class_CriteresRecherche::SORT_RANDOM,
+               'layout' => Intonation_Library_Widget_Carousel_Definition::LISTING_WITH_OPTIONS];
+
+    $profile_patcher = (new Class_Template_ProfilePatcher(null))
+      ->setProfile($profile)
+      ->addWidget(Intonation_Library_Widget_Carousel_Record_Definition::CODE,
+                  Class_Profil::DIV_MAIN,
+                  $params);
+
+    $widget = (new Class_Systeme_Widget_Widget())
+      ->setId(1)
+      ->setProfileId(2890)
+      ->load();
+
+    $rss = $this->fixture('Class_Rss',
+                          ['id' => 1,
+                           'titre' => 'Flux RSS lemonde',
+                           'url' => 'https://rss.lemonde.fr/c/205/f/3050/index.rss',
+                           'description' => 'lemonde.',
+                           'tags' => 'test;bokeh;libre;git']);
+
+    $rss_items = $rss->getFeedItems();
+
+    $rss_items = array_map(function($element)
+                       {
+                         return
+                           new Intonation_Library_View_Wrapper_RssItem($element,
+                                                                       $this->mock()
+                                                                       ->whenCalled('renderBadges')
+                                                                       ->answers(''));
+                       }, $rss_items);
+
+    $helper = (new Intonation_Library_AjaxPaginatedListHelper)
+      ->setCollection(new Storm_Collection($rss_items));
+
+    $id = $helper->getId();
+
+    $this->dispatch('/opac/index/ajax-paginated-list/id/' . $id);
+  }
+
+
+  /** @test */
+  public function rssItemShouldHaveData() {
+    $this->assertXPathContentContains('//div//a', 'Hirsch a "six minutes pour sauver le RSA"');
+  }
+}
diff --git a/tests/scenarios/Templates/TemplatesWidgetTest.php b/tests/scenarios/Templates/TemplatesWidgetTest.php
index e29cd6a873e3abac2214874bb438799d708f655a..dfa014847365c1b864e412faebe97366285963d0 100644
--- a/tests/scenarios/Templates/TemplatesWidgetTest.php
+++ b/tests/scenarios/Templates/TemplatesWidgetTest.php
@@ -912,8 +912,8 @@ class TemplateRecordsWidgetWithOptionsTest extends TemplatesIntonationTestCase {
 
 
   /** @test */
-  public function shouldContainsEmbededCode() {
-    $this->assertXPathContentContains('//pre', '/widget/render/widget_id/17/profile_id/72');
+  public function shouldContainsEmbededCodeWithAnchorTargetBlank() {
+    $this->assertXPathContentContains('//pre', '/widget/render/widget_id/17/profile_id/72/anchor_target/_blank');
   }