diff --git a/VERSIONS b/VERSIONS
index 5391188822862b096345ae3051ade934ab868219..1487f6724c2dcee78303934ad1aeddf9e53eb652 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,14 +1,31 @@
+10/09/2019 - v8.0.24
+
+ - ticket #96451 : Mail : correction de l'encodage des mails dont les sujets dépassent 78 caractères
+ 
+ - ticket #95613 : Redmine : accès aux pièces jointes de la forge via Bokeh
+
+
+
+
 20/08/2019 - v8.0.23
 
  - ticket #94332 : WebService : amélioration de l'affichage des sites de retraits des reservations.
+
  - ticket #95190 : Administration : Export CSV des médias
+
  - ticket #68507 : WebService Koha : Correction du format de la date de naissance dans le formulaire de pré-inscription.
+
  - ticket #93504 : Bibliothèque numérique : pouvoir ouvrir/télécharger des images avec l'extension JPG
+
  - ticket #94528 : Arte VOD : Maintenance du connecteur 
+
  - ticket #93542 : Affichage des vignettes : prise en compte des champs unimarc spécifiés dans la configuration du profil
+
  - ticket #95885 : Version Mobile : Affichage des horaires d'ouvertures dans la fiche bibliothèque.
 
 
+
+
 29/07/2019 - v8.0.22
 
  - ticket #80976 : Page notice : connecté en tant qu'administrateur, l'outil "Inspector Gadget" permet de supprimer un exemplaire
diff --git a/application/modules/admin/controllers/RedmineController.php b/application/modules/admin/controllers/RedmineController.php
index a6dc51e07e5406fc71a4795bb55a0717e2a38597..5f69c41b0800933f077f4c822630ce85b1247f13 100644
--- a/application/modules/admin/controllers/RedmineController.php
+++ b/application/modules/admin/controllers/RedmineController.php
@@ -175,6 +175,21 @@ class Admin_RedmineController extends ZendAfi_Controller_Action {
   }
 
 
