diff --git a/cosmogramme/cosmozend/application/modules/cosmo/controllers/DeleteItemsController.php b/cosmogramme/cosmozend/application/modules/cosmo/controllers/DeleteItemsController.php new file mode 100644 index 0000000000000000000000000000000000000000..cd0a2d5ce388edd7213945e697996c106b53d517 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/controllers/DeleteItemsController.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright (c) 2012-2014, 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 Cosmo_DeleteItemsController extends ZendAfi_Controller_Action { + + public function indexAction() { + $this->view->titre = $this->_('Suppression des notices ou des exemplaires'); + $this->view->libraries = Class_IntBib::query()->order('id_bib')->fetchAll(); + } + + + public function allDashboardAction() { + $this->view->titre = $this->_('Supprimer toutes les notices et tous les exemplaires'); + } + + + public function libraryDashboardAction() { + $id_bib = $this->_getParam('id'); + $bib_name = Class_IntBib::find($id_bib)->getNomCourt(); + + $this->view->id_bib = $id_bib; + $this->view->titre = $this->_("Supprimer tous les exemplaires de $bib_name"); + } + + + public function allAction() { + session_write_close(); + + $this->view->titre = $this->_('Suppression des notices et tout le reste en cours'); + + if ( null === $step = $this->_getParam('step')) { + $this->_helper->notify($this->_('Impossible de supprimer les notices et tout le reste')); + $this->_redirectClose($this->_getReferer()); + return; + } + + $cleaner = + ((new Class_Cosmogramme_Cleaner($step, $this->_getParam('counter', 0))) + ->delete()); + + $this->view->content = $this->view->Cosmo_Cleaner($cleaner); + + if ( 'ajax' !== $this->_getParam('render', '')) + return; + + $this->_helper->getHelper('HTMLAjaxResponse') + ->htmlAjaxResponseWithScript($this->view->content); + } + + + public function libraryAction() { + session_write_close(); + + $id_bib = $this->_getParam('id'); + $step = $this->_getParam('step'); + $count_items = $this->_getParam('count_items'); + $count_records = $this->_getParam('count_succinct_records'); + $counter = $this->_getParam('counter'); + $limit = $this->_getParam('limit') ?? 0; + + if ( null === ($id_bib = $this->_getParam('id', null))) { + $this->_helper->notify($this->_('Identifiant de bibliothèque manquant.')); + return $this->_redirectToReferer(); + } + + $cleaner = ((new Class_Cosmogramme_ItemsCleaner($id_bib, $step, $count_items, $count_records, $counter, $limit)) + ->delete()); + + $this->view->content = $this->view->Cosmo_ItemsCleaner($cleaner); + + if ( 'ajax' !== $this->_getParam('render', '')) + return; + + $this->_helper->getHelper('HTMLAjaxResponse') + ->htmlAjaxResponseWithScript($this->view->content); + } +} diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all-dashboard.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all-dashboard.phtml new file mode 100644 index 0000000000000000000000000000000000000000..14364c5de0a5ad3869b48c570135b26da30b5337 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all-dashboard.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->Cosmo_DeleteItemsAllDashboard(); diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all.phtml new file mode 100644 index 0000000000000000000000000000000000000000..ff71d31e883048cb05ea7b632370dd47b1fe1517 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/all.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->content; diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/index.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/index.phtml new file mode 100644 index 0000000000000000000000000000000000000000..5d33d1409524b4a6b2769202a19b02ed01b85e37 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/index.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->Cosmo_DeleteItems(); diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library-dashboard.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library-dashboard.phtml new file mode 100644 index 0000000000000000000000000000000000000000..afa99919ec8b1fac561d56acb62f8683fb998468 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library-dashboard.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->Cosmo_DeleteItemsLibraryDashboard(); diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library.phtml new file mode 100644 index 0000000000000000000000000000000000000000..ff71d31e883048cb05ea7b632370dd47b1fe1517 --- /dev/null +++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/delete-items/library.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->content; diff --git a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/DeleteItemsControllerTest.php b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/DeleteItemsControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..99e855b09bdaae3b13b205e2f50cdd465f000fbe --- /dev/null +++ b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/DeleteItemsControllerTest.php @@ -0,0 +1,617 @@ +<?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 Cosmo_DeleteItemsControllerIndexActionTest extends CosmoControllerTestCase { + public function setUp() { + parent::setUp(); + + $this->fixture(Class_IntBib::class, + ['id' => 0, + 'nom_court' => 'Authume']); + + $this->fixture(Class_IntBib::class, + ['id' => 5, + 'nom_court' => 'Foucherans']); + + $this->dispatch('/cosmo/delete-items/index'); + } + + + /** @test */ + public function titleShouldBeSuppressionDeNoticeOuDExemplaires() { + $this->assertXPathContentContains('//h1', + 'Suppression des notices ou des exemplaires'); + } + + + /** @test */ + public function pageShouldContainsButtonSupprimerToutesLesNoticesDeLaBase() { + $this->assertXPathContentContains('//button[@data-url="/cosmo/delete-items/all-dashboard"]', 'Supprimer toutes les notices de la base'); + } + + + /** @test */ + public function pageShouldContainsDeletionNotice() { + $this->assertXPathContentContains('//p[@class="notice"]', 'Les boutons suivants vous permettent de supprimer les exemplaires d\'une bibliothèque.'); + } + + + /** @test */ + public function tableShouldContainsLibraryIdZero() { + $this->assertXPath('//table/tbody/tr[1]/td[1][contains(text(),"0")]'); + } + + + /** @test */ + public function tableShoudContainsLibraryAuthume() { + $this->assertXPathContentContains('//table/tbody/tr[1]/td[2]', 'Authume'); + } + + + /** @test */ + public function tableShouldContainsDeleteItemsLibraryIdZero() { + $this->assertXPath('//table/tbody/tr[1]/td[3]/a[@href="/cosmo/delete-items/library-dashboard/id/0"]/img[@title="Supprimer les exemplaires"]'); + } + + + /** @test */ + public function tableShouldContainsLibraryIdFive() { + $this->assertXPathContentContains('//table/tbody/tr[2]/td[1]', '5'); + } + + + /** @test */ + public function tableShouldContainsLibraryFocherans() { + $this->assertXPathContentContains('//table/tbody/tr[2]/td[2]', 'Foucherans'); + } + + + /** @test */ + public function tableShouldContainsDeleteItemsLibraryIdFive() { + $this->assertXPath('//table/tbody/tr[2]/td[3]/a[@href="/cosmo/delete-items/library-dashboard/id/5"]/img[@title="Supprimer les exemplaires"]'); + } +} + + + + +abstract class Cosmo_DeleteItemsControllerAllTestCase extends CosmoControllerTestCase { + + public function setup() { + parent::setUp(); + + $this->fixture(Class_Notice::class, ['id' => 1]); + $this->fixture(Class_Notice::class, ['id' => 2]); + $this->fixture(Class_Notice::class, ['id' => 3]); + $this->fixture(Class_Notice::class, ['id' => 4]); + + $this->fixture(Class_Notice_SerialArticles::class, ['id' => 1, + 'clef_chapeau' => 'MY-KEY', + 'clef_numero' => '001']); + + $this->fixture(Class_NoticeSuccincte::class, ['id' => 1]); + $this->fixture(Class_Exemplaire::class, ['id' => 1]); + $this->fixture(Class_StatsNotices::class, ['id' => 1]); + $this->fixture(Class_CodifTags::class, ['id' => 1]); + $this->fixture(Class_Cosmogramme_Integration::class, ['id' => 1]); + $this->fixture(Class_IntAnalyse::class, ['id' => 1]); + $this->fixture(Class_Pret::class, ['id' => 1]); + $this->fixture(Class_Reservation::class, ['id' => 1]); + + Class_CosmoVar::setValueOf('cache_path', './cosmogramme/fichiers/my_cache'); + Class_CosmoVar::setValueOf('integration_path', './cosmogramme/fichiers/my_integration'); + Class_CosmoVar::setValueOf('log_path', './cosmogramme/fichiers/my_log'); + + $disk = (new Storm_FileSystem_Volatile) + ->mkdir('cosmogramme/fichiers/my_cache') + ->mkdir('cosmogramme/fichiers/my_integration') + ->mkdir('cosmogramme/fichiers/my_log') + ->cd('cosmogramme/fichiers/my_cache') + ->touch('8934789UI98E.cache') + ->cd('../my_integration') + ->touch('9456G6734YC.txt') + ->cd('/'); + + ZendAfi_View_Helper_Cosmo_DeleteItemsAllDashboard::setFileSystem($disk); + + $fs = $this->mock(); + $fs->whenCalled('opendir') + ->answers('dir_pointer') + + ->whenCalled('readdir') + ->willDo(function() + { + Class_Cosmogramme_Cleaner::getFileSystem()->whenCalled('readdir') + ->answers(false); + return '8934789UI98E.cache'; + }) + + ->whenCalled('is_dir') + ->answers(false) + + ->whenCalled('unlink') + ->answers(true) + + ->whenCalled('closedir') + ->answers(true); + + Class_Cosmogramme_Cleaner::setFileSystem($fs); + } + + + public function tearDown() { + ZendAfi_View_Helper_Cosmo_DeleteItemsAllDashboard::setFileSystem(null); + Class_Cosmogramme_Cleaner::setFileSystem(null); + return parent::tearDown(); + } +} + + + + +class Cosmo_DeleteItemsControllerAllRunDeleteArticleRecordsTest + extends Cosmo_DeleteItemsControllerAllTestCase { + + public function setUp() { + parent::setUp(); + } + + + public function stepClassMapping() { + return [[0, Class_Notice::class], + [1, Class_Notice_SerialArticles::class], + [2, Class_NoticeSuccincte::class], + [3, Class_Exemplaire::class], + [4, Class_StatsNotices::class], + [5, Class_CodifTags::class], + [6, Class_Cosmogramme_Integration::class], + [7, Class_IntAnalyse::class], + [8, Class_Pret::class], + [9, Class_Reservation::class]]; + } + + + /** + @dataProvider stepClassMapping + @test + */ + public function pageShouldContainsNextUrlWithNoticesSuccinctes(int $step, string $class) { + $this->dispatch('/cosmo/delete-items/all/step/' . $step . '/counter/' . $step ); + $step++; + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/all/step/' . $step . '/counter/' . $step . '"]'); + } + + + /** + @dataProvider stepClassMapping + @test + */ + public function entitiesShouldBeDeleted(int $step, string $class) { + $this->dispatch('/cosmo/delete-items/all/step/' . $step . '/counter/' . $step ); + $this->assertEmpty($class::findAll()); + } + + /** + @dataProvider stepClassMapping + @test + */ + public function pageShouldContainsTitleSuppressionEnCours(int $step, string $class) { + $this->dispatch('/cosmo/delete-items/all/step/' . $step . '/counter/' . $step ); + $this->assertXPathContentContains('//h3', 'Suppression en cours'); + } + + + public function stepDirectoryMapping() { + return [[10, 'cache_path'], + [11, 'integration_path'], + [12, 'log_path'] + ]; + } + + + /** + @dataProvider stepDirectoryMapping + @test + */ + public function directoryShouldBeEmpty(int $step, string $variable) { + $path = Class_CosmoVar::getValueOf($variable); + $this->dispatch('/cosmo/delete-items/all/step/' . $step . '/counter/' . $step ); + $this->assertFalse(Class_Cosmogramme_Cleaner::getFileSystem()->readdir($path)); + } +} + + + + +class Cosmo_DeleteItemsControllerAllDashboardTest + extends Cosmo_DeleteItemsControllerAllTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/cosmo/delete-items/all-dashboard'); + } + + + /** @test */ + public function pageTitleShouldBeDeleteAllRecordsAndItems() { + $this->assertXPathContentContains('//h1', 'Supprimer toutes les notices et tous les exemplaires'); + } + + + /** @test */ + public function pageShouldContainConfirmButtonLancerLaSuppression() { + $this->assertXPathContentContains('//button[@id="run_delete"]', 'Lancer la suppression'); + } + + + /** @test */ + public function pageShouldContainBackButton() { + $this->assertXPathContentContains('//button[@class="back admin-button"]', 'Retour'); + } + + + /** @test */ + public function pageShouldContainModelsWarning() { + $this->assertXPathContentContains('//p[@class="notice"]', 'Êtes-vous sûr de vouloir supprimer les données suivantes'); + } + + + /** @test */ + public function pageShouldContain4Notices() { + $this->assertXPathContentContains('//li', '4 notices'); + } + + + public function modelsToCount() : array { + return + [['notices'], + ['notices d\'articles'], + ['notices succinctes'], + ['exemplaires'], + ['statistiques des notices'], + ['codifications de tags'], + ['integrations'], + ['analyses d\'intégrations'], + ['prêts'], + ['réservations']]; + } + + + /** + * @test + * @dataProvider modelsToCount + */ + public function pageShouldContainModelLabel($label) { + $this->assertXPathContentContains('//ul/li', $label); + } + + + public function filesToCount() : array { + return [['my_cache'], + ['my_integration'], + ['my_log']]; + } + + + /** + * @test + * @dataProvider filesToCount + */ + public function pageShouldContainFolderToDelete($dir_name) { + $this->assertXPathContentContains('//ul/li', './cosmogramme/fichiers/' . $dir_name); + } + + + /** @test */ + public function pageShouldContainsFolderMyCacheWith1Files() { + $this->assertXPathContentContains('//ul/li', './cosmogramme/fichiers/my_cache (1 fichiers)'); + } + + + /** @test */ + public function pageShouldContainsFolderMyIntegrationWith1Files() { + $this->assertXPathContentContains('//ul/li', './cosmogramme/fichiers/my_integration (1 fichiers)'); + } + + + /** @test */ + public function pageShouldContainsFolderMyLogWith1Files() { + $this->assertXPathContentContains('//ul/li', './cosmogramme/fichiers/my_log (0 fichiers)'); + } + + + /** @test */ + public function pageShouldHaveScriptAjaxDelete() { + $this->assertXPath('//script[contains(@src, "/public/admin/js/ajax-delete/ajax-delete.js")]'); + } + + + /** @test */ + public function pageShouldHaveScriptModalLoading() { + $this->assertXPath('//script[contains(@src, "/public/admin/js/modal-loading/modal-loading.js")]'); + } + + + /** @test */ + public function pageShouldHaveCssModalLoading() { + $this->assertXPath('//link[contains(@href, "/public/admin/js/modal-loading/modal-loading.css")]'); + } + + + /** @test */ + public function pageShouldHaveScriptToInitAjaxDeleteOnDeleteAll() { + $this->assertXPathContentContains('//script', '$(function(){$("#delete_all").ajax_delete();});'); + } + + + /** @test */ + public function pageShouldHaveDivIdDeleteAllWithDataAfterDeleteUrlDashboard() { + $this->assertXPath('//div[@id="delete_all"][@data-after-delete-url="/cosmo/delete-items/all-dashboard"]'); + } + + + /** @test */ + public function pageShouldHaveDivIdDeleteAllWithDataDeleteUrlAll() { + $this->assertXPath('//div[@id="delete_all"][@data-delete-url="/cosmo/delete-items/all/step/0/counter/0"]'); + } +} + + + +abstract class Cosmo_DeleteItemsControllerLibraryTestCase extends CosmoControllerTestCase { + + public function setup() { + parent::setUp(); + + $this->fixture(Class_IntBib::class, + ['id' => 1, + 'id_bib' => 1, + 'nom_court' => 'Champvans']); + + $this->fixture(Class_IntBib::class, + ['id' => 3, + 'ib_bib' => 3, + 'nom_court' => 'Authume']); + + $this->fixture(Class_Notice::class, ['id' => 1, + 'facettes' => 'HCCC001 B1 B3']); + $this->fixture(Class_Notice::class, ['id' => 2, + 'facettes' => 'HCCC001 B1 B4']); + $this->fixture(Class_Notice::class, ['id' => 3, + 'facettes' => 'HCCC001 B1 B5']); + $this->fixture(Class_Notice::class, ['id' => 4, + 'facettes' => 'HCCC001 B3']); + + foreach (range(5, 104) as $id) + $this->fixture(Class_Notice::class, ['id' => $id, + 'facettes' => 'HCCC001']); + + + $this->fixture(Class_Exemplaire::class, ['id' => 1, + 'id_bib' => 1, + 'id_notice' => 1]); + + $this->fixture(Class_Exemplaire::class, ['id' => 2, + 'id_bib' => 3, + 'id_notice' => 1]); + + $this->fixture(Class_Exemplaire::class, ['id' => 3, + 'id_bib' => 3, + 'id_notice' => 2]); + + $this->fixture(Class_Exemplaire::class, ['id' => 4, + 'id_bib' => 3, + 'id_notice' => 3]); + + foreach (range(5, 104) as $item_id) + $this->fixture(Class_Exemplaire::class, ['id' => $item_id, + 'id_bib' => 3]); + + $this->fixture(Class_NoticeSuccincte::class, ['id' => 1, 'id_bib' => 1]); + $this->fixture(Class_NoticeSuccincte::class, ['id' => 2, 'id_bib' => 3]); + + foreach (range(3, 102) as $id) + $this->fixture(Class_NoticeSuccincte::class, ['id' => $id, 'id_bib' => 3]); + + $this->fixture(Class_Notice_SerialArticles::class, ['id' => 1, + 'clef_chapeau' => 'MY-KEY', + 'clef_numero' => '001']); + + $this->fixture(Class_CodifTags::class, ['id' => 1]); + } +} + + + +class Cosmo_DeleteItemsControllerLibraryDashboardTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/cosmo/delete-items/library-dashboard/id/1'); + } + + + /** @test */ + public function pageTitleShouldBeDeleteItemsForChampvans() { + $this->assertXPathContentContains('//h1', 'Supprimer tous les exemplaires de Champvans'); + } + + + /** @test */ + public function pageShouldContainConfirmButtonLancerLaSuppression() { + $this->assertXPathContentContains('//button[@id="run_delete"]', 'Lancer la suppression'); + } + + + /** @test */ + public function pageShouldContainBackButton() { + $this->assertXPathContentContains('//button[@class="back admin-button"]', 'Retour'); + } + + + /** @test */ + public function pageShouldContainModelsWarning() { + $this->assertXPathContentContains('//p[@class="notice"]', 'Êtes-vous sûr de vouloir supprimer les données suivantes'); + } + +} + + + + +class Cosmo_DeleteItemsControllerLibraryActionFirstStepFirstRangeTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/2/step/0/counter/0/limit/100'); + } + + + /** @test */ + public function pageShouldContainNextStepUrl() { + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/2/step/0/counter/100/limit/100"]'); + } + + + /** @test */ + public function hundredItemsShouldHaveBeenDeletedForLibraryThree() { + $this->assertEquals(3, count(Class_Exemplaire::findAllBy(['id_bib' => 3]))); + } +} + + + + +class Cosmo_DeleteItemsControllerLibraryActionFirstStepSecondRangeTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + Class_Exemplaire::deleteBy(['id <' => 100]); + $this->dispatch('/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/2/step/0/counter/100'); + } + + + /** @test */ + public function pageShouldContainNextStepUrl() { + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/2/step/1/counter/0"]'); + } + + + /** @test */ + public function allItemsShouldHaveBeenDeletedForLibraryThree() { + $this->assertCount(0, Class_Exemplaire::findAllBy(['id_bib' => 3])); + } +} + + + + +class Cosmo_DeleteItemsControllerLibraryActionSecondStepFirstRangeTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/1/counter/0/limit/100'); + } + + + /** @test */ + public function pageShouldContainNextStepUrl() { + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/1/counter/100/limit/100"]'); + } + + + /** @test */ + public function hundredRecordsShouldHaveBeenDeletedForLibraryThree() { + $this->assertCount(1, Class_NoticeSuccincte::findAllBy(['id_bib' => 3])); + } +} + + + + +class Cosmo_DeleteItemsControllerLibraryActionSecondStepSecondRangeTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + Class_NoticeSuccincte::deleteBy(['id <' => 100]); + $this->dispatch('/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/1/counter/100'); + } + + + /** @test */ + public function pageShouldContainNextStepUrl() { + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/2/counter/0"]'); + } + + + /** @test */ + public function allRecordsShouldHaveBeenDeletedForLibraryThree() { + $this->assertCount(0, Class_NoticeSuccincte::findAllBy(['id_bib' => 3])); + } +} + + + + +class Cosmo_DeleteItemsControllerLibraryActionThirdStepFirstRangeTest + extends Cosmo_DeleteItemsControllerLibraryTestCase { + + public function setUp() { + parent::setUp(); + Class_Exemplaire::deleteBy(['id_bib' => 3]); + $this->dispatch('/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/2/counter/0/limit/100'); + } + + + /** @test */ + public function pageShouldContainNextStepUrl() { + $this->assertXPath('//div[@data-delete-url="/cosmo/delete-items/library/id/3/count_items/103/count_succinct_records/101/step/2/counter/100/limit/100"]'); + } + + + /** @test */ + public function firstNoticeShouldHaveLostFacetB3() { + $this->assertEquals(' HCCC001 B1', Class_Notice::find(1)->getFacettes()); + } + + + /** @test */ + public function secondNoticeShouldHaveLostFacetB4AndB1() { + $this->assertEquals(' HCCC001', Class_Notice::find(2)->getFacettes()); + } + + + /** @test */ + public function thirdNoticeShouldHaveLostFacetB5() { + $this->assertEquals(' HCCC001', Class_Notice::find(3)->getFacettes()); + } + + + /** @test */ + public function fourthNoticeShouldHaveLostFacetB3() { + $this->assertEquals(' HCCC001', Class_Notice::find(4)->getFacettes()); + } +} diff --git a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IndexTest.php b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IndexTest.php index 535fad224dba1214dd5999d8e7d79e8c6b2a9699..d33d17f1ca0964e9eeb660a439d14120ec55f0b2 100644 --- a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IndexTest.php +++ b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IndexTest.php @@ -24,6 +24,7 @@ class Cosmo_IndexTest extends CosmoControllerTestCase { public function setUp() { parent::setUp(); + Class_IntBib::setTimeSource(new TimeSourceForTest('2021-10-16')); $this->fixture(Class_IntBib::class, ['id' => 23, @@ -79,6 +80,8 @@ class Cosmo_IndexTest extends CosmoControllerTestCase { Class_CosmoVar::setValueOf('integration_path', '/cosmo/integration/'); + Class_AdminVar::set('TEXT_REPLACEMENT',''); + $integre12 = $this ->mock() ->whenCalled('getFileSize') diff --git a/cosmogramme/php/_menu.php b/cosmogramme/php/_menu.php index 2337aa82d19eb9c938b356599942d53aa2a6f64a..777021a4abf90df2d0f63b04d653103382023c1d 100644 --- a/cosmogramme/php/_menu.php +++ b/cosmogramme/php/_menu.php @@ -117,7 +117,7 @@ else ?> <div class="menu_section">Utilitaires</div> <?php - ligneMenu("Suppression d'exemplaires","integre_supprimer_exemplaires.php"); + ligneMenu("Suppression d'exemplaires","../cosmozend/cosmo/delete-items/index"); ligneMenu("Réindexation des identifiants","util_indexation.php"); ligneMenu("Réindexation phonétique","util_fulltext.php?action=PARAM"); ?> diff --git a/cosmogramme/php/integre_supprimer_exemplaires.php b/cosmogramme/php/integre_supprimer_exemplaires.php deleted file mode 100644 index 11b39f71f24a4e3defcc33f4c862dfea4d3ea4be..0000000000000000000000000000000000000000 --- a/cosmogramme/php/integre_supprimer_exemplaires.php +++ /dev/null @@ -1,240 +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 - */ -/////////////////////////////////////////////////////////////////// -// -// SUPPRESSION DES EXEMPLIARES D'UNE BIBLIOTHEQUE -// -//////////////////////////////////////////////////////////////////// -include("_init_frame.php"); - -// Includes -require_once("classe_bib.php"); -$oBib=new bibliotheque(); -require_once("classe_chronometre.php"); -$chrono = new chronometre(); - -?> - -<h1>Suppression de notices ou d'exemplaires</h1> - -<?PHP - -//--------------------------------------------------------------------------------- -// SUPPRESSION -//--------------------------------------------------------------------------------- -if($_REQUEST["action"]=="SUPPRIMER") -{ - // Suppression de toutes les notices de la base - if($_REQUEST["id_bib"] == "ALL") - { - if($_SESSION["passe"]!= "admin_systeme") afficherErreur("Vous devez être connecté en tant qu'administrateur système pour exécuter cette fonction"); - viderTable("notices"); - viderTable("notices_articles"); - viderTable("notices_succintes"); - viderTable("exemplaires"); - viderTable("stats_notices"); - //viderTable("codif_auteur"); - //viderTable("codif_matiere"); - //viderTable("codif_interet"); - viderTable("codif_tags"); - viderTable("integrations"); - viderTable("int_analyse"); - viderTable("prets"); - viderTable("reservations"); - viderRepertoire("Cache","cache_path"); - viderRepertoire("Integration","integration_path"); - viderRepertoire("Logs","log_path"); - - print(BR.'<h3>Traitement terminé</h3>'.BR.BR.'</body></html>'); - exit; - } - - // Initialisations - $id_bib=$_REQUEST["id_bib"]; - $nb_notices=0; - $nb_total=0; - $avance=-1; - $pointeur_reprise=0; - $timeout=intval(ini_get("max_execution_time") * 0.75); - $timeStart=time(); - $chrono->start(); - - if($_REQUEST["reprise"]=="oui") restaureContext(); - else - { - $nb_total = $sql->fetchOne("select count(*) from notices"); - // Suppression des exemplaires et des notices succintes - $entete=BR.'<h4>Suppression des exemplaires de : '.$oBib->getNomCourt($id_bib).'</h4>'; - $nombre=$sql->execute("delete from exemplaires where id_bib=$id_bib"); - $nombre1=$sql->execute("delete from notices_succintes where id_bib=$id_bib"); - $entete.='<span class="vert" style="margin-left:10px">'.$nombre.' exemplaires supprimés</span>'.BR; - $entete.='<span class="vert" style="margin-left:10px">'.$nombre1.' notices succintes supprimées</span>'.BR; - } - - // Recalcul de la facette bibliotheque - print($entete); - print(BR.'<h4>Mise à jour des facettes bibliothèques</h4>'); - - // Jauge - print(BR.'<div class="jauge" style="border:none"><div id="pct" class="pct">0 %</div>'); - print('<div class="jauge"><div id="jauge" class="jauge_avance"></div></div>'); - print('</div>'); - print('<br><div>'); - print('<span id="notice"></span>'); - print('</div>'); - flush(); - - // Boucle de traitement - $resultat=$sql->prepareListe("select id_notice,facettes from notices where id_notice > $pointeur_reprise Order by id_notice "); - while($ligne=$sql->fetchNext($resultat)) - { - if($chrono->tempsPasse() > $timeout) sauveContexte(); - $id_notice=$ligne["id_notice"]; - $facette=explode(" ",$ligne["facettes"]); - $facettes=""; - for($i=0; $i < count($facette); $i++) - { - if(!$facette[$i]) continue; - if(substr($facette[$i],0,1) != "B")$facettes.=" ".$facette[$i]; - } - // Chercher les exemplaires - $bibs=$sql->fetchAll("select distinct(id_bib) from exemplaires where id_notice=$id_notice"); - if(count($bibs)) - { - foreach($bibs as $enreg) - { - $bib=" B".$enreg["id_bib"]; - $facettes.= $bib; - } - } - // Ecrire - $sql->execute("update notices set facettes='$facettes' where id_notice=$id_notice"); - $pointeur_reprise=$ligne["id_notice"]; - $nb_notices++; - afficherAvance($nb_notices,$nb_total); - } - - // Fin - afficherAvance($nb_total,$nb_total); - $chrono->timeStart=$timeStart; - print("<h4>Fin du traitement</h4>"); - print('<span class="vert" style="margin-left:10px">Temps de traitement : '.$chrono->end().'</span>'.BR.BR); - exit; -} - -//--------------------------------------------------------------------------------- -// LISTE DES BIBLIOTHEQUES -//--------------------------------------------------------------------------------- -print(BR.'<div style="margin-left:140px">'); -print(rendBouton("Supprimer toutes les notices de la base","integre_supprimer_exemplaires.php","action=SUPPRIMER&id_bib=ALL","Etes vous sûr de vouloir supprimer toutes les notices de la base ?")); -print('</div>'); - -print(BR.'<span class="violet"><b>Attention : cette fonction va supprimer tous les exemplaires de la bibliothèque que vous allez choisir</b></span>'.BR.BR); -print('<div class="liste" style="margin-left:100px">'); -print('<table>'); -$liste=$oBib->getAll(); -foreach( $liste as $id_bib => $bib) -{ - $suppr=rendBouton("Supprimer","integre_supprimer_exemplaires.php","action=SUPPRIMER&id_bib=".$bib["id_bib"]); - print('<tr><td style="padding:7px"><b>'. $bib["id_bib"].'</b></td>'); - print('<td style="padding:7px"><b>'. $bib["nom_court"].'</b></td>'); - print('<td>'.$suppr.'</td</tr>'); -} -print('</table></div>'.BR.BR); -print('</body></html>'); - -// ---------------------------------------------------------------- -// Gestion du contexte pour les timeout -// ---------------------------------------------------------------- -function sauveContexte() -{ - global $timeStart,$pointeur_reprise,$entete; - global $nb_notices,$nb_total; - - $data=compact("timeStart","pointeur_reprise","nb_notices","nb_total","entete"); - $_SESSION["reprise"]=$data; - redirection( "integre_supprimer_exemplaires.php?reprise=oui&action=SUPPRIMER"); -} - -function restaureContext() -{ - global $timeStart,$pointeur_reprise,$entete; - global $nb_notices,$nb_total; - - extract($_SESSION["reprise"]); - unset($_SESSION["reprise"]); -} - -function afficherAvance($pointeur,$nb_total) -{ - global $avance; - $pct=(int)(($pointeur / $nb_total) * 100); - if($pct != $avance) - { - $avance=$pct; - print('<script>'); - print("document.getElementById('pct').innerHTML='".$pct."%';"); - $jauge="document.getElementById('jauge').style.width='".$pct."%';"; - print($jauge); - print('</script>'); - } - if($pointeur % 100 == 0 or $pct==100) - { - print('<script>'); - print("document.getElementById('notice').innerHTML='".$pointeur." / ".$nb_total."';"); - print('</script>'); - } - flush(); -} - -function viderTable($table) -{ - global $sql; - $nb=$sql->fetchOne("select count(*) from ".$table); - print('<span class="violet" style="margin-left:10px"><b>» Table : '.$table.'</b></span>'); - $sql->execute("TRUNCATE TABLE ".$table); - if(!$nb) $msg="aucun enregistrement supprimé"; - elseif($nb == 1) $msg="1 enregistrement supprimé"; - else $msg=$nb." enregistrements supprimés"; - print('<span class="vert" style="margin-left:10px">- '.$msg.'</span>'.BR); -} - -function viderRepertoire($titre,$type) -{ - print('<span class="violet" style="margin-left:10px"><b>» Dossier : '.$titre.'</b></span>'); - $path=getVariable($type); - - @$dir = opendir($path) or AfficherErreur("Impossible d'ouvrir le dossier : " .$path); - while (($file = readdir($dir)) !== false) - { - $fichier=$path.$file; - if(is_dir($fichier)) continue; - $nb++; - unlink($fichier); - } - closedir( $dir); - // affichage du nombre - if(!$nb) $msg="aucun fichier supprimé"; - elseif($nb == 1) $msg="1 fichier supprimé"; - else $msg=$nb." fichiers supprimés"; - print('<span class="vert" style="margin-left:10px">- '.$msg.'</span>'.BR); -} -?> \ No newline at end of file diff --git a/library/Class/Cosmogramme/Cleaner.php b/library/Class/Cosmogramme/Cleaner.php new file mode 100644 index 0000000000000000000000000000000000000000..c0f02b24f2f86864bdfe4f2da3f981a2eeb03cc9 --- /dev/null +++ b/library/Class/Cosmogramme/Cleaner.php @@ -0,0 +1,170 @@ +<?php +/** + * Copyright (c) 2012-2022, 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_Cleaner { + + use Trait_StormFileSystem; + use Trait_Translator; + + protected array $_truncate_steps = []; + protected array $_clear_steps = []; + + const RECORDS = 0; + const SERIAL_ARTICLES = 1; + const SUCCINCTE = 2; + const ITEMS = 3; + const STATS = 4; + const TAGS = 5; + const INTEGRATION = 6; + const ANALYSE = 7; + const LOAN = 8; + const HOLD = 9; + + const CACHE_PATH = 10; + const INTEGRATION_PATH = 11; + const LOG_PATH = 12; + + protected string $_step; + protected int $_count_steps_done; + + + public function __construct(string $step, int $count_steps_done) { + $this->_truncate_steps = + [static::RECORDS => [Class_Notice::class, $this->_('suppression des notices')], + static::SERIAL_ARTICLES => [Class_Notice_SerialArticles::class, $this->_('suppression des notices d\'articles')], + static::SUCCINCTE => [Class_NoticeSuccincte::class, $this->_('suppression des notices succinctes')], + static::ITEMS => [Class_Exemplaire::class, $this->_('suppression des exemplaires')], + static::STATS => [Class_StatsNotices::class, $this->_('suppression des statistiques de notice')], + static::TAGS => [Class_CodifTags::class, $this->_('suppression des codifications de tag')], + static::INTEGRATION => [Class_Cosmogramme_Integration::class, $this->_('suppression des intégrations')], + static::ANALYSE => [Class_IntAnalyse::class, $this->_('suppression des analyses d\'intégrations')], + static::LOAN => [Class_Pret::class, $this->_('suppression des prêts')], + static::HOLD => [Class_Reservation::class, $this->_('suppression des réservations')]]; + + $this->_clear_steps = + [static::CACHE_PATH => ['cache_path', $this->_('Nettoyage du dossier cache')], + static::INTEGRATION_PATH => ['integration_path', $this->_('Nettoyage du dossier d\'intégration')], + static::LOG_PATH => ['log_path', $this->_('Nettoyage du dossier de log')]]; + + $this->_step = $step; + $this->_count_steps_done = $count_steps_done; + } + + + protected function _isTruncateStep() : bool { + return in_array($this->_step, array_keys($this->_truncate_steps)); + } + + + protected function _isClearStep() : bool { + return in_array($this->_step, array_keys($this->_clear_steps)); + } + + + public function getStepLabel() : string { + return $this->_isTruncateStep() + ? $this->_truncate_steps[$this->_step][1] + : $this->_clear_steps[$this->_step][1]; + } + + + public function delete() : self { + if ($this->_isTruncateStep()) + return $this->_truncateStep(); + + if ($this->_isClearStep()) + return $this->_clearDirectoryStep(); + + return $this; + } + + + protected function _truncateStep() : self { + $definition = $this->_truncate_steps[$this->_step]; + $class = reset($definition); + $class::truncate(); + $this->_count_steps_done++; + $this->_next = $this->_step + 1; + return $this; + } + + + protected function _clearDirectoryStep() : self { + $definition = $this->_clear_steps[$this->_step]; + $variable = reset($definition); + + $path = Class_CosmoVar::get($variable); + + $this->_count_steps_done++; + $this->_next = $this->_step + 1; + + $fs = static::getFileSystem(); + $dir = $fs->opendir($path); + + if (!$dir) + return $this; + + while (($file = $fs->readdir($dir)) !== false) + { + $fichier = $path . $file; + if($fs->is_dir($fichier)) continue; + $fs->unlink($fichier); + } + $fs->closedir($dir); + + return $this; + } + + + public function countStepsDone() : int { + return $this->_count_steps_done; + } + + + public function countTotalSteps() : int { + return count($this->_truncate_steps) + count($this->_clear_steps); + } + + + public function countStepsDonePercent() : int { + return ( 0 !== ($total_albums = $this->countTotalSteps())) + ? intval(($this->_count_steps_done / $total_albums) * 100) + : 0; + } + + + public function nextStep() : ?string { + return $this->_next && $this->_next < $this->countTotalSteps() + ? $this->_next + : null; + } + + + public function nextStepCounter() : int { + return $this->_count_steps_done ++; + } + + + public function countRemainingSteps() : int { + return $this->countTotalSteps() - $this->_count_steps_done; + } +} diff --git a/library/Class/Cosmogramme/ItemsCleaner.php b/library/Class/Cosmogramme/ItemsCleaner.php new file mode 100644 index 0000000000000000000000000000000000000000..cfaec5200d7414ed9ca9974f60d2669f915c64ca --- /dev/null +++ b/library/Class/Cosmogramme/ItemsCleaner.php @@ -0,0 +1,228 @@ +<?php +/** + * Copyright (c) 2012-2022, 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_ItemsCleaner { + + use Trait_Translator; + + const LIMIT = 1000; + + const ITEMS = 0; + const SUCCINCTE = 1; + const FACETS = 2; + + protected int + $_id_bib, + $_step, + $_count_steps_done, + $_count_items, + $_count_records, + $_count_entities_done, + $_limit; + + protected array + $_delete_steps = [], + $_clean_steps = []; + + public function __construct(int $id_bib, + int $step, + int $count_items, + int $count_records, + int $count_entities_done, + int $limit=self::LIMIT) { + + $this->_delete_steps = + [static::ITEMS => [Class_Exemplaire::class, $this->_('suppression des exemplaires')], + static::SUCCINCTE => [Class_NoticeSuccincte::class, $this->_('suppression des notices succinctes')]]; + + $this->_clean_steps = + [static::FACETS => ['facets', $this->_('nettoyage des facettes')]]; + + $this->_id_bib = $id_bib; + $this->_step = $step; + $this->_count_items = $count_items; + $this->_count_records = $count_records; + $this->_count_entities_done = $count_entities_done; + $this->_limit = $limit; + } + + + protected function _isDeleteStep() : bool { + return in_array($this->_step, array_keys($this->_delete_steps)); + } + + + protected function _isCleanStep() : bool { + return in_array($this->_step, array_keys($this->_clean_steps)); + } + + + public function getStepLabel() : string { + $label = ($this->_isDeleteStep() + ? $this->_delete_steps[$this->_step][1] + : $this->_clean_steps[$this->_step][1]); + + return $this->_('Étape %s/%s - %s', + $this->_step +1, + $this->countTotalSteps(), + $label); + } + + + public function delete() : self { + if ( $this->_isDeleteStep()) + return $this->_deleteStep(); + + if ( $this->_isCleanStep()) + return $this->_cleanStep(); + + return $this; + } + + + protected function _deleteStep() : self { + $definition = $this->_delete_steps[$this->_step]; + $class = reset($definition); + + $entities = $class::findAllBy(['id_bib' => $this->_id_bib, + 'limit' => $this->getLimit()]); + foreach($entities as $entity) + $this->_deleteEntity($entity); + + return $this; + } + + + protected function _cleanStep() : self { + $notices = Class_Notice::query() + ->select() + ->gt('id_notice', $this->_count_entities_done ) + ->order('id_notice') + ->limit($this->getLimit()) + ->fetchAll(); + + foreach($notices as $notice) + $this->_cleanFacets($notice); + + return $this; + } + + + protected function _cleanFacets($notice) : self { + $orig_facette = array_filter(explode(" ", $notice->getFacettes())); + $facettes = ''; + + foreach($orig_facette as $facet) + if(substr($facet,0,1) != Class_Bib::CODE_FACETTE) + $facettes .= ' ' . $facet; + + $bibs = Class_Exemplaire::query() + ->distinct(['id_bib']) + ->eq('id_notice', $notice->getIdNotice()) + ->fetchAll(); + + foreach($bibs as $bib) + $facettes.=" " . Class_Bib::CODE_FACETTE . $bib->getIdBib(); + + Class_Notice::find($notice->getIdNotice())->setFacettes($facettes)->save(); + $this->_count_entities_done++; + + return $this; + } + + + protected function _deleteEntity($entity) : self { + $entity->delete(); + $this->_count_entities_done++; + + return $this; + } + + + public function getLimit() : int { + return $this->_limit; + } + + + public function currentStep() : int { + return $this->_step; + } + + + public function nextStep() : ? int { + $next_step = $this->_step; + + if ($this->_count_entities_done >= $this->_totalToDo()) { + $next_step++; + $this->_count_entities_done = 0; + } + + return $next_step >= $this->countTotalSteps() ? null : $next_step; + } + + + public function countDonePercent() : int { + return (0 !== ($total = $this->_totalToDo())) + ? intval(($this->_count_entities_done / $this->_totalToDo()) * 100) + : 0; + } + + + public function countTotalSteps() : int { + return count($this->_delete_steps) + count($this->_clean_steps); + } + + + + public function nextCount() : int { + return $this->_count_entities_done; + } + + + public function totalItems() : int { + return $this->_count_items; + } + + + public function totalRecords() : int { + return $this->_count_records; + } + + + public function idBib() : int { + return $this->_id_bib; + } + + protected function _totalToDo() : int { + if ($this->_step == 0) + return $this->_count_items; + + if ($this->_step == 1) + return $this->_count_records; + + if ($this->_step == 2) + return Class_Notice::countBy([]); + + + return 0; + } +} diff --git a/library/Class/IntAnalyse.php b/library/Class/IntAnalyse.php new file mode 100644 index 0000000000000000000000000000000000000000..aceefc458f00c1b4aa0340a853818663c139fd2c --- /dev/null +++ b/library/Class/IntAnalyse.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright (c) 2012-2022, 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_IntAnalyse extends Storm_Model_Abstract { + + protected $_table_name = 'int_analyse'; +} diff --git a/library/Class/StatsNotices.php b/library/Class/StatsNotices.php index ac55c190745b85b8bd3af3eda5c6ce36f1667d47..dbd9431f654e7632d7308e80c7dc16a0ccbd519f 100644 --- a/library/Class/StatsNotices.php +++ b/library/Class/StatsNotices.php @@ -19,10 +19,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Class_StatsNotices { +class Class_StatsNotices extends Storm_Model_Abstract { use Trait_TimeSource; + protected + $_table_name = 'stats_notices'; + private $lib_mois = ['', 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', @@ -201,4 +204,4 @@ class Class_StatsNotices { return false; } } -?> \ No newline at end of file +?> diff --git a/library/Class/TableDescription/CosmoCleanLibraryItems.php b/library/Class/TableDescription/CosmoCleanLibraryItems.php new file mode 100644 index 0000000000000000000000000000000000000000..6f1031452d70e35a89a82ae556cd62d636b85ed8 --- /dev/null +++ b/library/Class/TableDescription/CosmoCleanLibraryItems.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright (c) 2012-2022, 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_CosmoCleanLibraryItems extends Class_TableDescription { + public function init() { + $this + ->addColumn($this->_('#id'), 'id') + ->addColumn($this->_('Nom'), 'nom_court') + ->addRowAction(['url' => ['action' => 'library-dashboard', + 'id' => '%s'], + 'icon' => 'delete', + 'label' => $this->_('Supprimer les exemplaires')]); + } +} diff --git a/library/ZendAfi/View/Helper/Admin/DeleteAjaxDashboard.php b/library/ZendAfi/View/Helper/Admin/DeleteAjaxDashboard.php new file mode 100644 index 0000000000000000000000000000000000000000..96e52b1274ccdc1c1708e8297f2c2b43f4cfce70 --- /dev/null +++ b/library/ZendAfi/View/Helper/Admin/DeleteAjaxDashboard.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Admin_DeleteAjaxDashboard + extends ZendAfi_View_Helper_BaseHelper { + + public function deleteAjaxDashboard(string $id, + string $delete_url, + string $after_delete_url, + string $counter_message) : string { + + Class_ScriptLoader::getInstance() + ->addScript(Class_Url::absolute('public/admin/js/ajax-delete/ajax-delete.js')) + ->addJqueryReady(sprintf('$("#%s").ajax_delete();', $id)); + + $html = [$this->_tag('h1', + $this->view->titre)]; + + $html[] = $counter_message; + $html[] = $this->_tagWarning($this->_('Pendant la suppression, la navigation sur le site peut être dégradée.')); + + $html[]= $this->view->button((new Class_Button) + ->setImage($this->view->renderIcon('class fa fa-exclamation-triangle')) + ->setText($this->_('Lancer la suppression')) + ->setAttribs(['onclick' => '', + 'data-confirm-message' => $this->_('Êtes vous sûr ?'), + 'id' => 'run_delete'])); + $html[] = $this->view->Button_Back(); + $html[] = $this->view->modalLoading($id); + + return $this->_div(['id' => $id, + 'data-delete-url' => $delete_url, + 'data-after-delete-url' => $after_delete_url], + implode($html)); + } +} diff --git a/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategory.php b/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategory.php index 490881bd81e4bcf24c571e8ffde5d9428e83c805..3c1d437e246a55520bf0f38a4106cf5100b5d887 100644 --- a/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategory.php +++ b/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategory.php @@ -40,7 +40,7 @@ class ZendAfi_View_Helper_Admin_DeleteAlbumCategory extends ZendAfi_View_Helper_ . BR . $this->_cancelButton(); - return $this->_div(['class' => 'delete_album_category', + return $this->_div(['class' => 'delete_ajax delete_album_category', 'data-delete-url' => $this->_getNewDeleteUrl(), 'data-count-remaining-albums' => $this->_cleaner->countRemainingAlbums(), 'data-count-remaining-sub-categories' => $this->_cleaner->countRemainingSubCategories(), diff --git a/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategoryDashboard.php b/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategoryDashboard.php index 2b73e7fdf7c1ed9b788d0067815aa28d54c13d8f..b3d0c976460707960006d8ca07f3d88bd4dfadea 100644 --- a/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategoryDashboard.php +++ b/library/ZendAfi/View/Helper/Admin/DeleteAlbumCategoryDashboard.php @@ -20,58 +20,50 @@ */ -class ZendAfi_View_Helper_Admin_DeleteAlbumCategoryDashboard extends ZendAfi_View_Helper_BaseHelper { +class ZendAfi_View_Helper_Admin_DeleteAlbumCategoryDashboard + extends ZendAfi_View_Helper_BaseHelper { + + protected + $_counter, + $_category; + public function deleteAlbumCategoryDashboard(Class_AlbumCategorie $category) : string { + $this->_category = $category; + $this->_counter = new Class_AlbumCategorie_DataCounters($this->_category); + $id = 'delete_album_category_dashboard_' . $category->getId(); + return $this->view->deleteAjaxDashboard($id, + $this->_deleteUrl(), + '/admin/album', + $this->_addCounterMessage()); + } + + + protected function _deleteUrl() : string { + return $this->view->url(['module' => 'admin', + 'controller' => 'album', + 'action' => 'run-delete-category', + 'id' => $this->_category->getId(), + 'count_albums' => $this->_counter->countTotalAlbums(), + 'count_sub_categories' => $this->_counter->countTotalSubCategories(), + 'count_records' => $this->_counter->countTotalRecords()], + null, true); + } + + + protected function _addCounterMessage() : string { + return $this->_tagNotice($this->_('Êtes-vous sûr de vouloir supprimer la catégorie %s%s%s%s', + $this->_category->getLibelle(), + $this->_countAlbumsToText($this->_counter), + $this->_countRecordsToText($this->_counter), + $this->_countSubCategoriesToText($this->_counter))); - Class_ScriptLoader::getInstance() - ->addScript(Class_Url::absolute('public/admin/js/delete-album-category/delete-album-category.js')) - ->addJqueryReady(sprintf('$("#%s").delete_album_category();', $id)); - - $confirm_message = $this->_('Êtes vous sûr ?'); - - $counter = new Class_AlbumCategorie_DataCounters($category); - - $delete_url = $this->view->url(['module' => 'admin', - 'controller' => 'album', - 'action' => 'run-delete-category', - 'id' => $category->getId(), - 'count_albums' => $counter->countTotalAlbums(), - 'count_sub_categories' => $counter->countTotalSubCategories(), - 'count_records' => $counter->countTotalRecords()], - null, true); - - $redirect_url = $this->view->url(['action' => 'index', - 'id' => null, - 'cat_id' => null, - 'render' => null]); - - $html = - $this->_tagNotice($this->_('Êtes-vous sûr de vouloir supprimer la catégorie %s%s%s%s', - $category->getLibelle(), - $this->_countAlbumsToText($counter), - $this->_countRecordsToText($counter), - $this->_countSubCategoriesToText($counter))) - . $this->_tagWarning($this->_('Pendant la suppression, la navigation sur le site peut être dégradée.')) - . $this->view->button((new Class_Button) - ->setImage($this->view->renderIcon('class fa fa-exclamation-triangle')) - ->setText($this->_('Lancer la suppression')) - ->setAttribs(['onclick' => '', - 'data-confirm-message' => $confirm_message, - 'id' => 'run_delete'])) - . $this->view->Button_Back() - . $this->view->modalLoading($id); - - return $this->_div(['id' => $id, - 'data-delete-url' => $delete_url, - 'data-after-delete-url' => '/admin/album'], - $html); } protected function _countAlbumsToText(Class_AlbumCategorie_DataCounters $counter) : string { - $count = $counter->countTotalAlbums(); + $count = $this->_counter->countTotalAlbums(); return $this->_plural($count, '', ', son album', diff --git a/library/ZendAfi/View/Helper/Cosmo/Cleaner.php b/library/ZendAfi/View/Helper/Cosmo/Cleaner.php new file mode 100644 index 0000000000000000000000000000000000000000..318c3301e1194f500c3e7cb2e5fea6afea6b67f5 --- /dev/null +++ b/library/ZendAfi/View/Helper/Cosmo/Cleaner.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Cosmo_Cleaner extends ZendAfi_View_Helper_BaseHelper { + + protected Class_Cosmogramme_Cleaner $_cleaner; + + + public function Cosmo_Cleaner(Class_Cosmogramme_Cleaner $cleaner) : string { + $this->_cleaner = $cleaner; + + if ( null === $this->_cleaner->nextStep()) + return ''; + + $html = + $this->_tag('h3', $this->_('Suppression en cours')) + . $this->_tagWarning($this->_('Pour continuer la suppression, veuillez garder cette page ouverte')) + . $this->_tagNotice($this->_('Vous serez redirigé une fois la suppression terminée')) + . $this->_countSteps() + . $this->_progressBar() + . BR + . $this->_cancelButton(); + + return $this->_div(['class' => 'delete_ajax', + 'data-delete-url' => $this->_getNewDeleteUrl(), + 'data-count-remaining-steps' => $this->_cleaner->countRemainingSteps()], + $html); + } + + + protected function _countSteps() : string { + return $this->_addTextToCounters($this->_cleaner->countStepsDone(), + $this->_cleaner->countTotalSteps()); + } + + + protected function _addTextToCounters(int $count_deleted, + int $count_total) { + if ( 0 === $count_total) + return ''; + + $step_label = $this->_cleaner->getStepLabel(); + $status_message = $this->_("Étape %s/%s (%s)", $count_deleted, $count_total, $step_label); + + return $this->_tagWarning($status_message); + } + + + protected function _progressBar() : string { + $id = 'progress_bar_ajax_delete'; + + $script = sprintf('$("#%s").progressbar({value: %d});', + $id, + $this->_cleaner->countStepsDonePercent()); + + Class_ScriptLoader::getInstance()->addJQueryReady($script); + + return $this->_div(['id' => $id], ''); + } + + + protected function _getNewDeleteUrl() : string { + $new_counters_params = + ['module' => 'cosmo', + 'controller' => 'delete-items', + 'action' => 'all', + 'step' => $this->_cleaner->nextStep(), + 'counter' => $this->_cleaner->nextStepCounter()]; + + return $this->view->url($new_counters_params, null, true); + } + + + protected function _cancelButton() : string { + return + $this->view->button((new Class_Button) + ->setImage($this->view->renderIcon('class fa fa-fire-extinguisher')) + ->setText($this->_('Stopper la suppression')) + ->setAttribs(['onclick' => '', + 'id' => 'stop_delete'])); + } +} diff --git a/library/ZendAfi/View/Helper/Cosmo/DeleteItems.php b/library/ZendAfi/View/Helper/Cosmo/DeleteItems.php new file mode 100644 index 0000000000000000000000000000000000000000..1e931c5744566fbd29757bfa6c7a0b6462c36538 --- /dev/null +++ b/library/ZendAfi/View/Helper/Cosmo/DeleteItems.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Cosmo_DeleteItems extends ZendAfi_View_Helper_BaseHelper { + public function Cosmo_DeleteItems() : string { + $html = [$this->_tag('h1', + $this->view->titre)]; + + $html[] = $this->view->button((new Class_Button) + ->setText($this->_('Supprimer toutes les notices de la base')) + ->setUrl($this->view->url(['module' => 'cosmo', + 'controller' => 'delete-items', + 'action' => 'all-dashboard'], null, true))); + + $html[] = $this->_tagNotice('Les boutons suivants vous permettent de supprimer les exemplaires d\'une bibliothèque.'); + + $html [] = $this->view->renderTable(new Class_TableDescription_CosmoCleanLibraryItems('clean_libraries'), $this->view->libraries); + + return implode($html); + } +} diff --git a/library/ZendAfi/View/Helper/Cosmo/DeleteItemsAllDashboard.php b/library/ZendAfi/View/Helper/Cosmo/DeleteItemsAllDashboard.php new file mode 100644 index 0000000000000000000000000000000000000000..e9ded6a3a6fa9045f6674531c0654958a224cdf0 --- /dev/null +++ b/library/ZendAfi/View/Helper/Cosmo/DeleteItemsAllDashboard.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Cosmo_DeleteItemsAllDashboard + extends ZendAfi_View_Helper_BaseHelper { + + use Trait_StormFileSystem; + + + public function Cosmo_DeleteItemsAllDashboard() : string { + return $this->view->deleteAjaxDashboard('delete_all', + $this->_deleteUrl(), + $this->_afterDeleteUrl(), + $this->_addCounterMessage()); + } + + + protected function _deleteUrl() : string { + return $this->view->url(['action' => 'all', + 'step' => 0, + 'counter' => '0']); + } + + + protected function _afterDeleteUrl() : string { + return $this->view->url(['action' => 'all-dashboard', + 'render' => null]); + } + + + protected function _addCounterMessage() : string { + return + $this->_tagNotice($this->_('Êtes-vous sûr de vouloir supprimer les données suivantes : %s', $this->_modelsCounterList())) + . $this->_tagNotice($this->_('et de vider les répertoires suivants : %s', $this->_filesCounterList())); + } + + + protected function _filesCounterList() : string { + $list = + [$this->_countIn('cache_path'), + $this->_countIn('integration_path'), + $this->_countIn('log_path')]; + + return + $this->_tag('ul', + implode(array_map(fn($text) => $this->_tag('li', $text), $list))); + } + + + protected function _modelsCounterList() : string { + $list = + [$this->_('%d notices', Class_Notice::count()), + $this->_('%d notices d\'articles', Class_Notice_SerialArticles::count()), + $this->_('%d notices succinctes', Class_NoticeSuccincte::count()), + $this->_('%d exemplaires', Class_Exemplaire::count()), + $this->_('%d statistiques des notices', Class_StatsNotices::count()), + $this->_('%d codifications de tags', Class_CodifTags::count()), + $this->_('%d integrations', Class_Cosmogramme_Integration::count()), + $this->_('%d analyses d\'intégrations', Class_IntAnalyse::count()), + $this->_('%d prêts', Class_Pret::count()), + $this->_('%d réservations', Class_Reservation::count())]; + + return + $this->_tag('ul', + implode(array_map(fn($text) => $this->_tag('li', $text), $list))); + } + + + protected function _countIn(string $key) : string { + $path = (string) Class_CosmoVar::get($key); + return $this->_('%s (%d fichiers)', + $path, + $this->_countFilesIn($path)); + } + + + protected function _countFilesIn(string $path) : int { + return count($this->getFileSystem()->fileNamesAt($path)); + } +} diff --git a/library/ZendAfi/View/Helper/Cosmo/DeleteItemsLibraryDashboard.php b/library/ZendAfi/View/Helper/Cosmo/DeleteItemsLibraryDashboard.php new file mode 100644 index 0000000000000000000000000000000000000000..3c7e9a7b45b99012aa3c4fc3fe4928d1ca58de0c --- /dev/null +++ b/library/ZendAfi/View/Helper/Cosmo/DeleteItemsLibraryDashboard.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Cosmo_DeleteItemsLibraryDashboard + extends ZendAfi_View_Helper_BaseHelper { + + protected + $_count_items, + $_count_succinct_records, + $_id_bib; + + public function Cosmo_DeleteItemsLibraryDashboard() : string { + $this->_id_bib = $this->view->id_bib; + $this->_count_items = count(Class_Exemplaire::findAllBy(['id_bib' => $this->_id_bib])); + $this->_count_succinct_records = count(Class_NoticeSuccincte::findAllBy(['id_bib' => $this->_id_bib])); + + return $this->view->deleteAjaxDashboard('delete_library', + $this->_deleteUrl(), + $this->_afterDeleteUrl(), + $this->_addCountersMessage()); + } + + + protected function _deleteUrl() : string { + $count_items = count(Class_Exemplaire::findAllBy(['id_bib' => $this->_id_bib])); + $count_succinct_records = count(Class_NoticeSuccincte::findAllBy(['id_bib' => $this->_id_bib])); + return $this->view->url(['action' => 'library', + 'id' => $this->view->id_bib, + 'count_items' => $this->_count_items, + 'count_succinct_records' => $this->_count_succinct_records, + 'step' => 0, + 'counter' => 0]); + } + + + protected function _afterDeleteUrl() : string { + return $this->view->url(['action' => 'library-dashboard', + 'id' => $this->view->id_bib, + 'render' => null]); + } + + + protected function _addCountersMessage() : string { + return + $this->_tagNotice($this->_('Êtes-vous sûr de vouloir supprimer les données suivantes : %s', + $this->_modelsCounterList($this->_count_items, + $this->_count_succinct_records))); + } + + + protected function _modelsCounterList($count_items, $count_succinct_records) : string { + $list = + [$this->_('%d exemplaires', $count_items), + $this->_('%d notices succinctes', $count_succinct_records)]; + + return + $this->_tag('ul', + implode(array_map(fn($text) => $this->_tag('li', $text), $list))); + } +} diff --git a/library/ZendAfi/View/Helper/Cosmo/ItemsCleaner.php b/library/ZendAfi/View/Helper/Cosmo/ItemsCleaner.php new file mode 100644 index 0000000000000000000000000000000000000000..75201a391e85d9ff4ec2d7032eff885b486c0867 --- /dev/null +++ b/library/ZendAfi/View/Helper/Cosmo/ItemsCleaner.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Cosmo_ItemsCleaner extends ZendAfi_View_Helper_BaseHelper { + + protected Class_Cosmogramme_ItemsCleaner $_cleaner; + + + public function Cosmo_ItemsCleaner(Class_Cosmogramme_ItemsCleaner $cleaner) : string { + $this->_cleaner = $cleaner; + + if ( null === $nextstep = $this->_cleaner->nextStep()) + return ''; + + $html = + $this->_tag('h3', $this->_('Suppression en cours')) + . $this->_tagWarning('Pour continuer la suppression, veuillez garder cette page ouverte') + . $this->_tagNotice('Vous serez redirigé une fois la suppression terminée') + . $this->_countSteps() + . $this->_progressBar() + . BR + . $this->_cancelButton(); + + return $this->_div(['class' => 'delete_ajax', + 'data-delete-url' => $this->_getNewDeleteUrl($nextstep)], + $html); + } + + + protected function _countSteps() : string { + $status_message = $this->_($this->_cleaner->getStepLabel()) + . ': ' . $this->_cleaner->countDonePercent() . '%'; + + return $this->_tagWarning($status_message) ; + } + + + protected function _progressBar() : string { + $id = 'progress_bar_ajax_delete'; + + $script = sprintf('$("#%s").progressbar({value: %d});', + $id, + $this->_cleaner->countDonePercent()); + + Class_ScriptLoader::getInstance()->addJQueryReady($script); + + return $this->_div(['id' => $id], ''); + } + + + protected function _getNewDeleteUrl($nextstep) : string { + $new_counters_params = + ['module' => 'cosmo', + 'controller' => 'delete-items', + 'action' => 'library', + 'id' => $this->_cleaner->idBib(), + 'count_items' => $this->_cleaner->totalItems(), + 'count_succinct_records' => $this->_cleaner->totalRecords(), + 'step' => $nextstep, + 'counter' => $this->_cleaner->nextCount()]; + + return $this->view->url($new_counters_params); + } + + + protected function _cancelButton() : string { + return + $this->view->button((new Class_Button) + ->setImage($this->view->renderIcon('class fa fa-fire-extinguisher')) + ->setText($this->_('Stopper la suppression')) + ->setAttribs(['onclick' => '', + 'id' => 'stop_delete'])); + } + +} diff --git a/library/ZendAfi/View/Helper/RenderModelAction.php b/library/ZendAfi/View/Helper/RenderModelAction.php index 0bf578604c5d8afe099b2978051085a45ba39995..39c8744c934ee5bf15b66b6b3e8f37514c52ecdd 100644 --- a/library/ZendAfi/View/Helper/RenderModelAction.php +++ b/library/ZendAfi/View/Helper/RenderModelAction.php @@ -62,7 +62,7 @@ class ZendAfi_View_Helper_RenderModelAction extends ZendAfi_View_Helper_BaseHelp $url = $this->_initUrl($model); $icon = $this->_initIcon($model); - $anchorOptions = ($model_id = $this->_getModelId($model)) + $anchorOptions = ('' !== ($model_id = $this->_getModelId($model))) ? $this->_initAnchorOptions($model_id, $url) : []; diff --git a/library/storm b/library/storm index 4859dae8444425a97da09b7989a566afbeba4e1a..9c8586c2c3523037428a115cb7060f4e567bc0ab 160000 --- a/library/storm +++ b/library/storm @@ -1 +1 @@ -Subproject commit 4859dae8444425a97da09b7989a566afbeba4e1a +Subproject commit 9c8586c2c3523037428a115cb7060f4e567bc0ab diff --git a/public/admin/js/delete-album-category/delete-album-category.js b/public/admin/js/ajax-delete/ajax-delete.js similarity index 85% rename from public/admin/js/delete-album-category/delete-album-category.js rename to public/admin/js/ajax-delete/ajax-delete.js index aedaa3ac2eca6425d061eb7efa700f155f4ba216..da236f752c398b17f4ee4d3f8f83513515e70ffe 100644 --- a/public/admin/js/delete-album-category/delete-album-category.js +++ b/public/admin/js/ajax-delete/ajax-delete.js @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ (function ( $ ) { - $.fn.delete_album_category = function () { + $.fn.ajax_delete = function () { var html = $(this); var after_delete_url = html.attr('data-after-delete-url'); var url = html.attr('data-delete-url'); @@ -31,11 +31,11 @@ reloadAfterCloseModalLoading(); openModalLoading(); disableModalLoadingMinimize(); - deleteCategory(url, after_delete_url); + callDeleteAction(url, after_delete_url); }); - function deleteCategory(url, after_delete_url) { + function callDeleteAction(url, after_delete_url) { if ( ! html.find('#modal-loading-ajax-content').length) return closeModalLoadingAndRedirect(after_delete_url); @@ -46,15 +46,16 @@ url: url + '/render/ajax', type: 'GET', dataType: 'html', - success : function(response) {return public_functions.onSuccess(response);} + success : function(response) {return public_functions.onSuccess(response);}, + error : function(response) {return closeModalLoadingAndRedirect(after_delete_url);} })); } var public_functions = { 'onSuccess': function(response) { html.find('#modal-loading-ajax-content').html(response); - deleteCategory(html.find('.delete_album_category').attr('data-delete-url'), - after_delete_url); + callDeleteAction(html.find('[data-delete-url]').attr('data-delete-url'), + after_delete_url); html.find('#stop_delete').off().click(function(event) { event.preventDefault(); return closeModalLoadingAndRedirect(); diff --git a/public/admin/js/delete-album-category/tests/delete-album-category-tests.html b/public/admin/js/ajax-delete/tests/ajax-delete-tests.html similarity index 96% rename from public/admin/js/delete-album-category/tests/delete-album-category-tests.html rename to public/admin/js/ajax-delete/tests/ajax-delete-tests.html index e5e9d5ad9afd96178a9aaf09f5021736f0c0562c..ff2c36f76303a8f9e205896e3e6a2ecebda5ab7b 100644 --- a/public/admin/js/delete-album-category/tests/delete-album-category-tests.html +++ b/public/admin/js/ajax-delete/tests/ajax-delete-tests.html @@ -29,8 +29,8 @@ <script src="../../jquery-3.6.0.min.js"></script> <script src="../../../../qunit-1.13.0.js"></script> <script src="../../modal-loading/modal-loading.js"></script> - <script src="../delete-album-category.js"></script> - <script src="delete-album-category-tests.js"></script> + <script src="../ajax-delete.js"></script> + <script src="ajax-delete-tests.js"></script> </head> <body> <div id="qunit"></div> diff --git a/public/admin/js/delete-album-category/tests/delete-album-category-tests.js b/public/admin/js/ajax-delete/tests/ajax-delete-tests.js similarity index 96% rename from public/admin/js/delete-album-category/tests/delete-album-category-tests.js rename to public/admin/js/ajax-delete/tests/ajax-delete-tests.js index 909bd9204907b210cbc55868a51b07160bcb134b..8e41465e3de5837e83763ee4eb4f1c6ba0372255 100644 --- a/public/admin/js/delete-album-category/tests/delete-album-category-tests.js +++ b/public/admin/js/ajax-delete/tests/ajax-delete-tests.js @@ -30,7 +30,7 @@ testStart( function() { test('click on run delete should add ajax call to queue', function() { - $("#delete_album_category_dashboard_14").delete_album_category(); + $("#delete_album_category_dashboard_14").ajax_delete(); closeModalLoadingAndRedirect = function () {}; $('#run_delete').click(); deepEqual(ajax_call_queue.length, 1); @@ -39,7 +39,7 @@ test('click on run delete should add ajax call to queue', function() { test('delete album category on success function should inject response in modal-loading ajax-content', function() { $("#delete_album_category_dashboard_14") - .delete_album_category() + .ajax_delete() .onSuccess('<div class="delete_album_category" data-delete-url="/admin/album/run-delete-category/id/1/count_albums/0/count_sub_categories/0/count_records/0/render/ajax/count_deleted_albums/0/count_deleted_records/0/count_deleted_sub_categories/0" data-count-remaining-albums="0" data-count-remaining-sub-categories="0" data-count-remaining-records="0">' + '<h3>Suppression en cours</h3>' + '<p class="warning" title="Attention: "Pour continuer la suppression, veuillez garder cette page ouverte". Ce message vous indique une incomplétude.">Pour continuer la suppression, veuillez garder cette page ouverte</p>' diff --git a/public/admin/js/modal-loading/modal-loading.css b/public/admin/js/modal-loading/modal-loading.css index 62ff74e63c4b80e0e689cd138e2e6f11add2cef2..8e0e42e5b5802ce5d50c0b93b6b674e71b7650dc 100644 --- a/public/admin/js/modal-loading/modal-loading.css +++ b/public/admin/js/modal-loading/modal-loading.css @@ -17,8 +17,12 @@ transition: height 0.4s, width 0.4s; } +.modal-loading #modal-loading-ajax-content > * { + text-align: center !important; +} + .modal-loading #modal-loading-ajax-content, -.modal-loading #modal-loading-ajax-content .delete_album_category, +.modal-loading #modal-loading-ajax-content .delete_ajax, .modal-loading img { position: absolute; top: 0; @@ -57,23 +61,24 @@ } -.modal-loading #modal-loading-ajax-content .delete_album_category { +.modal-loading #modal-loading-ajax-content .delete_ajax { width: 300px; - height: 340px; + height: 400px; overflow: hidden; border: 1px solid var(--border-highlight); border-radius: 3px; background-color: var(--button-highlight); } -.modal-loading.minimize #modal-loading-ajax-content .delete_album_category { +.modal-loading.minimize #modal-loading-ajax-content .delete_ajax { left: auto; bottom: auto; } - -.modal-loading #modal-loading-ajax-content .delete_album_category h3 { - margin-top : 0; +.modal-loading #modal-loading-ajax-content .delete_ajax h3 { + margin-top: 0; + padding-top: 1em; + padding-bottom: 1em; } .modal-loading .ui-progressbar { diff --git a/tests/application/modules/admin/controllers/AlbumControllerDeleteCategoryTest.php b/tests/application/modules/admin/controllers/AlbumControllerDeleteCategoryTest.php index 76284135a615ca00b744d3842a17409138d01ae8..d0e1153a5257529c4b0791ecc0b4889c162f7e2f 100644 --- a/tests/application/modules/admin/controllers/AlbumControllerDeleteCategoryTest.php +++ b/tests/application/modules/admin/controllers/AlbumControllerDeleteCategoryTest.php @@ -127,7 +127,7 @@ class AlbumControllerDeleteCategoryDispatchActionTest /** @test */ public function pageShouldContainsScriptPublicAdminDeleteNodeJs() { - $this->assertXPath('//script[contains(@src, "/public/admin/js/delete-album-category/delete-album-category.js")]'); + $this->assertXPath('//script[contains(@src, "/public/admin/js/ajax-delete/ajax-delete.js")]'); } @@ -145,7 +145,7 @@ class AlbumControllerDeleteCategoryDispatchActionTest /** @test */ public function shouldContainsJQueryReadyDeleteAlbumCategoryOnDashboard() { - $this->assertXPathContentContains('//script', '$(function(){$("#delete_album_category_dashboard_2").delete_album_category();});'); + $this->assertXPathContentContains('//script', '$(function(){$("#delete_album_category_dashboard_2").ajax_delete();});'); } @@ -180,7 +180,7 @@ class AlbumControllerDeleteCategoryAjaxDeleteCategoryDispatchTest /** @test */ public function shouldReturnDivClassDeleteAlbumCategoryWithNewCounters() { - $this->assertXPath('//div[@class="delete_album_category"][contains(@data-delete-url, "/count_deleted_albums/20/count_deleted_records/20/count_deleted_sub_categories/1")]'); + $this->assertXPath('//div[@class="delete_ajax delete_album_category"][contains(@data-delete-url, "/count_deleted_albums/20/count_deleted_records/20/count_deleted_sub_categories/1")]'); } @@ -227,7 +227,6 @@ class AlbumControllerDeleteCategoryAjaxDeleteCategoryRenderAjaxDispatchTest class AlbumControllerDeleteCategoryAjaxDeleteCategoryNextRunDispatchTest extends AlbumControllerDeleteCategoryTestCase { - public function setUp() { parent::setUp(); @@ -246,33 +245,33 @@ class AlbumControllerDeleteCategoryAjaxDeleteCategoryNextRunDispatchTest /** @test */ public function shouldReturnDivClassDeleteAlbumCategoryWithCumulatedCounters() { - $this->assertXPath('//div[@class="delete_album_category"][@data-delete-url= "/admin/album/run-delete-category/id/2/count_deleted_albums/40/count_deleted_records/40/count_deleted_sub_categories/2"][@data-count-remaining-albums="20"][@data-count-remaining-sub-categories="0"][@data-count-remaining-records="20"]'); + $this->assertXPath('//div[@class="delete_ajax delete_album_category"][@data-delete-url= "/admin/album/run-delete-category/id/2/count_deleted_albums/40/count_deleted_records/40/count_deleted_sub_categories/2"][@data-count-remaining-albums="20"][@data-count-remaining-sub-categories="0"][@data-count-remaining-records="20"]'); } /** @test */ public function titleH3ShouldContainsSuppressionEnCours() { - $this->assertXPathContentContains('//div[@class="delete_album_category"]/h3', 'Suppression en cours'); + $this->assertXPathContentContains('//div[@class="delete_ajax delete_album_category"]/h3', 'Suppression en cours'); } /** @test */ public function paragrapheWarningShouldContainsKeepPageOpenMessage() { - $this->assertXPathContentContains('//div[@class="delete_album_category"]/p[@class="warning"]', + $this->assertXPathContentContains('//div[@class="delete_ajax delete_album_category"]/p[@class="warning"]', 'Pour continuer la suppression, veuillez garder cette page ouverte'); } /** @test */ public function paragrapheNoticeShouldContainsRedirectWhenJobIsDoneMessage() { - $this->assertXPathContentContains('//div[@class="delete_album_category"]/p[@class="notice"]', + $this->assertXPathContentContains('//div[@class="delete_ajax delete_album_category"]/p[@class="notice"]', 'Vous serez redirigé sur la liste des collections une fois la suppression terminée'); } /** @test */ public function buttonStopProcessShouldBeDisplay() { - $this->assertXPathContentContains('//div[@class="delete_album_category"]/button[@id="stop_delete"]', + $this->assertXPathContentContains('//div[@class="delete_ajax delete_album_category"]/button[@id="stop_delete"]', 'Stopper la suppression'); } -} \ No newline at end of file +} diff --git a/tests/js/DeleteAlbumCategoryTest.php b/tests/js/DeleteAlbumCategoryTest.php index 92c199ee4324531f81f5feac8447909fa878690b..5b7223e6d12942ca686d94b148d572f6a29650ff 100644 --- a/tests/js/DeleteAlbumCategoryTest.php +++ b/tests/js/DeleteAlbumCategoryTest.php @@ -21,5 +21,5 @@ class DeleteAlbumCategoryTest extends BrowserTest { - protected $_test_path = 'public/admin/js/delete-album-category/tests/delete-album-category-tests.html'; + protected $_test_path = 'public/admin/js/ajax-delete/tests/ajax-delete-tests.html'; }