diff --git a/VERSIONS_WIP/84280 b/VERSIONS_WIP/84280 new file mode 100644 index 0000000000000000000000000000000000000000..ebf0032507a1389012b9c77a47423c633d174ff1 --- /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 318457c846946ae6e21df8f26ebfb01c5384c8e0..4fc7dfbcddccf3796a8f80eaeb674c7bd2c026df 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,50 @@ class Cosmo_IntegrationController extends Zend_Controller_Action{ $this->render('generate-log'); } + + + public function waitingFilesAction() { + $this->view->titre = $this->_('Fichiers en attente d\'intégration'); + $this->view->files = (new Class_Cosmogramme_Integration_WaitingFiles())->getCollection(); + } + + + 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 0000000000000000000000000000000000000000..d06e88f744b8521cd72a4d8eff33567242a72fa7 --- /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 410afdb6544a5a417f4ec9ba5a7e644c06b6399b..e6f94c2a1a604c883621380d69cb5823505024b6 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 788b786d8aa847ea12c3a2dd4ae5139b0df4d030..d9331fa8a8dbcabdb22e46772fbe58b11e815353 100644 --- a/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php +++ b/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php @@ -44,6 +44,8 @@ abstract class CosmoControllerTestCase extends Zend_Test_PHPUnit_ControllerTestC if ($this->_storm_default_to_volatile) Storm_Model_Loader::defaultToDb(); Storm_Model_Abstract::unsetLoaders(); + Class_FileManager::reset(); + Class_FileManager_FileSystem::reset(); } @@ -66,7 +68,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 6ebfd9c32d69829b53c50796b9943ea7e3651027..eb6fde315fd409ff3a028d80b212280b4657bd75 100644 --- a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php +++ b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php @@ -595,4 +595,138 @@ 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, + 'int_bib' => $this->fixture('Class_IntBib', + ['id' => 1, + 'nom_court' => 'Lunel']), + 'libelle' => 'total', + 'profil_donnees' => $this->fixture('Class_IntProfilDonnees', + ['id' => 34, + 'libelle' => 'Unimarc KTM']), + 'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT, + 'nom_fichier' => 'lunel/[DATE]_records_tot.mrc', + 'taille_min_import_total' => 0]); + + $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) + ->whenCalled('getProgrammed')->answers(Class_IntMajAuto::find(43)) + ; + + $this->_tot->whenCalled('setProgrammed')->answers($this->_tot); + + $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 tableShouldContainsLibraryLunel() { + $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 5cb66594acad9d2c0a151c5d156d7d4934e80200..b6c05c6a316cbc02ca11f8734a4a3eabce1e60ec 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 25a53e325eb27e8d2e303380a230b639f82e9b75..0000000000000000000000000000000000000000 --- 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.'» 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=" "; 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/Cosmogramme/Integration/PhasePrepareIntegrations.php b/library/Class/Cosmogramme/Integration/PhasePrepareIntegrations.php index 602a1eef9ba61f9fac8ba6d788362510e298fd9a..2836fe1805b724936f63e1212e564cfc2a7bc999 100644 --- a/library/Class/Cosmogramme/Integration/PhasePrepareIntegrations.php +++ b/library/Class/Cosmogramme/Integration/PhasePrepareIntegrations.php @@ -84,21 +84,27 @@ class Class_Cosmogramme_Integration_PhasePrepareIntegrations extends Class_Cosmo public function _runOne($majauto) { $id_bib = $majauto->getIdBib(); - if (!$bib = Class_IntBib::find($id_bib)) + + if (!$bib = Class_IntBib::find($id_bib)) { + $majauto->delete(); return; + } $this->_log ->log('<tr><td class="blank"><span class="bib">' . $bib->getNomCourt($id_bib) .'</span></td>' .'<td class="blank">'.$majauto->getNomFichier().'</td><td class="blank">'); + if (false !== strpos($majauto->getNomFichier(), 'http://') + || false !== strpos($majauto->getNomFichier(), 'https://')) + return $this->_prepareUrl($majauto); + + if (false !== strpos($majauto->getNomFichier(), '[DATE]')) + return $this->_runPattern($majauto); + $ftpfile = $this->_getFtpFile($majauto->getNomFichier()); $id_upload = Class_CosmoVar::get('ID_upload') + 1; $newfile = 'integre' . $id_upload . '.pan'; - if (false !== strpos($majauto->getNomFichier(), 'http://')) { - return $this->_prepareUrl($majauto); - } - if (!$ftpfile || !$this->getFileSystem()->is_file($ftpfile)) { $this->_log->log($this->_('pas de transfert pour : %s', $ftpfile) . '</td>'); return $this; @@ -127,8 +133,65 @@ class Class_Cosmogramme_Integration_PhasePrepareIntegrations extends Class_Cosmo } + protected function _runPattern($majauto) { + $selector = function($file) use($majauto) { + if (!$majauto->isFileNameValid($file)) + return false; + + if (!$majauto->isFileSizeValid($file)) { + $this->_log + ->log(sprintf('<span class="rouge">%s</span></td>', + $this->_('Le fichier est trop petit : %s o -> taille minimum attendue : %s o', + $file->getFileSize(), + $majauto->getFileSizeLimit()))); + return false; + } + + return true; + }; + + $files = (new Class_Cosmogramme_Integration_WaitingFiles) + ->getCollection() + ->select($selector); + + if ($files->isEmpty()) { + $this->_log->log($this->_('pas de transfert pour : %s', $majauto->getNomFichier()) . '</td>'); + return $this; + } + + foreach($files as $file) + $this->_runOnePattern($majauto, $file); + + return $this; + } + + + protected function _runOnePattern($majauto, $file) { + $filesystem = $this->getFilesystem(); + $newfile = $this->_getNextIntegrationFileName(); + + if (true !== $filesystem->rename($file->getId(), $this->_integration_path . $newfile)) { + $this->_log + ->log(sprintf('<span class="rouge">%s</span></td>', + $this->_('impossible de transférer %s vers %s', $file->getId(), $newfile))); + + $this->_incrementData('traitement_erreurs'); + return; + } + + $this->_log + ->log(sprintf('<span class="vert">%s</span></td>', + $this->_('transfert de %s vers %s', $file->getId(), $newfile))); + + + $this->_newIntegration($newfile, $majauto); + Class_CosmoVar::setValueOf('ID_upload', Class_CosmoVar::get('ID_upload') + 1); + } + + protected function _prepareUrl($majauto) { $uri = $majauto->getNomFichier(); + if (Class_Cosmogramme_Integration::findFirstBy(['traite' => 'non', 'fichier' => $uri])) return $this; @@ -218,4 +281,9 @@ class Class_Cosmogramme_Integration_PhasePrepareIntegrations extends Class_Cosmo 'traite' => 'non']); $integration->save(); } + + + protected function _getNextIntegrationFileName() { + return 'integre' . (Class_CosmoVar::get('ID_upload') + 1) . '.pan'; + } } diff --git a/library/Class/Cosmogramme/Integration/WaitingFiles.php b/library/Class/Cosmogramme/Integration/WaitingFiles.php new file mode 100644 index 0000000000000000000000000000000000000000..fd921d81b982ce85a140b31c82c4bd917f15744a --- /dev/null +++ b/library/Class/Cosmogramme/Integration/WaitingFiles.php @@ -0,0 +1,44 @@ +<?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_Cosmogramme_Integration_WaitingFiles { + public function getCollection() { + Class_FileManager::beOpenBar(); + Class_FileManager_FileSystem::beUnlimited(); + + $files = []; + foreach(Class_FileManager::directories(Class_CosmoVar::get('ftp_path')) as $directory) + foreach(Class_FileManager::files($directory->getPath()) as $file) + $files[] = $file; + + usort($files, + function($a, $b) + { + if ($a->getFileMTime() === $b->getFileMTime()) + return 0; + + return $a->getFileMTime() > $b->getFileMTime() ? 1 : -1; + }); + + return new Storm_Collection($files); + } +} diff --git a/library/Class/FileManager.php b/library/Class/FileManager.php index 7cbb50b2c84276c5ace2018ddedb085e8dad44a0..89de300e1dfa799267b4295d40a1cda068157c24 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 7289df022db2dc50a48e8b8d18283eb06a328280..120d2a4d19418f350371ff1c2d7b6e78d2ae2b2d 100644 --- a/library/Class/FileManager/FileSystem.php +++ b/library/Class/FileManager/FileSystem.php @@ -26,12 +26,23 @@ class Class_FileManager_FileSystem { const LISTING_LIMIT = 200; + protected static $_limited = true; protected $_cached_paths = [], $_oversized = []; + public static function beUnlimited() { + static::$_limited = false; + } + + + public static function reset() { + static::$_limited = true; + } + + public function directoriesAt($path) { return $this->_filterEntriesAt($path, [$this, 'directoryAt']); } @@ -45,7 +56,7 @@ class Class_FileManager_FileSystem { protected function _filterEntriesAt($path, $callback = null) { $items = $this->_glob($path, true); - if(Class_FileManager_FileSystem::LISTING_LIMIT < count($items)) { + if (static::$_limited && Class_FileManager_FileSystem::LISTING_LIMIT < count($items)) { $this->_oversized[] = $path; $items = array_slice($items, 0, Class_FileManager_FileSystem::LISTING_LIMIT); } @@ -289,6 +300,8 @@ class Class_FileManager_FileSystem { $basename = $info['basename']; $path = implode('/', array_filter([$dirname, $basename])); + $size = filesize($path); + $mtime = filemtime($path); return (new Class_FileManager) ->setId($path) @@ -300,8 +313,10 @@ class Class_FileManager_FileSystem { ->setParentPath($dirname) ->setWritable($this->_isWritable($path)) ->setBrowserParam('browser') - ->setMTime($this->_ISOTime(filemtime($path))) - ->setSize($this->_humanFilesize(filesize($path))); + ->setMTime($this->_ISOTime($mtime)) + ->setFileMTime($mtime) + ->setSize($this->_humanFilesize($size)) + ->setFileSize($size); } @@ -373,4 +388,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/IntMajAuto.php b/library/Class/IntMajAuto.php index 5e881b5e3f6720dfbab53196ffe3cb7aa7c3a122..41df414e0f0b4a5045fd27d373e1d019002de3ff 100644 --- a/library/Class/IntMajAuto.php +++ b/library/Class/IntMajAuto.php @@ -20,6 +20,8 @@ */ class Class_IntMajAuto extends Storm_Model_Abstract { + use Trait_Translator; + const OP_PARTIAL_IMPORT = 0; const OP_ITEMS_DELETION = 1; const OP_FULL_IMPORT = 2; @@ -32,4 +34,84 @@ class Class_IntMajAuto extends Storm_Model_Abstract { 'referenced_in' => 'id_bib'], 'profil_donnees' => ['model' => 'Class_IntProfilDonnees', 'referenced_in' => 'profil']]; + + /** + * @param $file_manager Class_FileManager + * @return boolean + */ + public function isFileNameValid($file_manager) { + if ($file_manager->getId() == $this->getNomFichier()) + return true; + + if (false === strpos($this->getNomFichier(), '[DATE]')) + return false; + + $folder = ($parent = $file_manager->getParent()) ? $parent->getName() : ''; + + if (false !== strpos($this->getNomFichier(), '/[DATE]') + && false === strpos($this->getNomFichier(), $folder . '/[DATE]')) + return false; + + $parts = explode('[DATE]', $this->getNomFichier()); + + return ($suffix = end($parts)) + ? (substr($file_manager->getName(), -strlen($suffix)) == $suffix) + : false; + } + + + /** + * @param $file_manager Class_FileManager + * @return boolean + */ + public function isFileSizeValid($file_manager) { + return ($size_limit = $this->getFileSizeLimit()) + ? $file_manager->getFileSize() >= $size_limit + : true; + } + + + public function getFileSizeLimit() { + if (!$profil = $this->getProfilDonnees()) + return 0; + + if (!$profil->isBiblioRecords()) + return 0; + + if (!$this->isTotal()) + return 0; + + return (int)$this->getTailleMinImportTotal() * 1024 * 1024; + } + + + public function isTotal() { + return static::OP_FULL_IMPORT == (int)$this->getTypeOperation(); + } + + + public function getLibraryLabel() { + return ($lib = $this->getIntBib()) + ? $lib->getLabel() + : ''; + } + + + public function getOperationLabel() { + $map = [static::OP_PARTIAL_IMPORT => $this->_('Import incrémentiel'), + static::OP_ITEMS_DELETION => $this->_('Suppression d\'exemplaires'), + static::OP_FULL_IMPORT => $this->_('Import total'), + static::OP_DELETE_PERGAME_HEADER => $this->_('Suppression fichier d\'en-tête Pergame')]; + + return array_key_exists($this->getTypeOperation(), $map) + ? $map[$this->getTypeOperation()] + : ''; + } + + + public function getDataProfileLabel() { + return ($profile = $this->getProfilDonnees()) + ? $profile->getLibelle() + : ''; + } } diff --git a/library/Class/Systeme/Report.php b/library/Class/Systeme/Report.php index b9e0002245d245ccccb88d83fc1a6f286faeb297..4419267a9ec493fc5cf5bb76b354b11725661795 100644 --- a/library/Class/Systeme/Report.php +++ b/library/Class/Systeme/Report.php @@ -272,7 +272,9 @@ class Class_Systeme_Report_Variables extends Class_Systeme_Report_Abstract { class Class_Systeme_Report_Userfiles extends Class_Systeme_Report_Abstract { public function acceptVisitor($visitor) { - $disk = Class_FileManager::getDiskSpaceInfo(); + if (!$disk = Class_FileManager::getDiskSpaceInfo()) + return; + $visitor->visitData('used', $this->_('Espace disque utilisé'), $disk->getUsed()); $visitor->visitData('free', $this->_('Espace disque libre'), $disk->getFree()); $visitor->visitData('total', $this->_('Espace disque total'), $disk->getTotal()); diff --git a/library/Class/TableDescription/CosmoWaitingFiles.php b/library/Class/TableDescription/CosmoWaitingFiles.php new file mode 100644 index 0000000000000000000000000000000000000000..c17e34f3461dbe33303704920d2986ced041de54 --- /dev/null +++ b/library/Class/TableDescription/CosmoWaitingFiles.php @@ -0,0 +1,144 @@ +<?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 ($prog = Class_IntMajAuto::findFirstby(['nom_fichier' => $folder . '/' . $item->getName()])) { + $item->setProgrammed($prog); + 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) { + return $element->isFileNameValid($item); + }); + + if (!$programmed) + return $this->_view->tagWarning($this->_('Non programmé')); + + $item->setProgrammed($programmed); + + return $programmed->isFileSizeValid($item) + ? $this->_view->tagNotice($this->_('Programmé')) + : $this->_view->tagError($this->_('Programmé mais trop petit (%s < %s)', + $this->_view->memoryFormat($item->getFileSize()), + $this->_view->memoryFormat($programmed->getFileSizeLimit()))); + }; + + $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) + + ->addColumn($this->_('Bibliothèque'), + function($item) { + return ($prog = $item->getProgrammed()) + ? $prog->getLibraryLabel() + : ''; + }) + + ->addColumn($this->_('Intégration'), + function($item) { + return ($prog = $item->getProgrammed()) + ? $prog->getLibelle() + : ''; + }) + + ->addColumn($this->_('Type d\'opération'), + function($item) { + return ($prog = $item->getProgrammed()) + ? $prog->getOperationLabel() + : ''; + }) + + ->addColumn($this->_('Profil de données'), + function($item) { + return ($prog = $item->getProgrammed()) + ? $prog->getDataProfileLabel() + : ''; + }) + + ->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 0000000000000000000000000000000000000000..9fec6a4c87bc78cff3224a58597797c524839469 --- /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(); + } +} diff --git a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php index 96f9331af73dca171eb5671c7c3e319d921d4c17..a3c3f28f6720444fb5289c45b7a0ce7ddd73708b 100644 --- a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php +++ b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php @@ -27,49 +27,89 @@ abstract class PhasePrepareIntegrationsWithOAITestCase public function setUp() { parent::setUp(); - $file_system = $this->mock() - ->whenCalled('is_file') - ->with ('ftp/my-library.net/transferts/foo/mylibrarytotal.txt') - ->answers(true) - - ->whenCalled('filesize') - ->with ('ftp/my-library.net/transferts/foo/mylibrarytotal.txt') - ->answers(11000000) - - ->whenCalled('is_file') - ->with ('ftp/my-library.net/transferts/foo/toosmall.txt') - ->answers(true) - - ->whenCalled('filesize') - ->with ('ftp/my-library.net/transferts/foo/toosmall.txt') - ->answers(1000000) - - ->whenCalled('is_file') - ->with ('ftp/my-library.net/transferts/foo/20180517mylibraryincr.txt') - ->answers(true) - - ->whenCalled('opendir') - ->with ('ftp/my-library.net/transferts/foo/') - ->answers('dir_pointer') - - ->whenCalled('readdir') - ->with ('dir_pointer') - ->answers('20180517mylibraryincr.txt') - - ->whenCalled('closedir') - ->with ('dir_pointer') - ->answers(true) - - ->whenCalled('rename') - ->with('ftp/my-library.net/transferts/foo/mylibrarytotal.txt', - 'ftp/my-library.net/integration/integre1179.pan') - ->answers(true) - - ->whenCalled('rename') - ->with('ftp/my-library.net/transferts/foo/20180517mylibraryincr.txt', - 'ftp/my-library.net/integration/integre1180.pan') - ->answers(true) - ->beStrict(); + $foo = + $this->mock() + ->whenCalled('getName')->answers('foo') + ->whenCalled('getPath')->answers('ftp/my-library.net/transferts/foo'); + + $file_with_date = + $this->mock() + ->whenCalled('getName')->answers('20180517mylibraryincr.txt') + ->whenCalled('getId')->answers('ftp/my-library.net/transferts/foo/20180517mylibraryincr.txt') + ->whenCalled('getFileMTime')->answers(strtotime('2018-05-17')) + ->whenCalled('getParent')->answers($foo); + + $other_with_date = + $this->mock() + ->whenCalled('getName')->answers('20190118mylibraryincr.txt') + ->whenCalled('getId')->answers('ftp/my-library.net/transferts/foo/20190118mylibraryincr.txt') + ->whenCalled('getFileMTime')->answers(strtotime('2019-01-18')) + ->whenCalled('getParent')->answers($foo); + + $file_system_manager = + $this->mock() + ->whenCalled('diskSpaceInfo')->answers(null) + + ->whenCalled('directoriesAt')->with('ftp/my-library.net/transferts/') + ->answers([$foo]) + + ->whenCalled('filesAt')->with('ftp/my-library.net/transferts/foo') + ->answers([$file_with_date, + $other_with_date]); + + + Class_FileManager::setFileSystem($file_system_manager); + + $file_system = + $this->mock() + ->whenCalled('is_file') + ->with ('ftp/my-library.net/transferts/foo/mylibrarytotal.txt') + ->answers(true) + + ->whenCalled('filesize') + ->with ('ftp/my-library.net/transferts/foo/mylibrarytotal.txt') + ->answers(11000000) + + ->whenCalled('is_file') + ->with ('ftp/my-library.net/transferts/foo/toosmall.txt') + ->answers(true) + + ->whenCalled('filesize') + ->with ('ftp/my-library.net/transferts/foo/toosmall.txt') + ->answers(1000000) + + ->whenCalled('is_file') + ->with ('ftp/my-library.net/transferts/foo/20180517mylibraryincr.txt') + ->answers(true) + + ->whenCalled('opendir') + ->with ('ftp/my-library.net/transferts/foo/') + ->answers('dir_pointer') + + ->whenCalled('readdir') + ->with('dir_pointer') + ->answers('20180517mylibraryincr.txt') + + ->whenCalled('closedir') + ->with ('dir_pointer') + ->answers(true) + + ->whenCalled('rename') + ->with('ftp/my-library.net/transferts/foo/mylibrarytotal.txt', + 'ftp/my-library.net/integration/integre1179.pan') + ->answers(true) + + ->whenCalled('rename') + ->with('ftp/my-library.net/transferts/foo/20180517mylibraryincr.txt', + 'ftp/my-library.net/integration/integre1180.pan') + ->answers(true) + + ->whenCalled('rename') + ->with('ftp/my-library.net/transferts/foo/20190118mylibraryincr.txt', + 'ftp/my-library.net/integration/integre1181.pan') + ->answers(true) + + ->beStrict(); $this->_http_client = $this->mock() ->whenCalled('postData') @@ -216,29 +256,15 @@ class PhasePrepareIntegrationsWithOAITest extends PhasePrepareIntegrationsWithOA /** @test */ - public function threeIntegrationsShouldHaveBeenCreated() { - $this->assertEquals(4, count(Class_Cosmogramme_Integration::findAll())); - } - - - /** @test */ - public function firstIntegrationFileShouldBeIntegre1179DotPan() { - $this->assertEquals('integre1179.pan', - Class_Cosmogramme_Integration::find(2)->getFichier()); - } - - - /** @test */ - public function firstIntegrationFileNameShouldBeMyLibraryTotalDotTxt() { - $this->assertEquals('foo/mylibrarytotal.txt', - Class_Cosmogramme_Integration::find(2)->getNomFichier()); + public function fiveIntegrationsShouldHaveBeenCreated() { + $this->assertEquals(5, count(Class_Cosmogramme_Integration::findAll())); } /** @test */ public function firstIntegrationKeyShouldBeHash() { - $this->assertEquals('a7be1b1bbdfe70b6fec563334accab3f', - Class_Cosmogramme_Integration::find(2)->getHash()); + $this->assertEquals(Class_Cosmogramme_Integration::findFirstBy(['nom_fichier' => 'foo/mylibrarytotal.txt']) + ->getHash(), 'a7be1b1bbdfe70b6fec563334accab3f'); } @@ -258,7 +284,7 @@ class PhasePrepareIntegrationsWithOAITest extends PhasePrepareIntegrationsWithOA /** @test */ public function thirdIntegrationURIShouldBeOAIRepository() { $this->assertEquals('http://oai-repository.fr/oai', - Class_Cosmogramme_Integration::find(4)->getFichier()); + Class_Cosmogramme_Integration::find(5)->getFichier()); } @@ -336,8 +362,8 @@ class PhasePrepareIntegrationsWithOAIIntegrationAlreadyRegisteredTest /** @test */ - public function oneIntegrationShouldHaveBeenCreated() { - $this->assertEquals(4, count(Class_Cosmogramme_Integration::findAll())); + public function fiveIntegrationShouldHaveBeenCreated() { + $this->assertEquals(5, count(Class_Cosmogramme_Integration::findAll())); } } @@ -352,12 +378,39 @@ class PhasePrepareIntegrationsNanookStandardTest Zend_Registry::set('sql', $this->mock()->beStrict()); - $file_system = $this->mock() - ->whenCalled('is_file') - ->with('ftp/my-library.net/transferts/foo/mylibrarytotal.txt') - ->answers(false) - ->beStrict(); + $file_system = $this->mock()->beStrict(); + + foreach ([2, 6, 8] as $site) + $file_system + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/suppressions.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/notices_total.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/notices.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/abonnes.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/prets.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/paniers_total.txt') + ->answers(false) + + ->whenCalled('is_file') + ->with('ftp/my-library.net/transferts/foo/site' . $site . '/paniers.txt') + ->answers(false); $this->_http_client = $this->mock() ->whenCalled('postData')