+  public function downloadFileAction() {
+    $library = Class_Bib::find($this->_getParam('id_lib', 0));
+    $service = new Class_WebService_Redmine($library);
+    $fileid = $this->_getParam('fileid');
+
+    if (!$content = $service->downloadFile($fileid)) {
+      $this->_helper->notify($this->_('Impossible de télécharger cette pièce jointe'));
+      $this->_redirectToReferer();
+      return;
+    }
+
+    $this->_helper->binaryDownload($content, $this->_getParam('filename'));
+  }
+
+
   public function uploadFileAction() {
     if (!$this->_request->isPost())
       return $this->_jsonError($this->_('La requête doit être de type POST'));
diff --git a/library/Class/WebService/Redmine.php b/library/Class/WebService/Redmine.php
index 89c41bab2b3117bea6b1facaf465967e778b1087..3ae9be3ffa6f2966dcbbb0ad1da739471aefe67a 100644
--- a/library/Class/WebService/Redmine.php
+++ b/library/Class/WebService/Redmine.php
@@ -20,7 +20,7 @@
  */
 
 class Class_WebService_Redmine extends Class_WebService_Abstract {
-   use Trait_Translator;
+  use Trait_Translator;
 
    const CUSTOM_PRIORITY_ID = 5;
    const CUSTOM_MODULE_ID = 37;
@@ -381,8 +381,12 @@ class Class_WebService_Redmine extends Class_WebService_Abstract {
 
 
   public function uploadFile($json) {
-    return $this->_getAttachmentApi()
-                ->upload($json);
+    return $this->_getAttachmentApi()->upload($json);
+  }
+
+
+  public function downloadFile($fileid) {
+    return $this->_getAttachmentApi()->download($fileid);
   }
 
 
diff --git a/library/Class/WebService/Redmine/Issue.php b/library/Class/WebService/Redmine/Issue.php
index e3bccad28f6e0c8973cab5ad2ee50175a60d4622..bfb49055070fe2fed501b7b57d3cf35c40f63e1f 100644
--- a/library/Class/WebService/Redmine/Issue.php
+++ b/library/Class/WebService/Redmine/Issue.php
@@ -139,10 +139,12 @@ class Class_WebService_Redmine_Issue extends Class_Entity {
       return $view->tag('del',
                         $this->_('Pièce jointe : "%s"', $attachment->getold_value()));
 
-    $show_url = sprintf('%s/attachments/download/%s/%s',
-                   Class_AdminVar::get('REDMINE_SERVER_URL'),
-                   $attachment->getname(),
-                   $attachment->getnew_value());
+    $show_url = Class_Url::absolute(['module' => 'admin',
+                                     'controller' => 'redmine',
+                                     'action' => 'download-file',
+                                     'id_lib' => $this->getLibrary()->getId(),
+                                     'fileid' => $attachment->getname(),
+                                     'filename' => $attachment->getnew_value()],null,true);
 
     return $view->tagAnchor($show_url,
                             $this->_('Pièce jointe : "%s"', $attachment->getnew_value()), ['target' => '_blank']);
diff --git a/library/startup.php b/library/startup.php
index d4a4df524472ebbc1a2cc160ba668c1f19d8bc57..ba43a06811d9bb522d6a083226ccc0f27d598174 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -81,7 +81,7 @@ class Bokeh_Engine {
 
   function setupConstants() {
     defineConstant('BOKEH_MAJOR_VERSION','8.0');
-    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.23');
+    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.24');
 
     defineConstant('BOKEH_REMOTE_FILES', 'http://git.afi-sa.fr/afi/opacce/');
 
diff --git a/library/storm b/library/storm
index 94e39cb7d1d85f8969127523c0624474e6ad6ed9..03db1d52ff87846f60a4c42085811ef14b880ab9 160000
--- a/library/storm
+++ b/library/storm
@@ -1 +1 @@
-Subproject commit 94e39cb7d1d85f8969127523c0624474e6ad6ed9
+Subproject commit 03db1d52ff87846f60a4c42085811ef14b880ab9
diff --git a/tests/application/modules/admin/controllers/CmsControllerTest.php b/tests/application/modules/admin/controllers/CmsControllerTest.php
index 6e49b08209ca3b9384b218bcd07840f7a35b0d3c..fb918f2b0789744fd04b1bc8f84bc0ccbd7dce9b 100644
--- a/tests/application/modules/admin/controllers/CmsControllerTest.php
+++ b/tests/application/modules/admin/controllers/CmsControllerTest.php
@@ -1711,7 +1711,7 @@ class CmsControllerNewsAddActionPostWithWorkflowTest
   /** @test */
   public function sentMailToAuthorWhenNewsIsValidatedShouldContainsDefaultSubject() {
     $this->postArticleValidated();
-    $this->assertEquals('=?utf8?Q?[Bokeh] Validation de l\'article Katsuhiro Otomo en dédicace !?',
+    $this->assertEquals('=?utf8?Q?[Bokeh] Validation de l\'article Katsuhiro Otomo ? =?utf8?Q?en dédicace !?',
                         quoted_printable_decode($this->mock_transport->getSentMails()[0]->getSubject()));
   }
 
@@ -1809,7 +1809,7 @@ class CmsControllerNewsAddActionPostWithWorkflowTest
   /** @test */
   public function sentMailToAdminWhenValidationPendingShouldContainsDefaultTitle() {
     $this->postArticleAValider();
-    $this->assertContains('[Bokeh] Validation d\'article en attente: Katsuhiro Otomo en dédicace !',
+    $this->assertContains('=?utf8?Q?[Bokeh] Validation d\'article en attente: ? =?utf8?Q?Katsuhiro Otomo en dédicace !?',
                           quoted_printable_decode($this->mock_transport->getSentMails()[0]->getSubject()));
   }
 
@@ -1978,7 +1978,7 @@ class CmsControllerWorkflowArticleRefusedTest extends CmsControllerWorkflowTestC
   /** @test */
   public function sentMailToUserWhenRefusedShouldContainsDefaultTitle() {
     $this->postArticleRefuser();
-    $this->assertContains('[Bokeh] Refus de l\'article Katsuhiro Otomo en dédicace !',
+    $this->assertContains('=?utf8?Q?[Bokeh] Refus de l\'article Katsuhiro Otomo en ? =?utf8?Q?dédicace !?',
                           quoted_printable_decode($this->mock_transport->getSentMails()[0]->getSubject()));
   }
 
diff --git a/tests/application/modules/admin/controllers/RedmineControllerTest.php b/tests/application/modules/admin/controllers/RedmineControllerTest.php
index 3b84848c1d754d888c1d66658c3e6a7fecc1e992..a94fef7ab9b30f8dcc8630b702034325bcc693f8 100644
--- a/tests/application/modules/admin/controllers/RedmineControllerTest.php
+++ b/tests/application/modules/admin/controllers/RedmineControllerTest.php
@@ -52,6 +52,7 @@ abstract class Admin_RedmineControllerTestCase extends Admin_AbstractControllerT
 
 
 
+
 class Admin_RedmineControllerTestActionWithNoBibTest extends Admin_RedmineControllerTestCase {
   public function setUp() {
     parent::setUp();
@@ -67,6 +68,7 @@ class Admin_RedmineControllerTestActionWithNoBibTest extends Admin_RedmineContro
 
 
 
+
 abstract class Admin_RedmineControllerWithAnnecyLibraryTestCase extends Admin_RedmineControllerTestCase {
 
   public function setUp() {
@@ -560,6 +562,13 @@ class Admin_RedmineControllerEditIssue34247Test extends Admin_RedmineControllerF
   public function historyShouldBePresent() {
     $this->assertXPathContentContains('//legend', 'Historique');
   }
+
+
+  /** @test */
+  public function attachmentShouldLinkToDownloadFile() {
+    $this->assertXPathContentContains('//a[contains(@href,"download-file/id_lib/1/fileid/456789/filename/proof.jpg")]', 'proof.jpg', $this->_response->getBody());
+  }
+
 }
 
 
@@ -863,3 +872,74 @@ class Admin_RedmineControllerPostEditIssue34247WithAttachmentTest extends Admin_
     $this->assertRedirect();
   }
 }
+
+
+
+class Admin_RedmineControllerTestActionDownloadAttachmentTest
+  extends Admin_RedmineControllerWithAnnecyLibraryTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $redmine_api = $this->mock()
+                        ->whenCalled('download')
+                        ->with(123)
+                        ->answers('toto');
+
+    $redmine_client = $this->mock()
+                           ->whenCalled('api')
+                           ->answers($redmine_api);
+
+    Class_WebService_Redmine::setClient($redmine_client);
+
+    $this->dispatch('/admin/redmine/download-file/id_lib/1/fileid/123/filename/test.txt', true);
+  }
+
+
+  /** @test */
+  public function responseBodyShouldBeToto() {
+    $this->assertEquals('toto', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function contentTypeShouldBeApplicationOctetStream() {
+    $this->assertEquals('application/octet-stream; name="test.txt"',
+                        $this->_response->getHeaders()[0]['value']);
+  }
+}
+
+
+
+class Admin_RedmineControllerTestActionDownloadEmptyAttachmentTest
+  extends Admin_RedmineControllerWithAnnecyLibraryTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $redmine_api = $this->mock()
+                        ->whenCalled('download')
+                        ->with(123)
+                        ->answers('');
+
+    $redmine_client = $this->mock()
+                           ->whenCalled('api')
+                           ->answers($redmine_api);
+
+    Class_WebService_Redmine::setClient($redmine_client);
+
+    $this->dispatch('/admin/redmine/download-file/id_lib/1/fileid/123/filename/test.txt', true);
+  }
+
+
+  /** @test */
+  public function shouldNotifyError() {
+    $this->assertFlashMessengerContentContains('Impossible de télécharger cette pièce jointe');
+  }
+
+
+  /** @test */
+  public function shouldRedirect() {
+    $this->assertRedirect();
+  }
+}
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/AbonneControllerActivitiesTest.php b/tests/application/modules/opac/controllers/AbonneControllerActivitiesTest.php
index bb93289eeed201ce2b5006b8632ea0e0ed19c8d8..32ac1fb1c79c1572f2602c14fe3ee775429419fa 100644
--- a/tests/application/modules/opac/controllers/AbonneControllerActivitiesTest.php
+++ b/tests/application/modules/opac/controllers/AbonneControllerActivitiesTest.php
@@ -706,8 +706,9 @@ class AbonneControllerActivitiesAmadouInscritSessionFebruaryJavaOpenTest extends
 
   /** @test */
   public function firstMailSubjectShouldBeRegistrationConfirmation() {
-    $this->assertEquals('=?utf8?Q?Confirmation d\'inscription à l\'activité "Learn Java"?',
-                        quoted_printable_decode($this->_mails[0]->getSubject()));
+    $this->assertEquals('=?utf8?Q?Confirmation=20d\'inscription=20=C3=A0=20l\'activit=C3=A9=20?=
+ =?utf8?Q?"Learn=20Java"?=',
+                        $this->_mails[0]->getSubject());
   }
 
 
@@ -943,7 +944,7 @@ class AbonneControllerActivitiesAmadouDesinscritSessionJuilletPythonTest
 
   /** @test */
   public function firstMailSubjectShouldContainsUnregistrationConfirmation() {
-    $this->assertContains('Confirmation de désinscription à l\'activité "Learn Python"',
+    $this->assertContains('=?utf8?Q?Confirmation de désinscription à ? =?utf8?Q?l\'activité "Learn Python"?',
                           quoted_printable_decode($this->_mails[0]->getSubject()));
   }
 
diff --git a/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php b/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
index 61ace025e99c1a929eb30ad95c41a8a7dcd64223..3f85abce911dc5993ebf5b444287c4db4be0cff7 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerReservationTest.php
@@ -1229,7 +1229,7 @@ class RechercheControllerReservationPickupAjaxAndNotifyByMailEnabledTest
 
   /** @test */
   public function mailSubjectShouldContainsJulianMonsieurSurExemplaire12341() {
-    $this->assertContains("Julian Monsieur sur l'exemplaire 12341",
+    $this->assertContains("Julian Monsieur ? =?utf8?Q?sur l'exemplaire 12341",
                           quoted_printable_decode(current($this->_sent_mails)->getSubject()));
   }
 
diff --git a/tests/library/ZendAfi/MailTest.php b/tests/library/ZendAfi/MailTest.php
index c40c6d923758855dd2c63e57eb3d2970b3a05250..55ad4dd4a22743040b07ae234359c35b76ff568b 100644
--- a/tests/library/ZendAfi/MailTest.php
+++ b/tests/library/ZendAfi/MailTest.php
@@ -50,4 +50,23 @@ class ZendAfi_MailTest extends ModelTestCase {
 
     $this->assertNotEmpty($mail->getRecipients());
   }
+
+
+  /** @test */
+  public function sujectWithUnicodeCharsShouldBeEncoded() {
+    $mock_transport = new MockMailTransport();
+    Zend_Mail::setDefaultTransport($mock_transport);
+
+    $mail = (new ZendAfi_Mail())
+      ->setFrom('tester@bokeh.afibre.fr')
+      ->addTo('patron@here.fr')
+      ->setSubject('Réseau des médiathèques Médi@Val : l\'actu de la rentrée')
+      ->setBodyHtml('<h1><a href="https://www.reseaumediaval.fr/index/index/id_profil/1" target="_top">&agrave; la m&eacute;diath&egrave;que de Marcy l&#39;Etoile</a></h1>');
+    $mail->send();
+
+    $sent_mail = $mock_transport->getSentMails()[0];
+    $this->assertEquals('=?utf8?Q?R=C3=A9seau=20des=20m=C3=A9diath=C3=A8ques=20M=C3=A9di@Val=20?=
+ =?utf8?Q?:=20l\'actu=20de=20la=20rentr=C3=A9e?=', $sent_mail->getSubject());
+
+  }
 }
\ No newline at end of file