diff --git a/VERSIONS_HOTLINE/100763 b/VERSIONS_HOTLINE/100763
new file mode 100644
index 0000000000000000000000000000000000000000..8fcb3fcd37bb4d67d1954e12ca8c56faecff82ab
--- /dev/null
+++ b/VERSIONS_HOTLINE/100763
@@ -0,0 +1 @@
+ - ticket #100763 : Compte lecteur : Détection et affichage des erreurs pouvant survenir lors de la récupération des prêts en retard auprès du SIGB
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/95311 b/VERSIONS_HOTLINE/95311
new file mode 100644
index 0000000000000000000000000000000000000000..7e3bce089b8720f4ee4dc1116f8e29a17e178e5f
--- /dev/null
+++ b/VERSIONS_HOTLINE/95311
@@ -0,0 +1 @@
+ - ticket #95311 : newsletter : ne pas tronquer les lettre d'infos contenant + de 22000 caractères UTF-8
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/96083 b/VERSIONS_HOTLINE/96083
new file mode 100644
index 0000000000000000000000000000000000000000..d0695bdf3cf01ed53741f2682866949d6903ec0e
--- /dev/null
+++ b/VERSIONS_HOTLINE/96083
@@ -0,0 +1 @@
+ - ticket #96083 : Ajout d'un bouton imprimer dans un article
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/96803 b/VERSIONS_HOTLINE/96803
new file mode 100644
index 0000000000000000000000000000000000000000..20fd72fc66f281ba0e68e1a2e4f5ef7d2c3e8f28
--- /dev/null
+++ b/VERSIONS_HOTLINE/96803
@@ -0,0 +1 @@
+ - ticket #96803 : Gestion des simple quotes erronées
\ No newline at end of file
diff --git a/VERSIONS_HOTLINE/99042 b/VERSIONS_HOTLINE/99042
new file mode 100644
index 0000000000000000000000000000000000000000..cedb3c381640b5d4451e3b8c104fc54548c06dde
--- /dev/null
+++ b/VERSIONS_HOTLINE/99042
@@ -0,0 +1 @@
+ - ticket #99042 : ArteVOD SSO : ajout du referer dans l'url de redirection
\ No newline at end of file
diff --git a/cosmogramme/sql/patch/patch_381.php b/cosmogramme/sql/patch/patch_381.php
index 1149b2094d1cb4c4a7b22e0d4037b4fd3455850f..d1752d297a1551175cf6f9a7145a0f5f92b81f4d 100644
--- a/cosmogramme/sql/patch/patch_381.php
+++ b/cosmogramme/sql/patch/patch_381.php
@@ -1,10 +1,9 @@
 <?php
 $adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
 try {
-  $adapter->query("alter table external_agenda
-                       add column `provider` text not null default ''" );
+  $adapter->query('alter table newsletter_dispatch modify body_html LONGTEXT;');
 } catch (Exception $e) {}
+
 try {
-  $adapter->query("alter table external_agenda
-                       add column `delete_orphan_events` tinyint(1) not null default 0" );
+  $adapter->query('alter table newsletter_dispatch modify body_text LONGTEXT;');
 } catch (Exception $e) {}
diff --git a/cosmogramme/sql/patch/patch_382.php b/cosmogramme/sql/patch/patch_382.php
new file mode 100644
index 0000000000000000000000000000000000000000..1149b2094d1cb4c4a7b22e0d4037b4fd3455850f
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_382.php
@@ -0,0 +1,10 @@
+<?php
+$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+try {
+  $adapter->query("alter table external_agenda
+                       add column `provider` text not null default ''" );
+} catch (Exception $e) {}
+try {
+  $adapter->query("alter table external_agenda
+                       add column `delete_orphan_events` tinyint(1) not null default 0" );
+} catch (Exception $e) {}
diff --git a/library/Class/ArteVodLink.php b/library/Class/ArteVodLink.php
index a28a27d604825d2789a4fcaed4b88c1406e5caeb..207bde536bf09580b5591893f8cd4569e9c98c68 100644
--- a/library/Class/ArteVodLink.php
+++ b/library/Class/ArteVodLink.php
@@ -77,7 +77,8 @@ class Class_ArteVodLink {
                'email' => $this->_user->getMail(),
                'dnaiss' => $this->_user->getNaissance(),
                'datout' => $this->_user->getDateFin(),
-               'return_url' => $this->baseUrl()];
+               'return_url' => $this->baseUrl(),
+               'referer' => Class_Url::getProtocol(). Class_AdminVar::getNomDomaine()];
 
     return static::AUTH_URL
       . '?'
diff --git a/library/Class/Indexation/PseudoNotice.php b/library/Class/Indexation/PseudoNotice.php
index ddb1be1653f1e153675c7450f11560b162481646..01d9f4b5f614ed0c1e95b4950fb56e022e3fb38e 100644
--- a/library/Class/Indexation/PseudoNotice.php
+++ b/library/Class/Indexation/PseudoNotice.php
@@ -599,14 +599,18 @@ class Class_Indexation_PseudoNotice_Cms extends Class_Indexation_PseudoNotice{
 
   protected function _prepareDatas() {
     parent::_prepareDatas();
-    $this->_datas['resume'] = strip_tags($this->_datas['description']);
-    $this->_datas['description'] = strip_tags($this->_datas['contenu']);
+    $this->_datas['resume'] = $this->_sanitizeString($this->_datas['description']);
+    $this->_datas['description'] = $this->_sanitizeString($this->_datas['contenu']);
     $this->_datas['auteur'] = $this->_model->hasAuteur()
       ? $this->_model->getAuteur()->getNomComplet()
       : '';
 
     $this->_datas['annee'] = date('Y', strtotime($this->_model->getDateCreation()));
   }
+
+  protected function _sanitizeString($string) {
+    return strip_tags(html_entity_decode($string, ENT_QUOTES));
+  }
 }
 
 
@@ -648,4 +652,4 @@ class Class_Indexation_PseudoNotice_Null extends Class_Indexation_PseudoNotice{
   public function isValid() {
     return false;
   }
-}
\ No newline at end of file
+}
diff --git a/library/Class/User/Cards.php b/library/Class/User/Cards.php
index 9e73bf671e49a35475f259a9121cabf208cd2328..6f4504e78bd011c8396a0f423c1582991f0f6042 100644
--- a/library/Class/User/Cards.php
+++ b/library/Class/User/Cards.php
@@ -180,6 +180,25 @@ class Class_User_Cards extends Storm_Model_Collection {
                       (new Class_User_ILSSubscription($card))->registerNotificationsOn($notifiable);
                   });
 
+    try {
+      if (!$this->hasPagedLoans() && ($late_loans_count = $this->getLateLoansCount()))
+        $notifiable->notify($this->_plural($late_loans_count,
+                                           '',
+                                           'Vous avez %d document en retard.',
+                                           'Vous avez %d documents en retard.',
+                                           $late_loans_count));
+
+      if ($pullable_items_count = $this->countWaitingToBePulled())
+        $notifiable->notify($this->_plural($pullable_items_count,
+                                           '',
+                                           'Vous avez %d document en attente de retrait.',
+                                           'Vous avez %d documents en attente de retrait.',
+                                           $pullable_items_count));
+    } catch(Exception $e) {
+      $notifiable->notify($this->_('Une erreur est survenue nous empêchant de lister vos prêts.'));
+      $notifiable->notify($this->_('Erreur : "%s"', $e->getMessage()));
+    }
+
     return $this;
   }
 }
