From c8d65f3ef8ea1b27c07642cbf80918d290eb0f13 Mon Sep 17 00:00:00 2001
From: gloas <gloas@afi-sa.fr>
Date: Wed, 16 Jan 2019 17:33:04 +0100
Subject: [PATCH] dev #84280 : fix waiting files in cosmogramme

---
 VERSIONS_WIP/84280                            |   1 +
 .../controllers/IntegrationController.php     | 106 +++++++++++----
 .../scripts/integration/waiting-files.phtml   |   8 ++
 cosmogramme/cosmozend/index.php               |   5 +-
 .../tests/CosmoControllerTestCase.php         |   2 +-
 .../controllers/IntegrationControllerTest.php | 116 ++++++++++++++++
 cosmogramme/php/_menu.php                     |   2 +-
 cosmogramme/php/integre_fichiers_attente.php  | 124 ------------------
 library/Class/FileManager.php                 |   6 +
 library/Class/FileManager/FileSystem.php      |  10 +-
 .../TableDescription/CosmoWaitingFiles.php    | 112 ++++++++++++++++
 .../Action/Helper/FileManagerDownload.php     |  51 +++++++
 12 files changed, 387 insertions(+), 156 deletions(-)
 create mode 100644 VERSIONS_WIP/84280
 create mode 100644 cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/waiting-files.phtml
 delete mode 100644 cosmogramme/php/integre_fichiers_attente.php
 create mode 100644 library/Class/TableDescription/CosmoWaitingFiles.php
 create mode 100644 library/ZendAfi/Controller/Action/Helper/FileManagerDownload.php

diff --git a/VERSIONS_WIP/84280 b/VERSIONS_WIP/84280
new file mode 100644
index 00000000000..ebf0032507a
--- /dev/null
+++ b/VERSIONS_WIP/84280
@@ -0,0 +1 @@
+ - ticket #84280 : Cosmogramme : correction de la détection des fichiers en attente composés avec une date
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php b/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
index 318457c8469..d4ee9e7e262 100644
--- a/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
+++ b/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
@@ -20,43 +20,43 @@
  */
 
 