\ No newline at end of file
diff --git a/library/Class/Users.php b/library/Class/Users.php
index 4bc6d06f566309d5afd1a5cb2252f7c50b6aa52e..e0a9c8337ad3eaeeed32766cf824ba40839ee2b4 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -1723,31 +1723,8 @@ class Class_Users extends Storm_Model_Abstract {
 
 
   public function registerNotificationsOn($notifiable) {
-    $cards = (new Class_User_Cards($this))->registerNotificationsOn($notifiable);
     (new Class_User_CardsNotification($this))->registerNotificationsOn($notifiable);
-
-    $this
-      ->_notifyOn($notifiable,
-                  !$cards->hasPagedLoans() && ($late_loans_count = $cards->getLateLoansCount()),
-                  $this->_plural($late_loans_count,
-                                 '',
-                                 'Vous avez %d document en retard.',
-                                 'Vous avez %d documents en retard.',
-                                 $late_loans_count))
-      ->_notifyOn($notifiable,
-                  $pullable_items_count = $cards->countWaitingToBePulled(),
-                  $this->_plural($pullable_items_count,
-                                 '',
-                                 'Vous avez %d document en attente de retrait.',
-                                 'Vous avez %d documents en attente de retrait.',
-                                 $pullable_items_count));
-  }
-
-
-  protected function _notifyOn($notifiable, $condition, $message) {
-    if ($condition)
-      $notifiable->notify($message);
-    return $this;
+    (new Class_User_Cards($this))->registerNotificationsOn($notifiable);
   }
 
 
diff --git a/library/Class/WebService/SIGB/Koha/Service.php b/library/Class/WebService/SIGB/Koha/Service.php
index 24392e14aafe73aa36a60edd2917577fce43b94b..40f45ea9a98fd85a85c5f806dfbd1adde3611cf7 100644
--- a/library/Class/WebService/SIGB/Koha/Service.php
+++ b/library/Class/WebService/SIGB/Koha/Service.php
@@ -150,28 +150,37 @@ class Class_WebService_SIGB_Koha_Service extends Class_WebService_SIGB_AbstractR
     $loans_page = 1;
     $loans = new Storm_Collection();
 
-    do {
-      $page_params = $this->loans_per_page
-        ? ['loans_per_page' => $this->loans_per_page,
-           'loans_page' => $loans_page]
-        : [];
-
-      $params = array_merge(['patron_id' => $emprunteur->getId(),
-                             'show_contact' => 0,
-                             'show_loans' => 1,
-                             'show_holds' => 0 ],
-                            $page_params);
-
-      $reader = $this->ilsdiGetLoansPage($params,
-                                                    Class_WebService_SIGB_Koha_LoansPageReader::newInstance());
-      $loans_page ++;
-      $loans->addAll($reader->getLoans());
-    } while ($reader->getTotal() > $loans->count());
+    $this->_getEmpruntPagesOf($emprunteur, $loans);
 
     return $loans->getArrayCopy();
   }
 
 
+
+  protected function _getEmpruntPagesOf($emprunteur, $loans, $loans_page = 1) {
+    $page_params = $this->loans_per_page
+      ? ['loans_per_page' => $this->loans_per_page,
+         'loans_page' => $loans_page]
+      : [];
+
+    $params = array_merge(['patron_id' => $emprunteur->getId(),
+                           'show_contact' => 0,
+                           'show_loans' => 1,
+                           'show_holds' => 0 ],
+                          $page_params);
+
+    if (!$reader = $this->ilsdiGetLoansPage($params,
+                                            Class_WebService_SIGB_Koha_LoansPageReader::newInstance()))
+      return $this;
+
+    $loans->addAll($reader->getLoans());
+    if ($reader->getTotal() > $loans->count())
+      $this->_getEmpruntPagesOf($emprunteur, $loans, $loans_page + 1);
+
+    return $this;
+  }
+
+
   protected function getEmprunteurFor($patron_id) {
     return $this->ilsdiGetPatronInfo(['patron_id' => $patron_id,
                                       'show_contact' => 1,
diff --git a/library/Class/WebService/SIGB/Opsys/Service.php b/library/Class/WebService/SIGB/Opsys/Service.php
index 862ac158ddc466a556178bb48ffb4099afdaf452..6c044281cf9c4cf8ab85484b86177d69edaff9a2 100644
--- a/library/Class/WebService/SIGB/Opsys/Service.php
+++ b/library/Class/WebService/SIGB/Opsys/Service.php
@@ -154,18 +154,19 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
   }
 
 
-  public function connect(){
-    try {
-      $osr = $this->search_client->OuvrirSession(new OuvrirSession());
-      $this->guid = $osr->getGUID();
-    } catch (SoapFault $e) {
-      return false;
-    }
-    return ($this->isConnected());
+  public function connect() {
+    return $this
+      ->_withClientTryOrDo(function($client)
+                           {
+                             $osr = $client->OuvrirSession(new OuvrirSession());
+                             $this->guid = $osr->getGUID();
+                             return $this->isConnected();
+                           },
+                           function($exception) { return false; });
   }
 
 
-    /** @codeCoverageIgnore */
+  /** @codeCoverageIgnore */
   protected function _dumpSoapTrace() {
     var_dump($this->search_client->__getLastRequestHeaders());
     var_dump($this->search_client->__getLastRequest());
@@ -175,26 +176,23 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
 
 
 
-  public function disconnect(){
-    $fs=new FermerSession($this->guid);
-    try {
-      $fsr = $this->search_client->FermerSession($fs);
-    } catch (Exception $e) {
-        //Aloes V190 plante parfois sur les FermerSession
-    }
+  public function disconnect() {
+    //Aloes V190 plante parfois sur les FermerSession
+    $this
+      ->_withClientTryOrDo(function($client)
+                           {
+                             $client->FermerSession(new FermerSession($this->guid));
+                           });
   }
 
 
+  /**
+   * @param $emprunteur Class_WebService_SIGB_Emprunteur
+   * @return array
+   */
   public function getEmpruntsOf($emprunteur) {
-    // prets pas en retard
-    $liste_prets_result = $this->search_client->EmprListerEntite(
-                                          EmprListerEntite::prets($this->guid));
-    $prets = $liste_prets_result->getEntites('Class_WebService_SIGB_Emprunt');
-
-    // prets en retard
-    $liste_prets_retard = $this->search_client->EmprListerEntite(
-                                  EmprListerEntite::prets_en_retard($this->guid));
-    $prets_retard = $liste_prets_retard->getEntites('Class_WebService_SIGB_Emprunt');
+    $prets = $this->_getEmpruntsOfType(EmprListerEntite::prets($this->guid));
+    $prets_retard = $this->_getEmpruntsOfType(EmprListerEntite::prets_en_retard($this->guid));
     foreach($prets_retard as $retard)
       $retard->setEnRetard(true);
 
@@ -202,10 +200,27 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
   }
 
 
+  protected function _getEmpruntsOfType($type) {
+    return $this->search_client->EmprListerEntite($type)
+                               ->getEntites('Class_WebService_SIGB_Emprunt');
+  }
+
+
+  protected function _withClientTryOrDo($action, $on_error=null) {
+    try {
+      return $action($this->search_client);
+    } catch (Exception $e) {
+      return null === $on_error
+        ? null
+        : $on_error($e);
+    }
+  }
+
+
   public function getReservationsOf($emprunteur) {
-    $reserv_result = $this->search_client->EmprListerEntite(
-                                  EmprListerEntite::reservations($this->guid));
-    return $reserv_result->getEntites('Class_WebService_SIGB_Opsys_Reservation');
+    return $this->search_client
+      ->EmprListerEntite(EmprListerEntite::reservations($this->guid))
+      ->getEntites('Class_WebService_SIGB_Opsys_Reservation');
   }
 
 
@@ -231,13 +246,18 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
    * @return Class_WebService_SIGB_Emprunteur
    */
   public function authentifierEmprunteur($user) {
-    $auth = new EmprAuthentifier($this->guid, $user->getLogin(), $user->getPassword());
+    $auth_result = $this
+      ->_withClientTryOrDo(function($client) use($user)
+                           {
+                             $auth = new EmprAuthentifier($this->guid,
+                                                          $user->getLogin(),
+                                                          $user->getPassword());
 
-    try {
-      $auth_result = $this->search_client->EmprAuthentifier($auth);
-    } catch (Exception $e) {
+                             return $client->EmprAuthentifier($auth);
+                           });
+
+    if (!$auth_result)
       return Class_WebService_SIGB_Emprunteur::nullInstance();
-    }
 
     $emprunteur = $auth_result
       ->createEmprunteur()
@@ -292,12 +312,12 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
   public function reserverExemplaire($user, $exemplaire, $code_annexe){
     $exemplaire_sigb = $this->getExemplaire($exemplaire->getIdOrigine(), $exemplaire->getCodeBarres());
     $emprunteur = $this->authentifierEmprunteur($user);
-    $reserv_result = $this->search_client
-      ->EmprReserver(
-                     new EmprReserver($this->guid,
+
+    return $this->search_client
+      ->EmprReserver(new EmprReserver($this->guid,
                                       $exemplaire_sigb->getId(),
-                                      $this->_is_reserver_retrait_bib_abonne ? '' : $code_annexe));
-    return $reserv_result->getReussite();
+                                      $this->_is_reserver_retrait_bib_abonne ? '' : $code_annexe))
+      ->getReussite();
   }
 
 
@@ -308,10 +328,10 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
    */
   public function supprimerReservation($user, $reservation_id){
     $emprunteur = $this->authentifierEmprunteur($user);
-    $supp_result = $this->search_client->EmprSupprResa(
-                                                new EmprSupprResa($this->guid,
-                                                                  $reservation_id));
-    return $supp_result->getReussite();
+
+    return $this->search_client
+      ->EmprSupprResa(new EmprSupprResa($this->guid, $reservation_id))
+      ->getReussite();
   }
 
 
@@ -322,21 +342,21 @@ class Class_WebService_SIGB_Opsys_Service extends Class_WebService_SIGB_Abstract
    */
   public function prolongerPret($user, $pret_id){
     $emprunteur = $this->authentifierEmprunteur($user);
-    $prolong_result = $this->search_client->EmprProlong(
-                                                 new EmprProlong($this->guid,
-                                                                 $pret_id));
-    return $prolong_result->getReussite();
+
+    return $this->search_client
+      ->EmprProlong(new EmprProlong($this->guid, $pret_id))
+      ->getReussite();
   }
 
 
-  public function getNotice($id){
-    try {
-      $notice_result = $this->search_client->RecupererNotice(
-        new RecupererNotice($this->guid, $id));
-      return $notice_result->createNotice();
-    } catch (Exception $e) {
-      //$this->_dumpSoapTrace();
-    }
+  public function getNotice($id) {
+    return $this
+      ->_withClientTryOrDo(function($client) use($id)
+                           {
+                             return $client
+                               ->RecupererNotice(new RecupererNotice($this->guid, $id))
+                               ->createNotice();
+                           });
   }
 
 
@@ -1861,6 +1881,3 @@ class ImportSousChamp {
   public $Etiquette; // string
   public $Description; // string
 }
-
-
-?>
\ No newline at end of file
diff --git a/library/ZendAfi/Controller/Action/Helper/Notify.php b/library/ZendAfi/Controller/Action/Helper/Notify.php
index 72286b0decfcca3cf03acc0d9e698e2928e6edf8..aaa7c48d937040b8d8700d097a16fa28819e0dbe 100644
--- a/library/ZendAfi/Controller/Action/Helper/Notify.php
+++ b/library/ZendAfi/Controller/Action/Helper/Notify.php
@@ -22,15 +22,13 @@
 class ZendAfi_Controller_Action_Helper_Notify extends Zend_Controller_Action_Helper_Abstract {
   protected $_options = [];
 
-  /**
-   * [[file:~/public_html/afi-opac3/library/Class/ScriptLoader.php::public%20function%20showNotifications()%20{][voir Class_ScriptLoader::showNotifications]]
-   */
   public function notify($message, $options=[]) {
     $this->getActionController()
          ->getHelper('flashMessenger')
          ->addNotification($message, array_merge($this->_options, $options));
   }
 
+
   public function direct($message, $options=[]) {
     $this->notify($message, $options);
   }
diff --git a/library/ZendAfi/View/Helper/Article/RenderAbstract.php b/library/ZendAfi/View/Helper/Article/RenderAbstract.php
index 41aa6c547544fb5280ca607e529fb3f8f24ed61f..a55dbd180b9d38474c60ee8459c5cfb04c351dc5 100644
--- a/library/ZendAfi/View/Helper/Article/RenderAbstract.php
+++ b/library/ZendAfi/View/Helper/Article/RenderAbstract.php
@@ -35,6 +35,7 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract
       . $this->_tag('footer',
                     $this->renderLieu($article)
                     .$this->renderICalLink($article)
+                    .$this->renderPrintLink($article)
                     .$this->renderReseauxSociaux($article)
                     .$this->renderAvis($article))
       . '<!-- RSPEAK_STOP -->';
@@ -134,6 +135,8 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract
 
   public function renderICalLink($article) { }
 
+  public function renderPrintLink($article) { }
+
 
   public function renderArticleInfo($article) {
     return $this->view->tagArticleInfo($article);
diff --git a/library/ZendAfi/View/Helper/Article/RenderFullContent.php b/library/ZendAfi/View/Helper/Article/RenderFullContent.php
index b27c4ea50cb951ef704e9c3640e26118d349afd3..bc1b4c35de0e9692b8495674818ad4645bc09674 100644
--- a/library/ZendAfi/View/Helper/Article/RenderFullContent.php
+++ b/library/ZendAfi/View/Helper/Article/RenderFullContent.php
@@ -44,5 +44,14 @@ class ZendAfi_View_Helper_Article_RenderFullContent extends ZendAfi_View_Helper_
       : '';
   }
 
+
+  public function renderPrintLink($article) {
+    return  $this->view->tag('div',
+                             $this->view->tagPrintLink((new Class_Entity())
+                                                       ->setModels([$article])
+                                                       ->setStrategy('Article_List')),
+                             ['class' => 'print']);
+
+  }
 }
 ?>
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/TagPrintLink.php b/library/ZendAfi/View/Helper/TagPrintLink.php
index 2be07218b3b0a46d4463a84121e28def48eee735..35c2c4e8c6cc6cd9bd56b8de1609b9004e1855a3 100644
--- a/library/ZendAfi/View/Helper/TagPrintLink.php
+++ b/library/ZendAfi/View/Helper/TagPrintLink.php
@@ -21,11 +21,16 @@
 
 
 class ZendAfi_View_Helper_TagPrintLink  extends ZendAfi_View_Helper_ModeleFusion_Link {
-
   public function tagPrintLink($instance) {
+    $link = ($instance->getStrategy() == 'Article_List')
+      ? $this->view->tagImg($this->view->skinImageUrl('print.png'),
+                            ['alt' => $this->_("Imprimer")])
+      : $this->_('Imprimer');
+
+
     $instance
       ->setAction('print')
-      ->setLink($this->_('Imprimer'))
+      ->setLink($link)
       ->setAttribs(['title' => $this->_('Aperçu avant impression'),
                     'target' => '_blank']);
 
diff --git a/public/opac/css/global.css b/public/opac/css/global.css
index 7ee0132279398a05aeeb198b38bfcbebc4ec9dc8..2a007904465f43c5ccda8a27186d1078713017e6 100644
--- a/public/opac/css/global.css
+++ b/public/opac/css/global.css
@@ -128,6 +128,11 @@ article footer .ical {
     margin-right: 3px;
 }
 
+article footer .print {
+    float: left;
+    margin-right: 3px;
+}
+
 dl.article_info {
     display:none;
 }
diff --git a/public/opac/images/print.png b/public/opac/images/print.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc452a1816fed3752b964440302aeb36d36e1a74
Binary files /dev/null and b/public/opac/images/print.png differ
diff --git a/tests/application/modules/opac/controllers/AuthControllerTest.php b/tests/application/modules/opac/controllers/AuthControllerTest.php
index 4d08c123c7ae72624472c7f15b491609d03f4755..6bfe3b45c9b95311322bef2adf2af2f68e047fe5 100644
--- a/tests/application/modules/opac/controllers/AuthControllerTest.php
+++ b/tests/application/modules/opac/controllers/AuthControllerTest.php
@@ -2745,3 +2745,61 @@ class AuthControllerPostLoginWithDifferentIdIntBibTest
     $this->assertEquals(56, $user->getIdIntBib());
   }
 }
+
+
+
+
+// see http://forge.afi-sa.fr/issues/100763
+class AuthControllerPostLoginOpsysWithExceptionTest extends AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+  public function setUp() {
+    parent::setUp();
+
+    ZendAfi_Auth::getInstance()->clearIdentity();
+
+    $this->fixture('Class_IntBib',
+                   ['id' => 44 ,
+                    'comm_sigb' => Class_IntBib::COM_OPSYS,
+                    'comm_params' => serialize(['url_serveur' => ''])]);
+
+    $service = $this->mock()
+                    ->whenCalled('providesAuthentication')->answers(true)
+                    ->whenCalled('isConnected')->answers(true)
+                    ->whenCalled('providesPagedLoans')->answers(false)
+
+                    ->whenCalled('getEmprunteur')
+                    ->answers(Class_WebService_SIGB_Emprunteur::newInstance(2233, 'harlock')->beValid())
+
+                    ->whenCalled('getEmpruntsOf')
+                    ->willDo(function()
+                             {
+                               throw new SoapFault('1512', 'Character reference "&#x1E" is an invalid XML character');
+                             });
+
+    Class_WebService_SIGB_Opsys::setService($service);
+
+    $this->postDispatch('/opac/auth/login',
+                        ['username' => 'harlock',
+                         'password' => 'arcadia4ever']);
+  }
+
+
+  public function tearDown() {
+    Class_WebService_SIGB_Opsys::setService(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldBeLogged() {
+    $this->assertNotNull(ZendAfi_Auth::getInstance()->getIdentity());
+  }
+
+
+  /** @test */
+  public function shouldNotifyErrorWithExceptionMessage() {
+    $this->assertFlashMessengerContentContains('Character reference');
+  }
+}
diff --git a/tests/application/modules/opac/controllers/CmsControllerPrintActionTest.php b/tests/application/modules/opac/controllers/CmsControllerPrintActionTest.php
index f80ae76fd35c2f24a0a4256185d842012407e47d..5bcc20c4ba7d0834bdf71bf556a0ed3579e60eae 100644
--- a/tests/application/modules/opac/controllers/CmsControllerPrintActionTest.php
+++ b/tests/application/modules/opac/controllers/CmsControllerPrintActionTest.php
@@ -69,12 +69,27 @@ class CmsControllerPrintActionArticleviewByDate extends AbstractControllerTestCa
 
 
   /** @test */
-  public function printLinkShouldBePresentInArticleview() {
+  public function printLinkShouldBePresentInArticleviewSelection() {
     $this->dispatch('cms/articleviewselection', true);
     $this->assertXPathContentContains('//a[contains(@href, "/cms/print/ids/2241%3B245/strategy/Article_List/modele_fusion/1")]', 'Imprimer', $this->_response->getBody());
   }
 
 
+  /** @test */
+  public function printLinkShouldBePresentInArticleview() {
+    $this->dispatch('/cms/articleview/id/2241', true);
+    $this->assertXPath('//a[contains(@href, "/cms/print/id/2241/ids/2241/strategy/Article_List/modele_fusion/1")]//img[@alt="Imprimer"]', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function printLinkShouldBeNotPresentInArticleviewWhenNoTemplate() {
+    Class_ModeleFusion::find(1)->delete();
+    $this->dispatch('/cms/articleview/id/2241', true);
+    $this->assertNotXPath('//a//img[@alt="Imprimer"]', $this->_response->getBody());
+  }
+
+
   /** @test */
   public function dispatchPrintCmsShouldContainsArticle() {
     $this->dispatch('/cms/print/ids/2241%3B245/strategy/Article_List/modele_fusion/1', true);
diff --git a/tests/application/modules/opac/controllers/ModulesControllerTest.php b/tests/application/modules/opac/controllers/ModulesControllerTest.php
index 161f14ce80f56ce8b6dddc03427b7adf24c78f35..7e7a8dcec8cf1330d3347daf43e02d55763a0d36 100644
--- a/tests/application/modules/opac/controllers/ModulesControllerTest.php
+++ b/tests/application/modules/opac/controllers/ModulesControllerTest.php
@@ -139,10 +139,10 @@ class ModulesControllerArteVodTest extends AbstractControllerTestCase {
                   ->addRight(Class_UserGroup::RIGHT_ACCES_ARTEVOD);
 
     Class_Users::getIdentity()->setUserGroups([$group]);
-
+    Class_AdminVar::set('NOM_DOMAINE', 'mymediatheque.com');
     $this->dispatch('/opac/modules/artevod');
     $this->assertXpathContentContains('//script',
-                                      'document.location.href="https://portal.mediatheque-numerique.com/sso_login?sso_id=afi&id=666&id_encrypted=ce04173447e59a7a3608f625b0348ccec5f4070c7c3f286b032cbe6374ec9f32&d=d8fb24f80a99bded9ed417591c5ac5cc1e123bcce10d65e1f6a83211ca18d3e8&return_url=https%3A%2F%2Fvod.mediatheque-numerique.com%2Fmediatheques%2Fuser";');
+                                      'document.location.href="https://portal.mediatheque-numerique.com/sso_login?sso_id=afi&id=666&id_encrypted=ce04173447e59a7a3608f625b0348ccec5f4070c7c3f286b032cbe6374ec9f32&d=d8fb24f80a99bded9ed417591c5ac5cc1e123bcce10d65e1f6a83211ca18d3e8&return_url=https%3A%2F%2Fvod.mediatheque-numerique.com%2Fmediatheques%2Fuser&referer=http%3A%2F%2Fmymediatheque.com";', $this->_response->getBody());
   }
 }
 
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
index 7b77a6cccd9e86594844f4ad9821a9d7b38d5a59..0069e1fe108e7e1ef06c9486de988662f4d43aa8 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
@@ -617,14 +617,14 @@ class NoticeAjaxControllerResumeAlbumTest extends AbstractControllerTestCase {
 
     Class_Album::getLoader()
       ->newInstanceWithId(2)
-      ->setDescription('Lucky Luke est un grand cow-boy');
+      ->setDescription('<p>Lucky Luke est un grand cow-boy de l&#39;Ouest</p>');
   }
 
 
   /** @test */
   public function contenuShouldContainsResume() {
     $this->dispatch('noticeajax/resume?id_notice=123');
-    $this->assertXPathContentContains('//div', 'Lucky Luke est un grand cow-boy');
+    $this->assertXPathContentContains('//div', 'Lucky Luke est un grand cow-boy de l\'Ouest', $this->_response->getBody());
   }
 
 
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 5cbf84e696048baca695874afe336f7a52c8aa09..6ac682bd40c9d93920715ca3b80b2dbcea4f4443 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -2972,6 +2972,24 @@ class UpgradeDB_380_Test extends UpgradeDBTestCase {
 
 
 class UpgradeDB_381_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    $this->silentQuery('ALTER TABLE newsletter_dispatch  modify body_text text');
+    $this->silentQuery('ALTER TABLE newsletter_dispatch  modify body_html text');
+  }
+
+  /**
+   * @test
+   */
+  public function fieldsForNewsletterBodyShouldBeLongText() {
+    $this->assertFieldType('newsletter_dispatch', 'body_text', 'longtext');
+    $this->assertFieldType('newsletter_dispatch', 'body_html', 'longtext');
+  }
+}
+
+
+
+
+class UpgradeDB_382_Test extends UpgradeDBTestCase {
   public function prepare() {
     $this
       ->silentQuery('ALTER TABLE external_agenda DROP COLUMN provider')
diff --git a/tests/library/Class/AlbumTest.php b/tests/library/Class/AlbumTest.php
index d4d45974fc5f860571ae069ca5dbf65ca275fb63..29b02598b5185d8ed916c82a1d53d82457628aee 100644
--- a/tests/library/Class/AlbumTest.php
+++ b/tests/library/Class/AlbumTest.php
@@ -762,4 +762,4 @@ class AlbumIsbnMarcTest extends ModelTestCase {
   public function isbnFromAlbumShouldBe9782081377523() {
     $this->assertEquals('9782081377523', $this->_album->getIsbn());
   }
-}
\ No newline at end of file
+}
diff --git a/tests/library/Class/ArteVodLinkTest.php b/tests/library/Class/ArteVodLinkTest.php
index 566fc8feaf5a8282fef7cedd0cd57ab57027aa99..14772e4a80dd04ef95b913d3fa4dd51e9ea2771c 100644
--- a/tests/library/Class/ArteVodLinkTest.php
+++ b/tests/library/Class/ArteVodLinkTest.php
@@ -57,7 +57,7 @@ class ArteVodLinkWithUserAndAlbumTest extends ModelTestCase {
       ->setDateFin('2023-09-12');
 
     Class_AdminVar::set('ARTE_VOD_SSO_KEY', $this->_sso_key);
-
+    Class_AdminVar::set('NOM_DOMAINE', 'mymediatheque.com');
     $this->_arte_vod_link = Class_ArteVodLink::forAlbumAndUser($entre_les_murs,
                                                                $this->_james_bond);
   }
@@ -81,7 +81,8 @@ class ArteVodLinkWithUserAndAlbumTest extends ModelTestCase {
                         .'&nom=Bond'
                         .'&email='.urlencode('jbond@007.fr')
                         .'&datout=2023-09-12'
-                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs'),
+                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs')
+                        .'&referer=http%3A%2F%2Fmymediatheque.com',
 
                         $this->_arte_vod_link->url());
   }
@@ -100,7 +101,8 @@ class ArteVodLinkWithUserAndAlbumTest extends ModelTestCase {
                         .'&id_encrypted='. hash('sha256', '45' . $this->_sso_key)
                         .'&d='. hash('sha256', date('dmY'). $this->_sso_key)
                         .'&datout=2023-09-12'
-                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs'),
+                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs')
+                        .'&referer=http%3A%2F%2Fmymediatheque.com',
                         $this->_arte_vod_link->url());
   }
 
@@ -120,7 +122,8 @@ class ArteVodLinkWithUserAndAlbumTest extends ModelTestCase {
                         .'&id=19'
                         .'&id_encrypted='. hash('sha256', '19' . $this->_sso_key)
                         .'&d='. hash('sha256', date('dmY') . $this->_sso_key)
-                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs'),
+                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs')
+                        .'&referer=http%3A%2F%2Fmymediatheque.com',
 
                         $this->_arte_vod_link->url());
   }
@@ -141,8 +144,8 @@ class ArteVodLinkWithUserAndAlbumTest extends ModelTestCase {
                         .'&id_encrypted='. hash('sha256', '45' . $this->_sso_key)
                         .'&d='. hash('sha256', date('dmY') . $this->_sso_key)
                         .'&dnaiss=1975-01-01'
-                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs'),
-
+                        .'&return_url=' . urlencode('http://www.mediatheque-numerique.com/films/entre-les-murs')
+                        .'&referer=http%3A%2F%2Fmymediatheque.com',
                         $this->_arte_vod_link->url());
   }
 }
diff --git a/tests/library/Class/Indexation/PseudoNoticeTest.php b/tests/library/Class/Indexation/PseudoNoticeTest.php
index 90112ae9396128f5adbf9a61e9b7ec9f7e215c73..acacd8d89a4a40c62cabe45acb02684d768cff9c 100644
--- a/tests/library/Class/Indexation/PseudoNoticeTest.php
+++ b/tests/library/Class/Indexation/PseudoNoticeTest.php
@@ -375,7 +375,7 @@ class Class_Indexation_PseudoNoticeArticleTest
                    ['id' => 1,
                     'titre' => 'My Article',
                     'contenu' => 'article is about...',
-                    'description' => 'This is a test',
+                    'description' => 'C&#39;est un m&eacute;gatest.',
                     'date_creation' => '2018-02-17 10:22:34',
                     'auteur' => $author,
                     'domaine_ids' => '17']);
@@ -403,8 +403,8 @@ class Class_Indexation_PseudoNoticeArticleTest
 
 
   /** @test */
-  public function firstRecordSummaryShouldBeThisIsATest() {
-    $this->assertEquals('This is a test', Class_Notice::find(1)->getResume());
+  public function firstRecordSummaryShouldBeCestUnMegaTest() {
+    $this->assertEquals('C\'est un mégatest.', Class_Notice::find(1)->getResume());
   }
 
 
@@ -846,4 +846,4 @@ class Class_Indexation_PseudoNoticeSacramentariumTest extends Class_Indexation_P
     $item = $this->_notice->getExemplaires()[0];
     $this->assertEquals(7, $item->getGenre());
   }
-}
\ No newline at end of file
+}
diff --git a/tests/library/Class/WebService/SIGB/OpsysServiceTest.php b/tests/library/Class/WebService/SIGB/OpsysServiceTest.php
index 2c2d383a7d57306417b1b9e2b6c94353c22b2bb0..7812c81afdc65015f4d44e5597e08beb9834bc76 100644
--- a/tests/library/Class/WebService/SIGB/OpsysServiceTest.php
+++ b/tests/library/Class/WebService/SIGB/OpsysServiceTest.php
@@ -182,7 +182,9 @@ class OpsysServiceTestAutoConnect extends ModelTestCase {
 }
 
 
-class Class_System_OpsysServiceFactoryTestUrls extends ModelTestCase {
+
+
+class OpsysServiceFactoryTestUrls extends ModelTestCase {
   public function setUp() {
     $this->factory = new Class_WebService_SIGB_Opsys_ServiceFactory();
   }
@@ -217,7 +219,7 @@ class Class_System_OpsysServiceFactoryTestUrls extends ModelTestCase {
 
 
 
-class Class_WebService_SIGB_OpsysServiceTestProxy extends ModelTestCase {
+class OpsysServiceTestProxy extends ModelTestCase {
 
   protected $_storm_default_to_volatile = true,
     $factory,
@@ -714,7 +716,7 @@ class OpsysServiceGetExemplaireFromCacheTestDisponibilite extends OpsysServiceWi
     }
 
     $recuperer_notice_res = $this->createMock('RecupererNoticeResponse',
-                                           array('createNotice'));
+                                              array('createNotice'));
     $recuperer_notice_res
       ->expects($this->once())
       ->method('createNotice')
@@ -1734,4 +1736,30 @@ class OpsysServiceDisconnectTest extends OpsysServiceWithSessionTestCase {
   public function serveurSessionNomServeurShouldBeInternet() {
     $this->assertEquals('INTERNET', $this->_fermer_session->Param->ListeServeurs[0]->NomServeur);
   }
-}
\ No newline at end of file
+}
+
+
+
+
+// see http://forge.afi-sa.fr/issues/100763
+class OpsysServiceGetEmpruntsOfWithInvalidXmlTest extends ModelTestCase {
+  /**
+   * @test
+   * @expectedException SoapFault
+   */
+  public function shouldThrowException() {
+    $client = $this->mock()
+                   ->whenCalled('OuvrirSession')
+                   ->answers($this->mock()
+                             ->whenCalled('getGUID')
+                             ->answers('1234'))
+
+                   ->whenCalled('EmprListerEntite')
+                   ->willDo(function()
+                            {
+                              throw new SoapFault('1512', 'Character reference "&#x1E" is an invalid XML character');
+                            });
+
+    (new Class_WebService_SIGB_Opsys_Service($client))->getEmpruntsOf($this->mock());
+  }
+}