-class Cosmo_IntegrationController extends Zend_Controller_Action{
-  use Trait_Translator;
+class Cosmo_IntegrationController extends ZendAfi_Controller_Action{
 
-	public function preDispatch() {
-		$this->cosmoPath = $this->view->cosmoPath = new CosmoPaths();
-	}
+  public function preDispatch() {
+    parent::preDispatch();
+    $this->cosmoPath = $this->view->cosmoPath = new CosmoPaths();
+  }
 
 
-	public function controlAction() {
-		if (!$bib = Class_IntBib::find($this->_getParam('id', 0)))
-			$bib = Class_IntBib::newInstance(['nom_court' => 'Bibliothèque inconnue']);
+  public function controlAction() {
+    if (!$bib = Class_IntBib::find($this->_getParam('id', 0)))
+      $bib = Class_IntBib::newInstance(['nom_court' => 'Bibliothèque inconnue']);
 
-		$this->view->bib = $bib;
-		$this->view->integrations = $bib->getIntegrations();
-	}
+    $this->view->bib = $bib;
+    $this->view->integrations = $bib->getIntegrations();
+  }
 
 
-	public function testAction() {
+  public function testAction() {
     if ($layout = Zend_Layout::getMvcInstance())
       $layout->disableLayout();
 
-		if (!($type = $this->_getParam('type'))
-				|| !($options = $this->_getParam('options'))) {
-			$this->view->response = 'Paramètres insuffisants';
-			return;
-		}
+    if (!($type = $this->_getParam('type'))
+        || !($options = $this->_getParam('options'))) {
+      $this->view->response = 'Paramètres insuffisants';
+      return;
+    }
 
-		if (!$service_name = Class_IntBib::detectService($type)) {
-			$this->view->response = 'Service de type ' . $type . ' inconnu';
-			return;
-		}
+    if (!$service_name = Class_IntBib::detectService($type)) {
+      $this->view->response = 'Service de type ' . $type . ' inconnu';
+      return;
+    }
 
-		$service = call_user_func([$service_name, 'getService'], $options);
-		$this->view->response = $service->test();
-		if (!$this->view->response)
-			$this->view->response = 'Impossible de contacter le service';
-	}
+    $service = call_user_func([$service_name, 'getService'], $options);
+    $this->view->response = $service->test();
+    if (!$this->view->response)
+      $this->view->response = 'Impossible de contacter le service';
+  }
 
 
   public function generateAction() {
@@ -78,4 +78,58 @@ class Cosmo_IntegrationController extends Zend_Controller_Action{
 
     $this->render('generate-log');
   }
+
+
+  public function waitingFilesAction() {
+    $this->view->titre = $this->_('Fichiers en attente d\'intégration');
+
+    Class_FileManager::beOpenBar();
+
+    $files = [];
+    foreach (Class_FileManager::directories(Class_CosmoVar::get('ftp_path')) as $directory)
+      foreach(Class_FileManager::files($directory->getPath()) as $file)
+        $files[] = $file;
+
+    $this->view->files = $files;
+  }
+
+
+  public function waitingFilesDeleteAction() {
+    if (!$item_path = $this->_getParam('id')) {
+      $this->_helper->notify($this->_('La suppression n\'a pas fonctionné car il n\'y a pas de chemin à supprimer.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    Class_FileManager::beOpenBar();
+
+    if(!$item = Class_FileManager::find($item_path)) {
+      $this->_helper->notify($this->_('La suppression n\'a pas fonctionné car le chemin est invalide.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    $success = Class_FileManager::delete($item);
+    $message = $success
+      ? $this->_('"%s" supprimé.', $item->getPath())
+      : $this->_('Impossible de supprimer "%s".', $item->getPath());
+
+    $this->_helper->notify($message);
+    return $this->_redirectClose($this->_getReferer());
+  }
+
+
+  public function waitingFilesDownloadAction() {
+    if (!$item_path = $this->_getParam('id')) {
+      $this->_helper->notify($this->_('Le téléchargement n\'a pas fonctionné car il n\'y a pas de chemin à télécharger.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    Class_FileManager::beOpenBar();
+
+    if(!$item = Class_FileManager::find($item_path)) {
+      $this->_helper->notify($this->_('Le téléchargement n\'a pas fonctionné car le chemin est invalide.'));
+      return $this->_redirectClose($this->_getReferer());
+    }
+
+    $this->_helper->fileManagerDownload($item);
+  }
 }
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/waiting-files.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/waiting-files.phtml
new file mode 100644
index 00000000000..d06e88f744b
--- /dev/null
+++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/waiting-files.phtml
@@ -0,0 +1,8 @@
+<?php
+echo
+  $this->tag('h1', $this->titre)
+  . ($this->files
+     ? $this->renderTable((new Class_TableDescription_CosmoWaitingFiles('waiting-files'))
+                          ->setView($this)
+                          ->read(), $this->files)
+     : $this->tagNotice($this->_('Il n\'y a aucun fichier en attente d\'intégration')));
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/index.php b/cosmogramme/cosmozend/index.php
index 410afdb6544..e6f94c2a1a6 100644
--- a/cosmogramme/cosmozend/index.php
+++ b/cosmogramme/cosmozend/index.php
@@ -32,9 +32,8 @@ set_include_path( $cosmozendPath
 . PATH_SEPARATOR . $cosmozendPath . '/application'
 . PATH_SEPARATOR . get_include_path());
 
-// pretending I am under Bokeh's root
-chdir('../..');
-
+// change dir to Bokeh's root
+chdir($cosmo_path->getBasePath());
 
 Zend_Session::start();
 
diff --git a/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php b/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
index 788b786d8aa..7972e185b71 100644
--- a/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
+++ b/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
@@ -66,7 +66,7 @@ abstract class CosmoControllerTestCase extends Zend_Test_PHPUnit_ControllerTestC
   }
 
 
-  public function dispatch($url = null, $throw_exceptions = false) {
+  public function dispatch($url = null, $throw_exceptions = true) {
     // redirector should not exit
     $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
     $redirector->setExit(false);
diff --git a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
index 6ebfd9c32d6..8642e921a87 100644
--- a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
+++ b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
@@ -595,4 +595,120 @@ class Cosmo_IntegrationControllerGenerateActionNanookPostTest
   public function shouldSayFinished() {
     $this->assertXPathContentContains('//h2', 'Traitement terminé');
   }
+}
+
+
+
+abstract class Cosmo_IntegrationControllerWaitingFilesTestCase extends CosmoControllerTestCase {
+  protected $_tot;
+
+  public function setUp() {
+    parent::setUp();
+    Class_CosmoVar::set('ftp_path', '/ccpl34');
+
+    $this->fixture('Class_IntMajAuto',
+                   ['id' => 43,
+                    'nom_fichier' => 'lunel/[DATE]_records_tot.mrc']);
+
+    $lunel = $this->mock()->whenCalled('getPath')
+                  ->answers('/ccpl34/lunel')
+                  ->whenCalled('getName')->answers('Lunel');
+
+    $this->_tot = $this->mock()
+                       ->whenCalled('getParent')->answers($lunel)
+                       ->whenCalled('isWritable')->answers(true)
+                       ->whenCalled('getName')->answers('20190116220001_records_tot.mrc')
+                       ->whenCalled('getMTime')->answers('2019-01-13 22:88:77')
+                       ->whenCalled('getSize')->answers('42 Ko')
+                       ->whenCalled('getFileSize')->answers(43986)
+                       ->whenCalled('getId')->answers('/ccpl34/lunel/20190116220001_records_tot.mrc')
+                       ->whenCalled('getModels')->answers([])
+                       ->whenCalled('isDir')->answers(false)
+                       ->whenCalled('getPath')->answers('/ccpl34/lunel/')
+                       ->whenCalled('readfile')->answers(true)
+      ;
+
+    $file_system = $this
+      ->mock()
+      ->whenCalled('directoriesAt')->with('/ccpl34')->answers([$lunel])
+
+      ->whenCalled('filesAt')->with('/ccpl34/lunel')->answers([$this->_tot])
+
+      ->whenCalled('directoryAt')->with('ccpl34/lunel/20190116220001_records_tot.mrc')
+      ->answers(null)
+
+      ->whenCalled('fileAt')->with('ccpl34/lunel/20190116220001_records_tot.mrc')
+      ->answers($this->_tot)
+
+      ->whenCalled('delete')->with('/ccpl34/lunel/20190116220001_records_tot.mrc')
+      ->answers(true)
+      ;
+
+    Class_FileManager::setFileSystem($file_system);
+
+    Class_AdminVar::set('NOM_DOMAINE', 'http://localhost');
+  }
+}
+
+
+
+class Cosmo_IntegrationControllerWaitingFilesActionTest
+  extends Cosmo_IntegrationControllerWaitingFilesTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/cosmo/integration/waiting-files');
+  }
+
+
+  /** @test */
+  public function titleShouldBeFichiersEnAttenteDIntegration() {
+    $this->assertXPathContentContains('//h1', 'Fichiers en attente d\'intégration');
+  }
+
+
+  /** @test */
+  public function tableShouldContainsLunelDirectory() {
+    $this->assertXPathContentContains('//td', 'Lunel');
+  }
+
+
+  /** @test */
+  public function linkToDeleteRecordTotShouldBePresent() {
+    $this->assertXPath('//a[@href = "/cosmo/integration/waiting-files-delete?id=%2Fccpl34%2Flunel%2F20190116220001_records_tot.mrc"]');
+  }
+
+
+  /** @test */
+  public function linkToDownloadRecordTotShouldBePresent() {
+    $this->assertXPath('//a[@href = "/cosmo/integration/waiting-files-download?id=%2Fccpl34%2Flunel%2F20190116220001_records_tot.mrc"]');
+  }
+}
+
+
+
+class Cosmo_IntegrationControllerWaitingFilesDeleteTest
+  extends Cosmo_IntegrationControllerWaitingFilesTestCase {
+
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/cosmo/integration/waiting-files-delete?id=%2Fccpl34%2Flunel%2F20190116220001_records_tot.mrc');
+  }
+
+
+  /** @test */
+  public function recordsTotShouldBeDeleted() {
+    $this->assertTrue(Class_FileManager::getFileSystem()->methodHasBeenCalled('delete'));
+  }
+}
+
+
+
+class Cosmo_IntegrationControllerWaitingFilesDownloadTest
+  extends Cosmo_IntegrationControllerWaitingFilesTestCase {
+
+  /** @test */
+  public function totShouldBeRead() {
+    $this->dispatch('/cosmo/integration/waiting-files-download?id=%2Fccpl34%2Flunel%2F20190116220001_records_tot.mrc');
+    $this->assertTrue($this->_tot->methodHasBeenCalled('readfile'));
+  }
 }
\ No newline at end of file
diff --git a/cosmogramme/php/_menu.php b/cosmogramme/php/_menu.php
index 5cb66594aca..b6c05c6a316 100644
--- a/cosmogramme/php/_menu.php
+++ b/cosmogramme/php/_menu.php
@@ -70,7 +70,7 @@ else
 	ligneMenu("Contrôle des intégrations","integre_controle_integrations.php");
 	ligneMenu("Journal des intégrations","../cosmozend/cosmo/logs");
 	ligneMenu("Traitements en cours","integre_traitements_attente.php");
-	ligneMenu("Fichiers en attente","integre_fichiers_attente.php");
+	ligneMenu("Fichiers en attente","../cosmozend/cosmo/integration/waiting-files");
 	ligneMenu("Lancer les traitements","integre_traite_main.php",true);
 	?>
 	<div class="menu_section">Analyse des données</div>
diff --git a/cosmogramme/php/integre_fichiers_attente.php b/cosmogramme/php/integre_fichiers_attente.php
deleted file mode 100644
index 25a53e325eb..00000000000
--- a/cosmogramme/php/integre_fichiers_attente.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?PHP
-/**
- * Copyright (c) 2012, 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
- */
-////////////////////////////////////////////////////////////////////////////////////////////
-// AFFICHAGE DES FICHIERS EN ATTENTE
-////////////////////////////////////////////////////////////////////////////////////////////
-include("_init_frame.php");
-require_once("classe_maj_auto.php");
-$cls_maj_auto=new maj_auto();
-
-// ----------------------------------------------------------------
-// SUPPRESSION
-// ----------------------------------------------------------------
-if($_REQUEST["action"] == "SUPPRIMER")
-{
-	unlink($_REQUEST["fichier"]);
-}
-
-// ----------------------------------------------------------------
-// LISTE
-// ----------------------------------------------------------------
-print('<h1>Fichiers en attente d\'intégration</h1>');
-
-// Parser le repertoire ftp
-$path=getVariable("ftp_path");
-if(strRight($path,1) != "/") $path.="/";
-$dir = opendir($path) or AfficherErreur("Impossible d'ouvrir le dossier des transferts (variable ftp_path) : " .$path);
-while (($file = readdir($dir)) !== false)
-{
-	if(is_dir($path.$file) and $file !="test" and substr($file,0,1) != ".") $dossier[]["nom"]=$file;
-}
-closedir($dir);
-if(!$dossier) AfficherErreur("Il n'y a aucun sous-dossier dans le dossier des transferts.");
-
-$nb_fic=0;
-for($i=0; $i < count($dossier); $i++)
-{
-	$dir = opendir($path.$dossier[$i]["nom"]);
-	if($dir == false) continue;
-	while (($file = readdir($dir)) !== false)
-	{
-		if(is_file($path.$dossier[$i]["nom"]."/".$file)) {$dossier[$i]["fichiers"][]=$file; $nb_fic++; }
-		elseif(substr($file,0,4)=="site")
-		{
-			// Sous repertoires pergame
-			$dir_pergame = opendir($path.$dossier[$i]["nom"]."/".$file);
-			while (($file_pergame = readdir($dir_pergame)) !== false)
-			{
-				if($file_pergame=='test.txt') continue;
-				if(is_file($path.$dossier[$i]["nom"]."/".$file."/".$file_pergame)) {$dossier[$i]["fichiers"][]=$file."/".$file_pergame; $nb_fic++; }
-			}
-			closedir($dir_pergame);
-		}
-	}
-	closedir($dir);
-}
-if(!$nb_fic) quit("Il n'y a aucun fichier en attente d'intégration");
-else echo BR.'&raquo;&nbsp;NB : Vous pouver télécharger un fichier par un click droit sur son nom, puis enregistrer sous'.BR.BR;
-
-// Affichage
-print('<div class="liste"><table>');
-print('<tr>');
-print('<th>Dossier</th>');
-print('<th>Fichier</th>');
-print('<th>Transféré le</th>');
-print('<th>Taille</th>');
-print('<th>Statut</th>');
-print('<th>Suppr.</th>');
-print('</tr>');
-
-for($i=0; $i < count($dossier); $i++)
-{
-	if(! count($dossier[$i]["fichiers"])) continue;
-	$fic=0;
-	foreach($dossier[$i]["fichiers"] as $file)
-	{
-		$fichier=$path.$dossier[$i]["nom"]."/".$file;
-		$infos=stat($fichier);
-		$taille=number_format(($infos["size"] / 1024),0, ',', ' ')." ko";
-		$date=date("d-m-Y", $infos["mtime"]);
-		if($fic > 0) $d="&nbsp;"; else $d=$dossier[$i]["nom"];
-		$suppr=rendUrlImg("suppression.gif", "integre_fichiers_attente.php","action=SUPPRIMER&fichier=".$fichier,"Supprimer ce fichier");
-		$controle=$sql->fetchOne("Select count(*) from int_maj_auto where nom_fichier='". $dossier[$i]["nom"]."/".$file ."'");
-		if($controle) $statut = '<font color="darkgreen">Programmé</font>'; else $statut = '<font color="red">non programmé</font>';
-
-		print('<tr>');
-		print('<td>'.$d.'</td>');
-		print('<td>'.'<a href="'.URL_BASE.getVariable("ftp_path").$dossier[$i]["nom"].'/'.$file.'">'.$file.'</a></td>');
-		print('<td align="center">'.$date	.'</td>');
-		print('<td align="right">'.$taille.'</td>');
-		print('<td>'.$statut.'</td>');
-		print('<td align="center">'.$suppr.'</td>');
-		print('</tr>');
-		$fic++;
-	}
-}
-print('</div></table>');
-print('</body></html>');
-
-function quit($msg)
-{
-	if($msg) print(BR.BR.'<h3 style="margin-left:30px">'.$msg.'</h3>');
-	print('</body></html>');
-	exit;
-}
-
-?>
\ No newline at end of file
diff --git a/library/Class/FileManager.php b/library/Class/FileManager.php
index 7cbb50b2c84..89de300e1df 100644
--- a/library/Class/FileManager.php
+++ b/library/Class/FileManager.php
@@ -37,6 +37,7 @@ class Class_FileManager extends Class_Entity {
                          'BrowserParam' => '',
                          'MTime' => '',
                          'Size' => '',
+                         'FileSize' => 0,
                          'Dir' => '',
                          'Dimensions' => '',
                          'Type' => '',
@@ -571,4 +572,9 @@ class Class_FileManager extends Class_Entity {
   public function getContent() {
     return static::getFileSystem()->getContent($this->getRealpath());
   }
+
+
+  public function readfile() {
+    return static::getFileSystem()->readfile($this->getRealpath());
+  }
 }
\ No newline at end of file
diff --git a/library/Class/FileManager/FileSystem.php b/library/Class/FileManager/FileSystem.php
index 7289df022db..341fc76d371 100644
--- a/library/Class/FileManager/FileSystem.php
+++ b/library/Class/FileManager/FileSystem.php
@@ -289,6 +289,7 @@ class Class_FileManager_FileSystem {
 
     $basename = $info['basename'];
     $path = implode('/', array_filter([$dirname, $basename]));
+    $size = filesize($path);
 
     return (new Class_FileManager)
       ->setId($path)
@@ -301,7 +302,8 @@ class Class_FileManager_FileSystem {
       ->setWritable($this->_isWritable($path))
       ->setBrowserParam('browser')
       ->setMTime($this->_ISOTime(filemtime($path)))
-      ->setSize($this->_humanFilesize(filesize($path)));
+      ->setSize($this->_humanFilesize($size))
+      ->setFileSize($size);
   }
 
 
@@ -373,4 +375,10 @@ class Class_FileManager_FileSystem {
   public function getContent($path) {
     return file_get_contents($path);
   }
+
+
+  public function readfile($path) {
+    ob_end_clean();
+    return readfile($path);
+  }
 }
\ No newline at end of file
diff --git a/library/Class/TableDescription/CosmoWaitingFiles.php b/library/Class/TableDescription/CosmoWaitingFiles.php
new file mode 100644
index 00000000000..de22923e427
--- /dev/null
+++ b/library/Class/TableDescription/CosmoWaitingFiles.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Copyright (c) 2012-2018, 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_TableDescription_CosmoWaitingFiles extends Class_TableDescription {
+
+  use Trait_Translator;
+
+  protected $_view;
+
+
+  public function setView($view) {
+    $this->_view = $view;
+    return $this;
+  }
+
+
+  public function read() {
+    $statut = function($item) {
+      $folder = ($parent = $item->getParent()) ? $parent->getName() : '';
+
+      if (Class_IntMajAuto::findFirstby(['nom_fichier' => $folder . '/' . $item->getName()]))
+        return $this->_view->tagNotice($this->_('Programmé'));
+
+      if (!$all_prog = Class_IntMajAuto::findAllBy(['where' => 'nom_fichier like "' . $folder . '/[DATE]%"']))
+        return $this->_view->tagWarning($this->_('Non programmé'));
+
+      $programmed = (new Storm_Collection($all_prog))
+      ->detect(
+               function($element) use($item) {
+                 $parts = explode('[DATE]', $element->getNomFichier());
+
+                 return ($suffix = end($parts))
+                 ? (substr($item->getName(), -strlen($suffix)) == $suffix)
+                 : false;
+               });
+
+      return $programmed
+      ? $this->_view->tagNotice($this->_('Programmé'))
+      : $this->_view->tagWarning($this->_('Non programmé'));
+    };
+
+    $delete_url = $this->_view->url(['module' => 'cosmo',
+                                     'controller' => 'integration',
+                                     'action' => 'waiting-files-delete'], null, true);
+
+    $delete = function($item) use($delete_url) {
+      $writable = $item->isWritable();
+      $ico = $this->_view->boutonIco('type=del',
+                                     'bulle=' . ($writable
+                                                 ? $this->_('Supprimer le fichier %s', $item->getName())
+                                                 : $this->_('Impossible de supprimer le fichier %s car il y a un problème de droits', $item->getName())));
+
+      return ($writable
+              ? $this->_view->tagAnchor($delete_url . '?' . http_build_query(['id' => $item->getId()]),
+                                        $ico)
+              : $this->_view->tag('span',
+                                  $ico,
+                                  ['style' => 'filter: opacity(0.3); cursor: not-allowed']));
+    };
+
+    $download_url = $this->_view->url(['module' => 'cosmo',
+                                       'controller' => 'integration',
+                                       'action' => 'waiting-files-download'], null, true);
+
+    $download = function($item) use($download_url) {
+      return $this->_view->tagAnchor($download_url . '?' . http_build_query(['id' => $item->getId()]),
+                                     $this->_view->boutonIco('picto=down',
+                                                             'bulle='. $this->_('Télécharger le fichier %s', $item->getName())));
+    };
+
+    $this
+      ->addColumn($this->_('Dossier'),
+                  function($item) { return ($parent = $item->getParent()) ? $parent->getName() : ''; })
+
+      ->addColumn($this->_('Fichier'),
+                  function($item) { return $item->getName(); })
+
+      ->addColumn($this->_('Transféré le'),
+                  function($item) { return $item->getMTime(); })
+
+      ->addColumn($this->_('Taille'),
+                  function($item) { return $this->_view->memoryFormat($item->getFileSize()); })
+
+      ->addColumn($this->_('Statut'), $statut)
+
+      ->addRowAction($delete)
+
+      ->addRowAction($download)
+      ;
+
+    return $this;
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Controller/Action/Helper/FileManagerDownload.php b/library/ZendAfi/Controller/Action/Helper/FileManagerDownload.php
new file mode 100644
index 00000000000..9fec6a4c87b
--- /dev/null
+++ b/library/ZendAfi/Controller/Action/Helper/FileManagerDownload.php
@@ -0,0 +1,51 @@
+<?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 ZendAfi_Controller_Action_Helper_FileManagerDownload
+ extends Zend_Controller_Action_Helper_Abstract {
+
+  /** @param $file_manager Class_FileManager */
+  public function direct($file_manager) {
+    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->setNoRender();
+
+    if ($layout = Zend_Layout::getMvcInstance())
+      $layout->disableLayout();
+
+    $response = $this->getResponse();
+    $response->canSendHeaders(true);
+
+    $response->clearAllHeaders();
+    $response->setHeader('Content-Type', 'application/octet-stream; name="' . $file_manager->getName() . '"', true);
+    $response->setHeader('Content-Transfer-Encoding', 'binary', true);
+
+    $response->setHeader('Content-Length', $file_manager->getFileSize(), true);
+    $response->setHeader('Expires', '0');
+    $response->setHeader('Cache-Control', 'no-cache, must-revalidate');
+    $response->setHeader('Pragma', 'no-cache');
+
+    $response->setHeader('Content-Disposition', 'attachment; filename="' . $file_manager->getName() . '"', true);
+
+    $response->sendHeaders();
+
+    $file_manager->readfile();
+  }
+}
-- 
GitLab